import {
  consumeOauthPendingState,
  countMailConnections,
  upsertOauthMicrosoftConnection,
} from "../../../../db/mail";
import { encrypt } from "../../../../utils/crypto";
import {
  exchangeCodeForTokens,
  extractEmailFromIdToken,
  MS_OAUTH_SCOPES,
} from "../../../../utils/ms-oauth";

/**
 * POST /api/mail/oauth/microsoft/callback
 *
 * Step 2 of the Microsoft OAuth PKCE flow.
 *
 * Called by the native app after it intercepts the deep-link redirect from Microsoft
 * (rebreak://auth/mail-oauth-callback?code=…&state=…).
 *
 * NO requireUser — the state parameter is the auth mechanism here (CSRF-safe because
 * only the user who initiated the flow has the stateId in their session). The userId
 * is retrieved from oauth_pending_states.
 *
 * Consent-Gate strategy (Hans-Müller-aligned):
 *   We set consent_at = now() inline during this callback, because the user has
 *   already passed through the ConnectMailSheet Art. 9 consent step before reaching
 *   the Outlook-OAuth button. The consent_version is stamped as CURRENT_ART9_MAIL_VERSION.
 *   This is the same pattern as [id].post.ts (password-based connect).
 *
 * Body:
 *   code: string    — authorization code from Microsoft
 *   state: string   — stateId from the init step (CSRF validation)
 *
 * Response:
 *   200: { connectionId, email, provider: 'outlook_oauth' }
 *   400: { error: 'invalid_body' }
 *   401: { error: 'invalid_state' }   — state not found, expired, or already used
 *   500: { error: 'token_exchange_failed' }
 */
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";

export default defineEventHandler(async (event) => {
  const config = useRuntimeConfig(event);
  const clientId = config.msOauthClientId as string;

  if (!clientId) {
    throw createError({
      statusCode: 500,
      data: { error: "MS_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 ?? "microsoft_error",
      },
    });
  }

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

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

  if (!outlookEmail) {
    throw createError({
      statusCode: 502,
      data: {
        error: "email_extraction_failed",
        detail: "Could not extract email from Microsoft ID-token. Ensure openid+User.Read 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 upsertOauthMicrosoftConnection({
    userId,
    email: outlookEmail,
    encryptedAccessToken,
    encryptedRefreshToken,
    tokenExpiry,
    scope: scope ?? MS_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: outlookEmail,
    provider: "outlook_oauth",
    title: null,
  };
});
