import { app, dialog, type BrowserWindow } from "electron";
import electronUpdater, { type AppUpdater, type UpdateInfo } from "electron-updater";
import { isSmokeRun } from "./runtimeFlags.js";

const FIRST_CHECK_DELAY_MS = 10_000;
const CHECK_INTERVAL_MS = 6 * 60 * 60 * 1_000;

interface UpdateRuntime {
  isPackaged: boolean;
  platform: NodeJS.Platform;
  isPortable: boolean;
  isAppImage: boolean;
  isSmoke: boolean;
  disabled: boolean;
}

interface UpdateManagerOptions {
  updater: AppUpdater;
  confirmRestart(version: string): Promise<boolean>;
  logError(error: unknown): void;
  setTimeout(callback: () => void, delay: number): NodeJS.Timeout;
  setInterval(callback: () => void, delay: number): NodeJS.Timeout;
  clearTimeout(timer: NodeJS.Timeout): void;
  clearInterval(timer: NodeJS.Timeout): void;
}

export function updateDisabledReason(runtime: UpdateRuntime): string | null {
  if (!runtime.isPackaged) return "development build";
  if (runtime.isSmoke) return "smoke run";
  if (runtime.disabled) return "disabled by environment";
  if (runtime.isPortable) return "portable Windows build";
  if (runtime.platform === "darwin") return "unsigned macOS build";
  if (runtime.platform === "linux" && !runtime.isAppImage) return "non-AppImage Linux build";
  return null;
}

export async function startDesktopUpdates(window: BrowserWindow): Promise<() => void> {
  const disabledReason = updateDisabledReason({
    isPackaged: app.isPackaged,
    platform: process.platform,
    isPortable: Boolean(process.env.PORTABLE_EXECUTABLE_FILE),
    isAppImage: Boolean(process.env.APPIMAGE),
    isSmoke: isSmokeRun(),
    disabled: process.env.DESKTOP_DISABLE_UPDATES === "1",
  });
  if (disabledReason) return () => undefined;

  // electron-updater is CommonJS; destructuring the default import is its ESM-safe path.
  const { autoUpdater } = electronUpdater;
  return configureUpdateManager({
    updater: autoUpdater,
    confirmRestart: (version) => confirmRestart(window, version),
    logError: (error) => console.error("Desktop update error:", error),
    setTimeout,
    setInterval,
    clearTimeout,
    clearInterval,
  });
}

export function configureUpdateManager(options: UpdateManagerOptions): () => void {
  const { updater } = options;
  updater.autoDownload = true;
  updater.autoInstallOnAppQuit = true;

  let checkInFlight = false;
  let promptInFlight = false;
  const check = () => {
    if (checkInFlight) return;
    checkInFlight = true;
    void updater.checkForUpdates()
      // electron-updater reports check failures through its error event. Consume the
      // rejected promise as well so it cannot become an unhandled rejection.
      .catch(() => undefined)
      .finally(() => { checkInFlight = false; });
  };
  const onError = (error: Error) => options.logError(error);
  const onDownloaded = (info: UpdateInfo) => {
    if (promptInFlight) return;
    promptInFlight = true;
    void options.confirmRestart(info.version)
      .then((restart) => {
        if (restart) updater.quitAndInstall(false, true);
      })
      .catch(options.logError)
      .finally(() => { promptInFlight = false; });
  };

  updater.on("error", onError);
  updater.on("update-downloaded", onDownloaded);
  const firstCheck = options.setTimeout(check, FIRST_CHECK_DELAY_MS);
  const repeatedCheck = options.setInterval(check, CHECK_INTERVAL_MS);
  firstCheck.unref();
  repeatedCheck.unref();

  return () => {
    options.clearTimeout(firstCheck);
    options.clearInterval(repeatedCheck);
    updater.removeListener("error", onError);
    updater.removeListener("update-downloaded", onDownloaded);
  };
}

async function confirmRestart(window: BrowserWindow, version: string): Promise<boolean> {
  if (window.isDestroyed()) return false;
  const result = await dialog.showMessageBox(window, {
    type: "info",
    title: "Atjauninājums ir gatavs",
    message: `Versija ${version} ir lejupielādēta.`,
    detail: "Restartējiet lietotni, lai pabeigtu atjaunināšanu.",
    buttons: ["Restartēt tagad", "Vēlāk"],
    defaultId: 0,
    cancelId: 1,
  });
  return result.response === 0;
}
