"use client";

import { createContext, useContext, useState, useEffect, startTransition, type ReactNode } from "react";
import {
  parseThemeQuery,
  resolveIncomingThemeMode,
  resolveStoredTheme,
  type ResolvedTheme,
} from "./theme-sync";

export type Theme = "light" | "dark" | "system";

interface ThemeContextType {
  theme: Theme;
  setTheme: (t: Theme) => void;
  resolved: ResolvedTheme;
  isEmbedded: boolean;
}

function getPrefersDark() {
  if (typeof window === "undefined") {
    return true;
  }

  return window.matchMedia("(prefers-color-scheme: dark)").matches;
}

function getIsEmbedded() {
  if (typeof window === "undefined") {
    return false;
  }

  try {
    return window.self !== window.top;
  } catch {
    return true;
  }
}

function getParentResolvedTheme(): ResolvedTheme | null {
  if (!getIsEmbedded()) {
    return null;
  }

  try {
    const parentRoot = window.parent.document.documentElement;
    const parentBody = window.parent.document.body;

    if (parentBody?.getAttribute("theme-mode") === "dark" || parentRoot.classList.contains("dark")) {
      return "dark";
    }

    return "light";
  } catch {
    return null;
  }
}

function getThemeQuery(): Theme | null {
  if (typeof window === "undefined") {
    return null;
  }

  return parseThemeQuery(new URLSearchParams(window.location.search).get("theme"));
}

const ThemeContext = createContext<ThemeContextType>({
  theme: "system",
  setTheme: () => {},
  resolved: "dark",
  isEmbedded: false,
});

export function ThemeProvider({ children }: { children: ReactNode }) {
  const [theme, setThemeState] = useState<Theme>("system");
  const [isEmbedded, setIsEmbedded] = useState(getIsEmbedded);
  const [queryTheme, setQueryTheme] = useState<Theme | null>(getThemeQuery);
  const [parentResolved, setParentResolved] = useState<ResolvedTheme | null>(getParentResolvedTheme);
  const [resolved, setResolved] = useState<ResolvedTheme>(
    () => {
      const nextQueryTheme = getThemeQuery();

      return (
        getParentResolvedTheme()
        ?? (nextQueryTheme ? resolveStoredTheme(nextQueryTheme, getPrefersDark()) : null)
        ?? resolveStoredTheme("system", getPrefersDark())
      );
    },
  );

  useEffect(() => {
    const saved = localStorage.getItem("theme") as Theme | null;
    if (saved && ["light", "dark", "system"].includes(saved)) {
      startTransition(() => {
        setThemeState(saved);
      });
    }
  }, []);

  useEffect(() => {
    const nextIsEmbedded = getIsEmbedded();
    const nextQueryTheme = getThemeQuery();
    const nextParentResolved = getParentResolvedTheme();

    startTransition(() => {
      setIsEmbedded(nextIsEmbedded);
      setQueryTheme(nextQueryTheme);
      setParentResolved((current) => current ?? nextParentResolved);
    });
  }, []);

  useEffect(() => {
    if (!isEmbedded) {
      return;
    }

    const handleMessage = (event: MessageEvent) => {
      if (event.source !== window.parent) {
        return;
      }

      const nextResolved = resolveIncomingThemeMode(
        event.data?.themeMode,
        window.matchMedia("(prefers-color-scheme: dark)").matches,
      );
      if (nextResolved) {
        setParentResolved(nextResolved);
      }
    };

    window.addEventListener("message", handleMessage);
    return () => window.removeEventListener("message", handleMessage);
  }, [isEmbedded]);

  useEffect(() => {
    const mq = window.matchMedia("(prefers-color-scheme: dark)");
    const root = document.documentElement;
    const activeTheme = queryTheme ?? theme;
    const queryResolved = queryTheme
      ? resolveStoredTheme(queryTheme, mq.matches)
      : null;

    const applyResolved = (nextResolved: ResolvedTheme) => {
      setResolved(nextResolved);
      root.classList.remove("light", "dark");
      root.classList.add(nextResolved);
      root.setAttribute("data-theme", nextResolved);
    };

    applyResolved(
      parentResolved
        ?? queryResolved
        ?? resolveStoredTheme(theme, mq.matches),
    );

    if (parentResolved !== null || activeTheme !== "system") {
      return;
    }

    const handleSystemThemeChange = (event: MediaQueryListEvent) => {
      applyResolved(resolveStoredTheme(activeTheme, event.matches));
    };

    mq.addEventListener("change", handleSystemThemeChange);
    return () => mq.removeEventListener("change", handleSystemThemeChange);
  }, [parentResolved, queryTheme, theme]);

  const setTheme = (t: Theme) => {
    setThemeState(t);
    localStorage.setItem("theme", t);
  };

  return (
    <ThemeContext.Provider value={{ theme, setTheme, resolved, isEmbedded }}>
      {children}
    </ThemeContext.Provider>
  );
}

export function useTheme() {
  return useContext(ThemeContext);
}
