import type { Response } from "express";
import { OAuth2Client } from "google-auth-library";
import { prisma } from "./prisma";
import { decrypt } from "./crypto";
import { oauthClientForRefreshToken } from "./google";
import { getDataOwnerId } from "./data-owner";

// Erreur typée quand le compte Google n'est pas (ou plus) lié / autorisé.
export class GoogleNotConnectedError extends Error {
  constructor(message = "Compte Google non connecté") {
    super(message);
    this.name = "GoogleNotConnectedError";
  }
}

// Client Google authentifié au nom de l'utilisateur courant (rafraîchit l'access token seul).
// Réutilisable par les modules calendar, mail, etc.
export async function getUserGoogleClient(userId: string): Promise<OAuth2Client> {
  const account = await prisma.googleAccount.findFirst({ where: { userId } });
  if (!account) throw new GoogleNotConnectedError();
  return oauthClientForRefreshToken(decrypt(account.refreshTokenEnc));
}

// Client Google du **propriétaire** (Super Admin) : utilisé par agenda/mail/Drive pour que
// tous les comptes voient le même compte Google partagé.
export async function getOwnerGoogleClient(): Promise<OAuth2Client> {
  const ownerId = await getDataOwnerId();
  if (!ownerId) throw new GoogleNotConnectedError();
  return getUserGoogleClient(ownerId);
}

// Indique si une erreur googleapis correspond à un refresh token révoqué/expiré
// (ex. mode Test : expiration au bout de 7 jours) → l'utilisateur doit reconnecter Google.
export function isAuthError(err: unknown): boolean {
  const e = err as {
    code?: number;
    message?: string;
    response?: { status?: number; data?: { error?: string; error_description?: string } };
    errors?: { reason?: string }[];
  };
  const code = e?.response?.data?.error;
  if (code === "invalid_grant" || code === "invalid_token") return true;
  if (/invalid_grant/.test(e?.message ?? "")) return true;
  // Scope insuffisant (ex. Drive ajouté après coup → l'utilisateur doit re-consentir).
  const status = e?.response?.status ?? e?.code;
  const reason = e?.errors?.[0]?.reason;
  if (status === 403 && (reason === "insufficientPermissions" || /insufficient/i.test(e?.message ?? ""))) {
    return true;
  }
  if (/ACCESS_TOKEN_SCOPE_INSUFFICIENT/i.test(e?.message ?? "")) return true;
  return false;
}

// Réponse HTTP normalisée pour les erreurs d'accès aux APIs Google (réutilisée par calendar/mail).
export function sendGoogleError(res: Response, err: unknown, context = "Google") {
  if (err instanceof GoogleNotConnectedError) {
    return res.status(409).json({ error: "not_connected", message: "Compte Google non connecté." });
  }
  if (isAuthError(err)) {
    return res
      .status(409)
      .json({ error: "reconnect", message: "Connexion Google expirée, veuillez la reconnecter." });
  }
  console.error(`${context} API error:`, err);
  return res.status(502).json({ error: "google_error", message: `Erreur lors de l'accès à ${context}.` });
}
