/**
 * Extension update check: the server publishes the current version at
 * /api/ext/manifest; when it is newer than the installed extension, the popup
 * shows an "update available" banner (the core background/content scripts are
 * not hot-updatable in MV3 — updating means installing a new zip).
 */
import browser from "webextension-polyfill";
import { compareVersions } from "@ltt/shared";
import { getSettings, saveSettings, type Settings } from "./settings";

const CHECK_INTERVAL_MS = 12 * 60 * 60 * 1000; // 12h

interface ExtManifest {
  version?: string | null;
  assets?: unknown;
}

/** GET /api/ext/manifest with a hard timeout; null when unreachable. */
export async function fetchExtManifest(serverUrl: string, timeoutMs = 5000): Promise<ExtManifest | null> {
  const controller = new AbortController();
  const timer = setTimeout(() => controller.abort(), timeoutMs);
  try {
    const response = await fetch(`${serverUrl.replace(/\/+$/, "")}/api/ext/manifest`, {
      signal: controller.signal,
    });
    if (!response.ok) return null;
    return (await response.json()) as ExtManifest;
  } catch {
    return null;
  } finally {
    clearTimeout(timer);
  }
}

/**
 * Fetches the server manifest and stores the update state. Never throws —
 * a check is a background nicety, not a feature the UI waits on.
 */
export async function checkForUpdate(): Promise<{ remoteVersion: string | null; updateAvailable: boolean }> {
  try {
    const settings = await getSettings();
    const manifest = await fetchExtManifest(settings.serverUrl);
    const remoteVersion = typeof manifest?.version === "string" ? manifest.version : null;
    const updateAvailable = Boolean(
      remoteVersion && compareVersions(remoteVersion, browser.runtime.getManifest().version) > 0
    );
    await saveSettings({ remoteVersion, updateAvailable, lastVersionCheckAt: Date.now() });
    return { remoteVersion, updateAvailable };
  } catch {
    return { remoteVersion: null, updateAvailable: false };
  }
}

/** Check on service-worker start, throttled to CHECK_INTERVAL_MS. */
export async function maybeCheckForUpdate(): Promise<void> {
  try {
    const settings = await getSettings();
    if (Date.now() - (settings.lastVersionCheckAt ?? 0) < CHECK_INTERVAL_MS) return;
    await checkForUpdate();
  } catch {
    // storage may be unavailable early in the SW lifecycle — ignore
  }
}

/** Re-check when the server (or panel) URL changes in settings. */
export async function checkAfterSettingsChange(patch: Partial<Settings>): Promise<void> {
  if ("serverUrl" in patch || "panelUrl" in patch) await checkForUpdate();
}