import { StrictMode } from "react";
import { flushSync } from "react-dom";
import { createRoot } from "react-dom/client";
import "@fontsource-variable/inter";
import catalogJson from "../../data/catalog.json";
import type { CatalogPayload } from "../shared/types";
import App from "./App";
import { PlayerProvider } from "./player";
import { SessionProvider } from "./session";
import "./styles.css";
import { AppRouter } from "./router";
import { ChatShareProvider } from "./chat-share";

const catalog = catalogJson as CatalogPayload;

function preloadImage(url: string) {
  return new Promise<void>((resolve) => {
    const image = new Image();
    image.decoding = "async";
    const isBackground = url === "/mm-bg-dark.jpg";
    if (isBackground) console.log("[Mastery Music] Preloading background image:", url);
    image.onload = () => {
      if (isBackground) console.log("[Mastery Music] Background image ready");
      if (typeof image.decode === "function") void image.decode().catch(() => undefined).finally(resolve);
      else resolve();
    };
    image.onerror = () => {
      if (isBackground) console.error("[Mastery Music] Background image failed to load:", url);
      resolve();
    };
    image.src = url;
  });
}

async function preloadInterface() {
  const artwork = [
    ...catalog.artists.map((artist) => artist.image),
    ...catalog.songGroups.map((song) => song.artwork),
    ...catalog.mixes.map((mix) => mix.artwork),
  ];
  const urls = new Set<string>([
    "/icons/app-icon-192.png",
    "/media/brand/mastery-music-logo.webp",
    "/mascots/master-e-kingcrab.webp",
    "/mascots/lil-seaturtlee.webp",
    "/mm-bg-dark.jpg", // site background image, preloaded for instant paint
    ...artwork.flatMap((item) => [item.small, item.medium, item.large, item.fallback]),
  ]);
  const bar = document.querySelector<HTMLElement>(".preload-progress");
  const track = document.querySelector<HTMLElement>(".preload-track");
  const total = urls.size + 1;
  let completed = 0;
  const advance = () => {
    completed += 1;
    const progress = Math.min(1, completed / total);
    bar?.style.setProperty("--preload-progress", String(progress));
    track?.setAttribute("aria-valuenow", String(Math.round(progress * 100)));
  };
  const jobs = [...urls].map((url) => preloadImage(url).finally(advance));
  jobs.push((document.fonts?.ready ?? Promise.resolve()).then(() => undefined).finally(advance));
  const assetsReady = Promise.allSettled(jobs);
  const timeout = new Promise<void>((resolve) => window.setTimeout(resolve, 15000));
  await Promise.all([Promise.race([assetsReady, timeout]), new Promise<void>((resolve) => window.setTimeout(resolve, 450))]);
  bar?.style.setProperty("--preload-progress", "1");
  track?.setAttribute("aria-valuenow", "100");
}

async function boot() {
  await preloadInterface().catch(() => undefined);
  const root = createRoot(document.getElementById("root")!);
  flushSync(() => root.render(
    <StrictMode>
      <AppRouter>
        <SessionProvider>
          <PlayerProvider><ChatShareProvider><App /></ChatShareProvider></PlayerProvider>
        </SessionProvider>
      </AppRouter>
    </StrictMode>,
  ));
  requestAnimationFrame(() => {
    document.body.classList.remove("mm-loading");
    document.body.classList.add("mm-ready");
    const preloader = document.getElementById("app-preloader");
    preloader?.classList.add("is-done");
    window.setTimeout(() => preloader?.remove(), 260);
  });
}

void boot();

if (import.meta.env.PROD && "serviceWorker" in navigator) {
  window.addEventListener("load", () => {
    void navigator.serviceWorker.register("/sw.js");
  });
}
