import { createHash, randomBytes, timingSafeEqual } from "node:crypto";
import { spawn } from "node:child_process";
import { createServer } from "node:http";
import { loadConfig } from "../config.ts";
import { KickOAuthClient } from "../kick/KickOAuthClient.ts";
import { SessionVault } from "./SessionVault.ts";

const base64Url = (value: Buffer): string => value.toString("base64url");

const safeEqual = (left: string, right: string): boolean => {
  const a = Buffer.from(left);
  const b = Buffer.from(right);
  return a.length === b.length && timingSafeEqual(a, b);
};

const openBrowser = (url: string): void => {
  if (process.platform !== "win32") {
    process.stdout.write(`Ouvrez cette adresse dans votre navigateur :\n${url}\n`);
    return;
  }
  const child = spawn("explorer.exe", [url], {
    detached: true,
    stdio: "ignore",
    windowsHide: true
  });
  child.unref();
};

const main = async (): Promise<void> => {
  const config = loadConfig();
  const redirect = new URL(config.kickRedirectUri);
  if (
    redirect.protocol !== "http:"
    || !["localhost", "127.0.0.1"].includes(redirect.hostname)
    || !redirect.port
  ) {
    throw new Error("FILYX_KICK_REDIRECT_URI doit être une adresse HTTP locale avec un port.");
  }

  const state = base64Url(randomBytes(32));
  const verifier = base64Url(randomBytes(64));
  const challenge = base64Url(createHash("sha256").update(verifier).digest());
  const authorization = new URL("https://id.kick.com/oauth/authorize");
  authorization.search = new URLSearchParams({
    response_type: "code",
    client_id: config.kickClientId,
    redirect_uri: config.kickRedirectUri,
    scope: config.kickScopes.join(" "),
    code_challenge: challenge,
    code_challenge_method: "S256",
    state
  }).toString();

  const callback = new Promise<string>((resolve, reject) => {
    let timeout: NodeJS.Timeout | undefined;
    const server = createServer((request, response) => {
      const requestUrl = new URL(request.url ?? "/", config.kickRedirectUri);
      if (requestUrl.pathname !== redirect.pathname) {
        response.writeHead(404, { "Content-Type": "text/plain; charset=utf-8" });
        response.end("Page introuvable.");
        return;
      }
      const returnedState = requestUrl.searchParams.get("state") ?? "";
      const code = requestUrl.searchParams.get("code") ?? "";
      const oauthError = requestUrl.searchParams.get("error") ?? "";
      const validState = safeEqual(returnedState, state);
      const success = validState && Boolean(code) && !oauthError;
      response.writeHead(success ? 200 : 400, {
        "Content-Type": "text/html; charset=utf-8",
        "Cache-Control": "no-store",
        "Content-Security-Policy": "default-src 'none'; style-src 'unsafe-inline'"
      });
      response.end(
        `<!doctype html><html lang="fr"><meta charset="utf-8"><title>FilyX OAuth</title>`
        + `<body style="font:18px system-ui;padding:40px;background:#151515;color:#fff">`
        + `<h1>${success ? "FilyX est autorisé" : "Autorisation refusée"}</h1>`
        + `<p>${success ? "Vous pouvez fermer cette page et revenir à l’installation." : "Revenez à l’installation pour voir le détail."}</p>`
        + `</body></html>`
      );
      server.close();
      if (timeout) clearTimeout(timeout);
      if (!validState) reject(new Error("État OAuth invalide."));
      else if (oauthError) reject(new Error(`Kick a refusé l’autorisation : ${oauthError}`));
      else if (!code) reject(new Error("Code OAuth Kick absent."));
      else resolve(code);
    });
    server.on("error", (error) => {
      if (timeout) clearTimeout(timeout);
      reject(error);
    });
    const host = redirect.hostname === "localhost" ? "127.0.0.1" : redirect.hostname;
    server.listen(Number(redirect.port), host);
    timeout = setTimeout(() => {
      server.close();
      reject(new Error("Autorisation Kick expirée après 10 minutes."));
    }, 10 * 60_000);
    timeout.unref();
  });

  process.stdout.write("\n=== Autorisation officielle Kick pour FiIyx ===\n");
  process.stdout.write("Une page Kick va s’ouvrir. Connectez-vous à FiIyx puis cliquez sur Autoriser.\n\n");
  process.stdout.write(`URL d'autorisation Kick :\n${authorization.toString()}\n\n`);
  openBrowser(authorization.toString());

  const code = await callback;
  const vault = new SessionVault(config.vaultPath, config.oauthMasterKey);
  const client = new KickOAuthClient(config, vault);
  const session = await client.exchangeAuthorizationCode(code, verifier);
  process.stdout.write(`Compte ${session.accountUsername} vérifié. Autorisation OAuth chiffrée enregistrée.\n`);
};

main().catch((error: unknown) => {
  const message = error instanceof Error ? error.message : "Erreur inconnue";
  process.stderr.write(`Assistant OAuth FilyX : ${message}\n`);
  process.exitCode = 1;
});
