import { app, BrowserWindow, ipcMain, session, shell, type IpcMainInvokeEvent } from "electron";
import { mkdir } from "node:fs/promises";
import { join } from "node:path";
import { pathToFileURL } from "node:url";
import type { LauncherSettings, LauncherSnapshot } from "./shared/contracts.js";
import { loadLauncherConfig, resolveLauncherPaths } from "./main/config.js";
import { LauncherLogger } from "./main/logger.js";
import { LauncherOrchestrator } from "./main/orchestrator.js";
import { isTrustedRendererDocument } from "./main/renderer-security.js";

const SUPPORT_URL = "https://discord.gg/bglands";
const MICROSOFT_SETUP_URL = "https://entra.microsoft.com/#view/Microsoft_AAD_RegisteredApps/ApplicationsListBlade";

if (!app.requestSingleInstanceLock()) {
  app.quit();
} else {
  app.enableSandbox();
}

let mainWindow: BrowserWindow | undefined;

function trustedSender(event: IpcMainInvokeEvent, rendererUrl: string): void {
  if (!event.senderFrame || !isTrustedRendererDocument(event.senderFrame.url, rendererUrl)) {
    throw new Error("Origine IPC non autorisée");
  }
}

async function createApplication(): Promise<void> {
  const paths = resolveLauncherPaths();
  await mkdir(paths.logs, { recursive: true, mode: 0o700 });
  const logger = new LauncherLogger(paths.logs);
  await logger.initialize();
  process.on("uncaughtException", (error) => void logger.error("Erreur non interceptée", error));
  process.on("unhandledRejection", (error) => void logger.error("Promesse rejetée", error));

  const config = await loadLauncherConfig(paths);
  const orchestrator = new LauncherOrchestrator(config, paths, logger);
  const rendererPath = join(__dirname, "renderer", "index.html");
  const rendererUrl = pathToFileURL(rendererPath).toString();
  mainWindow = new BrowserWindow({
    width: 1180,
    height: 760,
    minWidth: 900,
    minHeight: 620,
    show: false,
    backgroundColor: "#080a0f",
    autoHideMenuBar: true,
    titleBarStyle: "hidden",
    titleBarOverlay: {
      color: "#070A08",
      symbolColor: "#F4F1E8",
      height: 46,
    },
    title: "BG LANDS Launcher",
    webPreferences: {
      preload: join(__dirname, "preload.cjs"),
      nodeIntegration: false,
      contextIsolation: true,
      sandbox: true,
      webSecurity: true,
      allowRunningInsecureContent: false,
      devTools: !app.isPackaged,
    },
  });
  mainWindow.webContents.setWindowOpenHandler(() => ({ action: "deny" }));
  mainWindow.webContents.on("will-navigate", (event, url) => {
    if (!isTrustedRendererDocument(url, rendererUrl)) event.preventDefault();
  });
  mainWindow.once("ready-to-show", () => mainWindow?.show());
  mainWindow.on("closed", () => { mainWindow = undefined; });

  const register = <T extends unknown[]>(channel: string, handler: (...args: T) => Promise<unknown> | unknown): void => {
    ipcMain.handle(channel, (event, ...args: unknown[]) => {
      trustedSender(event, rendererUrl);
      return handler(...args as T);
    });
  };
  register("bglands:get-snapshot", () => orchestrator.getSnapshot());
  register("bglands:connect", () => orchestrator.connectMicrosoft());
  register("bglands:cancel-connect", () => orchestrator.cancelMicrosoftLogin());
  register("bglands:disconnect", () => orchestrator.disconnectMicrosoft());
  register("bglands:play", () => orchestrator.play());
  register("bglands:repair", () => orchestrator.repair());
  register("bglands:save-settings", (settings: LauncherSettings) => orchestrator.saveSettings(settings));
  register("bglands:configure-microsoft", (clientId: string) => orchestrator.configureMicrosoft(clientId));
  register("bglands:open-microsoft-setup", async () => {
    await shell.openExternal(MICROSOFT_SETUP_URL, { activate: true });
  });
  register("bglands:open-logs", async () => {
    const error = await shell.openPath(paths.logs);
    if (error) throw new Error(`Windows n'a pas pu ouvrir le dossier des logs: ${error}`);
  });
  register("bglands:open-support", async () => {
    await shell.openExternal(SUPPORT_URL, { activate: true });
  });
  register("bglands:open-login", async () => {
    const raw = orchestrator.getMicrosoftLoginUrl();
    if (!raw) throw new Error("Aucune connexion Microsoft en attente");
    const url = new URL(raw);
    const microsoftHost = url.hostname === "microsoft.com"
      || url.hostname.endsWith(".microsoft.com")
      || url.hostname === "microsoftonline.com"
      || url.hostname.endsWith(".microsoftonline.com")
      || url.hostname === "login.live.com"
      || url.hostname === "aka.ms";
    if (url.protocol !== "https:" || !microsoftHost || url.username || url.password) {
      throw new Error("URL Microsoft non approuvée");
    }
    await shell.openExternal(url.toString(), { activate: true });
  });
  orchestrator.on("snapshot", (snapshot: LauncherSnapshot) => {
    if (!mainWindow?.isDestroyed()) mainWindow?.webContents.send("bglands:snapshot", snapshot);
    if (snapshot.phase === "running" && snapshot.settings.closeLauncherOnGameStart) mainWindow?.hide();
    if ((snapshot.phase === "ready" || snapshot.phase === "error") && !mainWindow?.isVisible()) mainWindow?.show();
  });

  await mainWindow.loadFile(rendererPath);
  await orchestrator.initialize();
  app.on("second-instance", () => {
    if (!mainWindow) return;
    if (mainWindow.isMinimized()) mainWindow.restore();
    mainWindow.show();
    mainWindow.focus();
  });
}

app.whenReady().then(async () => {
  session.defaultSession.setPermissionRequestHandler((_webContents, _permission, callback) => callback(false));
  session.defaultSession.setPermissionCheckHandler(() => false);
  await createApplication();
}).catch((error) => {
  console.error(error);
  app.quit();
});

app.on("window-all-closed", () => app.quit());
