import { Router } from "express";
import jwt from "jsonwebtoken";
import { prisma } from "../../lib/prisma";
import { env } from "../../config/env";
import { encrypt, decrypt } from "../../lib/crypto";
import {
  GOOGLE_SCOPES,
  makeOAuthClient,
  oauthClientForRefreshToken,
  isGoogleConfigured,
} from "../../lib/google";
import { getGoogleConfig } from "../../lib/config-store";
import { requireAuth, requireCapability } from "../../middleware/auth";

export const googleRouter = Router();

// État anti-CSRF signé : porte l'identité de l'utilisateur jusqu'au callback Google
// (le callback n'a pas besoin de dépendre du cookie de session).
function signState(userId: string): string {
  return jwt.sign({ sub: userId, purpose: "google_oauth" }, env.jwt.accessSecret, {
    expiresIn: "10m",
  });
}
function verifyState(state: string): string {
  const payload = jwt.verify(state, env.jwt.accessSecret) as {
    sub: string;
    purpose?: string;
  };
  if (payload.purpose !== "google_oauth") throw new Error("state invalide");
  return payload.sub;
}

// État de la connexion Google de l'utilisateur courant.
googleRouter.get("/status", requireAuth, requireCapability("config:manage"), async (req, res) => {
  if (!(await isGoogleConfigured())) {
    return res.json({ configured: false, connected: false });
  }
  const account = await prisma.googleAccount.findFirst({
    where: { userId: req.user!.sub },
    select: { email: true, scopes: true, connectedAt: true, lastRefreshAt: true },
  });
  return res.json({ configured: true, connected: Boolean(account), account });
});

// Démarre le flux OAuth : redirige vers l'écran de consentement Google.
googleRouter.get("/oauth/start", requireAuth, requireCapability("config:manage"), async (req, res) => {
  if (!(await isGoogleConfigured())) {
    return res.status(503).json({ error: "Google OAuth non configuré (Client ID/Secret manquants)" });
  }
  const client = await makeOAuthClient();
  const url = client.generateAuthUrl({
    access_type: "offline", // pour obtenir un refresh token
    prompt: "consent", // force le refresh token à chaque liaison
    scope: GOOGLE_SCOPES,
    include_granted_scopes: true,
    state: signState(req.user!.sub),
  });
  return res.redirect(url);
});

// Callback Google : échange le code, stocke le refresh token chiffré, puis renvoie au frontend.
googleRouter.get("/oauth/callback", async (req, res) => {
  const { code, state, error } = req.query as Record<string, string>;
  const front = env.corsOrigin;

  if (error) return res.redirect(`${front}/settings?google=denied`);
  if (!code || !state) return res.redirect(`${front}/settings?google=error`);

  let userId: string;
  try {
    userId = verifyState(state);
  } catch {
    return res.redirect(`${front}/settings?google=error`);
  }

  try {
    const client = await makeOAuthClient();
    const { tokens } = await client.getToken(code);

    // Identité du compte Google lié (depuis l'id_token).
    if (!tokens.id_token) throw new Error("id_token manquant");
    const { clientId } = await getGoogleConfig();
    const ticket = await client.verifyIdToken({
      idToken: tokens.id_token,
      audience: clientId,
    });
    const payload = ticket.getPayload();
    if (!payload?.sub) throw new Error("profil Google illisible");
    const googleSub = payload.sub;
    const googleEmail = payload.email ?? "";

    // refresh_token : présent grâce à prompt=consent. Sinon, on conserve l'existant.
    const existing = await prisma.googleAccount.findUnique({
      where: { userId_googleSub: { userId, googleSub } },
    });
    if (!tokens.refresh_token && !existing) {
      return res.redirect(`${front}/settings?google=norefresh`);
    }
    const refreshTokenEnc = tokens.refresh_token
      ? encrypt(tokens.refresh_token)
      : existing!.refreshTokenEnc;

    await prisma.googleAccount.upsert({
      where: { userId_googleSub: { userId, googleSub } },
      create: {
        userId,
        googleSub,
        email: googleEmail,
        refreshTokenEnc,
        scopes: tokens.scope ? tokens.scope.split(" ") : GOOGLE_SCOPES,
        lastRefreshAt: new Date(),
      },
      update: {
        email: googleEmail,
        refreshTokenEnc,
        scopes: tokens.scope ? tokens.scope.split(" ") : GOOGLE_SCOPES,
        lastRefreshAt: new Date(),
      },
    });

    return res.redirect(`${front}/settings?google=connected`);
  } catch (e) {
    console.error("Google OAuth callback error:", e);
    return res.redirect(`${front}/settings?google=error`);
  }
});

// Délie le compte Google : révoque le token côté Google puis supprime la liaison.
googleRouter.post("/disconnect", requireAuth, requireCapability("config:manage"), async (req, res) => {
  const account = await prisma.googleAccount.findFirst({
    where: { userId: req.user!.sub },
  });
  if (!account) return res.json({ ok: true });

  try {
    const client = await oauthClientForRefreshToken(decrypt(account.refreshTokenEnc));
    await client.revokeCredentials();
  } catch (e) {
    // Révocation best-effort : on supprime la liaison locale même si la révocation échoue.
    console.warn("Révocation Google échouée (on supprime quand même la liaison):", e);
  }

  await prisma.googleAccount.delete({ where: { id: account.id } });
  return res.json({ ok: true });
});
