import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { registerSW } from "virtual:pwa-register";
import App from "./App";
import { redirectToCanonicalFoodHost } from "./canonical-host";
import { malformedStorePathSegment } from "./store-slug";
import "./styles.css";

// Preview `xxxx.maxyon-food.pages.dev` → host permanente (sem letras/números extras).
const leavingPreviewHost = redirectToCanonicalFoodHost();

/** Remove path inválido (ex.: /%60 = backtick) preservando query/hash. */
function canonicalizeMalformedStorePath(): void {
  if (typeof window === "undefined") return;
  const bad = malformedStorePathSegment(window.location.pathname);
  if (bad == null) return;
  const next = `/${window.location.search}${window.location.hash}`;
  window.history.replaceState(null, "", next || "/");
}

canonicalizeMalformedStorePath();

const RECOVERY_KEY = "maxyon-build-recovery";

/** Limpa caches de catálogo antigos que prendiam o celular em "Carregando…". */
async function purgeStaleCatalogCaches(): Promise<void> {
  if (!("caches" in window)) return;
  try {
    const keys = await caches.keys();
    await Promise.all(
      keys
        .filter((key) => key.includes("food-last-catalog"))
        .map((key) => caches.delete(key)),
    );
  } catch {
    // Sem cache API (modo privado): segue o boot normal.
  }
}

async function purgeAppShell(): Promise<void> {
  try {
    if ("caches" in window) {
      const keys = await caches.keys();
      await Promise.all(keys.map((key) => caches.delete(key)));
    }
    if ("serviceWorker" in navigator) {
      const registrations = await navigator.serviceWorker.getRegistrations();
      await Promise.all(registrations.map((item) => item.unregister()));
    }
  } catch {
    // Recarregar já resolve na maioria dos casos.
  }
}

function recoveryMark(): string | null {
  try {
    return sessionStorage.getItem(RECOVERY_KEY);
  } catch {
    return null;
  }
}

/**
 * Compara o selo embutido no bundle com `version.json` (fora do precache). Se o
 * celular estiver preso numa shell antiga, limpa tudo e recarrega uma única vez
 * por build publicada — sem depender de ação do cliente.
 */
async function ensureFreshBuild(): Promise<void> {
  let remote: string | null = null;
  try {
    const response = await fetch(`/version.json?ts=${Date.now()}`, {
      cache: "no-store",
    });
    if (!response.ok) return;
    const payload = (await response.json()) as { buildId?: unknown };
    remote = typeof payload.buildId === "string" ? payload.buildId : null;
  } catch {
    return;
  }
  if (!remote || remote === __APP_BUILD__ || recoveryMark() === remote) return;
  try {
    sessionStorage.setItem(RECOVERY_KEY, remote);
  } catch {
    // Modo privado: segue sem trava de loop, o reload continua sendo único.
  }
  await purgeAppShell();
  window.location.reload();
}

if (!leavingPreviewHost) {
  void purgeStaleCatalogCaches();
  void ensureFreshBuild();

  document.addEventListener("visibilitychange", () => {
    if (document.visibilityState === "visible") void ensureFreshBuild();
  });

  const updateSW = registerSW({
    immediate: true,
    onNeedRefresh() {
      // Nova versão do SW: ativa e recarrega para o celular sair do shell velho.
      void updateSW(true);
    },
    onRegisteredSW(_url, registration) {
      if (!registration) return;
      // Procura update ao abrir e a cada minuto (abas longas no celular).
      void registration.update();
      window.setInterval(() => {
        void registration.update();
      }, 60_000);
    },
  });

  createRoot(document.getElementById("root")!).render(
    <StrictMode>
      <App />
    </StrictMode>,
  );
}
