import {
  consumeOauthPendingState,
  countMailConnections,
  upsertOauthGoogleConnection,
} from "../../../../db/mail";
import { encrypt } from "../../../../utils/crypto";
import {
  exchangeCodeForTokens,
  extractEmailFromIdToken,
  GOOGLE_OAUTH_SCOPES,
} from "../../../../utils/google-oauth";
import { CURRENT_ART9_MAIL_VERSION } from "../../../../utils/consent-texts";
import { writeConsentGrant, setMailConnectionConsent } from "../../../../db/consent";
import { getProfile } from "../../../../db/profile";
import { getPlanLimits } from "../../../../utils/plan-features";

/**
 * POST /api/mail/oauth/google/callback
 *
 * Step 2 des Google OAuth PKCE-Flows.
 *
 * Wird von der nativen App aufgerufen nachdem sie den Deep-Link
 * rebreak://auth/mail-oauth-callback?code=…&state=… abgefangen hat.
 *
 * NO requireUser — state ist der Auth-Mechanismus (CSRF-safe weil nur der User
 * der den Flow gestartet hat die stateId kennt). userId kommt aus oauth_pending_states.
 *
 * Body:
 *   code: string    — Authorization Code von Google
 *   state: string   — stateId aus dem init-Step (CSRF-Validierung)
 *
 * Response:
 *   200: { connectionId, email, provider: 'gmail_oauth' }
 *   400: { error: 'invalid_body' }
 *   401: { error: 'invalid_state' }
 *   403: { error: 'plan_limit' }
 *   502: { error: 'token_exchange_failed' }
 *   502: { error: 'email_extraction_failed' }
 */
export default defineEventHandler(async (event) => {
  const config = useRuntimeConfig(event);
  const clientId = config.googleOauthClientId as string;

  if (!clientId) {
    throw createError({
      statusCode: 500,
      data: { error: "GOOGLE_OAUTH_CLIENT_ID not configured" },
    });
  }

  const body = await readBody(event).catch(() => null) as {
    code?: string;
    state?: string;
  } | null;

  if (!body?.code || !body?.state) {
    throw createError({
      statusCode: 400,
      data: { error: "invalid_body", detail: "code and state are required" },
    });
  }

  const { code, state: stateId } = body;

  // ── Validate + consume state ────────────────────────────────────────────────
  const pendingState = await consumeOauthPendingState(stateId);

  if (!pendingState) {
    throw createError({
      statusCode: 401,
      data: {
        error: "invalid_state",
        detail: "State not found, expired (>10 min), or already used",
      },
    });
  }

  const { userId, codeVerifier, email: hintEmail } = pendingState;

  // ── Plan-Limit check ────────────────────────────────────────────────────────
  const profile = await getProfile(userId);
  const limits = getPlanLimits(profile?.plan ?? "free");

  if (limits.mailAgents !== Infinity) {
    const count = await countMailConnections(userId);
    if (count >= limits.mailAgents) {
      throw createError({
        statusCode: 403,
        data: {
          error: "plan_limit",
          resource: "mail_accounts",
          current: count,
          limit: limits.mailAgents,
        },
      });
    }
  }

  // ── Token Exchange ──────────────────────────────────────────────────────────
  let tokenResponse;
  try {
    tokenResponse = await exchangeCodeForTokens({
      clientId,
      code,
      codeVerifier,
    });
  } catch (err: any) {
    throw createError({
      statusCode: 502,
      data: {
        error: "token_exchange_failed",
        detail: err.message ?? "google_error",
      },
    });
  }

  const { access_token, refresh_token, expires_in, scope, id_token } = tokenResponse;

  // ── Extract email from ID-token ─────────────────────────────────────────────
  let gmailEmail: string | null = hintEmail ?? null;
  if (id_token) {
    const extracted = extractEmailFromIdToken(id_token);
    if (extracted) gmailEmail = extracted;
  }

  if (!gmailEmail) {
    throw createError({
      statusCode: 502,
      data: {
        error: "email_extraction_failed",
        detail: "Could not extract email from Google ID-token. Ensure openid+email scopes are granted.",
      },
    });
  }

  // ── Encrypt tokens ──────────────────────────────────────────────────────────
  const encryptedAccessToken = encrypt(access_token);
  const encryptedRefreshToken = encrypt(refresh_token);
  const tokenExpiry = new Date(Date.now() + expires_in * 1000);

  // ── Consent setup ───────────────────────────────────────────────────────────
  const now = new Date();
  const ipAddress =
    getHeader(event, "x-forwarded-for")?.split(",")[0]?.trim() ??
    getHeader(event, "x-real-ip") ??
    null;
  const userAgent = getHeader(event, "user-agent") ?? null;

  // ── Upsert MailConnection ───────────────────────────────────────────────────
  const connection = await upsertOauthGoogleConnection({
    userId,
    email: gmailEmail,
    encryptedAccessToken,
    encryptedRefreshToken,
    tokenExpiry,
    scope: scope ?? GOOGLE_OAUTH_SCOPES,
  });

  // ── Consent stamp + audit log ───────────────────────────────────────────────
  await setMailConnectionConsent({
    connectionId: connection.id,
    userId,
    consentAt: now,
    consentVersion: CURRENT_ART9_MAIL_VERSION,
    consentIpAddress: ipAddress,
  });

  await writeConsentGrant({
    userId,
    consentType: "art9-mail",
    consentVersion: CURRENT_ART9_MAIL_VERSION,
    consentAt: now,
    ipAddress,
    userAgent,
    mailConnectionId: connection.id,
  });

  return {
    connectionId: connection.id,
    email: gmailEmail,
    provider: "gmail_oauth",
    title: null,
  };
});
