import { refreshAndSaveTokens } from "../db/mail";
import { decrypt } from "./crypto";

/**
 * MailConnection-Shape: nur die Felder die für Auth-Resolution nötig sind.
 * Beide Scan-Endpoints bekommen das volle Prisma-Objekt — dieses Interface
 * dient als explizites Subset damit der Helper nicht vom vollen Typ abhängt.
 */
export interface MailConnectionAuthFields {
  id: string;
  email: string;
  authMethod: string;
  passwordEncrypted: string;
  oauthAccessToken?: string | null;
  oauthTokenExpiry?: Date | null;
}

export type ImapAuth =
  | { user: string; accessToken: string }
  | { user: string; pass: string };

/**
 * Gibt das korrekte `auth`-Objekt für ImapFlow zurück.
 *
 * - oauth2_microsoft: Access-Token decrypten, bei Ablauf via MS-Endpoint refreshen.
 *   Nutzt refreshAndSaveTokens() aus db/mail (Race-Condition-sicher, Prisma-basiert).
 * - Alle anderen authMethods (app_password, default): passwordEncrypted decrypten.
 *
 * Wirft wenn:
 *   - App-Password leer oder decrypt fehlschlägt
 *   - OAuth-Token fehlt und kein Refresh möglich
 *   - refreshAndSaveTokens() wirft (revoked refresh_token, MS-Endpoint-Fehler)
 *
 * @param connection  MailConnection-Felder (Subset)
 * @param clientId    MS Azure App Registration Client-ID (nur für OAuth-Pfad)
 */
export async function resolveImapAuth(
  connection: MailConnectionAuthFields,
  clientId: string,
): Promise<ImapAuth> {
  if (connection.authMethod === "oauth2_microsoft") {
    // Token-Expiry-Check: 5-Minuten-Puffer damit der Scan nicht
    // mitten in einem großen Mailbox-Durchlauf mit abgelaufenem Token stirbt.
    const fiveMinFromNow = Date.now() + 5 * 60 * 1000;
    const isExpiredOrMissing =
      !connection.oauthTokenExpiry ||
      connection.oauthTokenExpiry.getTime() < fiveMinFromNow;

    let accessToken: string;
    if (isExpiredOrMissing) {
      // Wirft wenn Refresh-Token fehlt oder MS-Endpoint antwortet mit Fehler.
      // Caller (scan.post / scan-internal.post) soll per try/catch continue-n.
      accessToken = await refreshAndSaveTokens(connection.id, clientId);
    } else {
      if (!connection.oauthAccessToken) {
        throw new Error(
          `oauth2_microsoft connection ${connection.id} has no oauthAccessToken stored`,
        );
      }
      accessToken = decrypt(connection.oauthAccessToken);
    }

    return { user: connection.email, accessToken };
  }

  // App-Password-Pfad (gmail, icloud, gmx, yahoo, custom)
  if (!connection.passwordEncrypted) {
    throw new Error(
      `Connection ${connection.id} has no passwordEncrypted (authMethod=${connection.authMethod})`,
    );
  }
  const pass = decrypt(connection.passwordEncrypted);
  return { user: connection.email, pass };
}
