import { BackendAuthProvider } from "@tinacms/datalayer";
import { FastifyRequest, FastifyReply } from "fastify";
import { getSession } from "./session";
import { Issuer, generators } from "openid-client";

// Stockage temporaire des states (en production, utiliser Redis)
const stateStore = new Map<string, { timestamp: number }>();

// Nettoyage périodique des states expirés (5 minutes)
setInterval(() => {
  const now = Date.now();
  for (const [state, data] of stateStore.entries()) {
    if (now - data.timestamp > 5 * 60 * 1000) {
      stateStore.delete(state);
    }
  }
}, 60 * 1000);

async function getOIDCClient() {
  const issuer = await Issuer.discover(process.env.OIDC_ISSUER!);
  return new issuer.Client({
    client_id: process.env.OIDC_CLIENT_ID!,
    client_secret: process.env.OIDC_CLIENT_SECRET!,
    redirect_uris: [process.env.OIDC_CALLBACK_URL!],
    response_types: ["code"],
  });
}

function generateState(): string {
  const state = generators.state();
  stateStore.set(state, { timestamp: Date.now() });
  return state;
}

function validateState(state: string): boolean {
  const data = stateStore.get(state);
  if (!data) return false;
  
  // Vérifier que le state n'est pas expiré (5 minutes)
  if (Date.now() - data.timestamp > 5 * 60 * 1000) {
    stateStore.delete(state);
    return false;
  }
  
  stateStore.delete(state);
  return true;
}

export const createOIDCBackendAuth = (): BackendAuthProvider => {
  return {
    isAuthorized: async (req: FastifyRequest, res: FastifyReply) => {
      const session = await getSession(req, res);
      
      if (!session.user) {
        return {
          isAuthorized: false,
          errorMessage: "Unauthorized",
          errorCode: 401,
        };
      }
      
      return { isAuthorized: true };
    },
    
    extraRoutes: {
      "/auth/login": {
        secure: false,
        handler: async (req, res) => {
          const client = await getOIDCClient();
          const state = generateState();
          
          const authorizationUrl = client.authorizationUrl({
            scope: process.env.OIDC_SCOPE || "openid profile email",
            state,
          });
          
          res.redirect(authorizationUrl);
        },
      },
      
      "/auth/callback": {
        secure: false,
        handler: async (req, res) => {
          try {
            const client = await getOIDCClient();
            const params = client.callbackParams(req.raw);
            
            // Vérifier le state pour prévenir les attaques CSRF
            if (!params.state || !validateState(params.state)) {
              return res.status(400).send({ error: "Invalid state parameter" });
            }
            
            // Échanger le code contre des tokens
            const tokenSet = await client.callback(
              process.env.OIDC_CALLBACK_URL!,
              params,
              { state: params.state }
            );
            
            // Valider et extraire les claims
            const claims = tokenSet.claims();
            
            // Créer la session
            const session = await getSession(req, res);
            session.user = {
              id: claims.sub,
              email: claims.email || "",
              name: claims.name || claims.preferred_username || "",
            };
            session.accessToken = tokenSet.access_token;
            session.idToken = tokenSet.id_token;
            
            await session.save();
            
            // Rediriger vers l'admin
            res.redirect("/admin");
          } catch (error) {
            console.error("OIDC callback error:", error);
            res.status(500).send({ error: "Authentication failed" });
          }
        },
      },
      
      "/auth/logout": {
        secure: false,
        handler: async (req, res) => {
          const session = await getSession(req, res);
          session.destroy();
          res.redirect("/admin");
        },
      },
      
      "/api/me": {
        secure: false,
        handler: async (req, res) => {
          const session = await getSession(req, res);
          
          if (!session.user) {
            return res.status(401).send({ error: "Not authenticated" });
          }
          
          return session.user;
        },
      },
    },
  };
};
