"use client";

import type { ThemeMode, ThemeSettings } from "@domainops/shared-types";
import { App, ConfigProvider, theme as antdTheme } from "antd";
import type { AliasToken } from "antd/es/theme/interface";
import { createContext, useContext, useEffect, useState } from "react";

const FONT_FAMILY = '"Inter", "Segoe UI Variable Display", "Segoe UI", system-ui, -apple-system, sans-serif';

interface PorcelainPalette {
  token: Partial<AliasToken>;
  shadow: string;
  tableHeaderBg: string;
  rowHoverBg: string;
}

/**
 * The Porcelain surface palette (docs/interface-design-concepts-2026.pdf,
 * concept B): a soft grey page, white cards, thin cool borders - and its
 * dark counterpart. The accent and status colours still come from
 * Settings > Appearance, so branding stays an admin choice.
 */
const PORCELAIN_LIGHT: PorcelainPalette = {
  token: {
    colorBgLayout: "#f3f4f9",
    colorBgContainer: "#ffffff",
    colorBgElevated: "#ffffff",
    colorBorder: "#dfe2ef",
    colorBorderSecondary: "#eceef7",
    colorText: "#1d2133",
    colorTextSecondary: "#6a7090",
    colorTextTertiary: "#8a90ad",
    colorFillAlter: "#f7f8fd",
  },
  shadow: "0 1px 2px rgba(30, 35, 90, 0.04), 0 10px 30px rgba(30, 35, 90, 0.06)",
  tableHeaderBg: "#f7f8fd",
  rowHoverBg: "#f7f8ff",
};

const PORCELAIN_DARK: PorcelainPalette = {
  token: {
    colorBgLayout: "#0e1020",
    colorBgContainer: "#161a2e",
    colorBgElevated: "#1b2036",
    colorBorder: "#2a2f4a",
    colorBorderSecondary: "#222740",
    colorText: "#e6e8f6",
    colorTextSecondary: "#a0a7c8",
    colorTextTertiary: "#7b82a6",
    colorFillAlter: "#1a1f35",
  },
  shadow: "0 1px 2px rgba(0, 0, 0, 0.3), 0 10px 30px rgba(0, 0, 0, 0.25)",
  tableHeaderBg: "#1a1f35",
  rowHoverBg: "#1c2140",
};

const ThemeSettingsContext = createContext<ThemeSettings | null>(null);

export function useThemeSettings(): ThemeSettings {
  const context = useContext(ThemeSettingsContext);
  if (!context) {
    throw new Error("useThemeSettings must be used within a ThemeProvider");
  }
  return context;
}

interface ThemeModeContextValue {
  /** The signed-in user's own override, or null if they haven't set one / can't. */
  mode: ThemeMode | null;
  setMode: (mode: ThemeMode | null) => void;
  /** The resolved light/dark rendering right now - "system" already collapsed away. */
  isDark: boolean;
}

const ThemeModeContext = createContext<ThemeModeContextValue | null>(null);

/**
 * The signed-in user's personal light/dark/system override - separate from
 * useThemeSettings() (the site-wide admin default) so most consumers don't
 * need to know an override exists. setMode() only changes the *visual*
 * state immediately; persisting it to the user's own row is the caller's
 * job (see the theme selector in the dashboard shell).
 */
export function useThemeMode(): ThemeModeContextValue {
  const context = useContext(ThemeModeContext);
  if (!context) {
    throw new Error("useThemeMode must be used within a ThemeProvider");
  }
  return context;
}

/**
 * Applies the super-admin-controlled appearance settings at runtime via
 * Ant Design's ConfigProvider - no rebuild needed. See docs/ARCHITECTURE.md
 * §6.8. Component-level style choices (spacing scale, card/button style,
 * background style) are exposed as data attributes on the wrapper for our
 * own components to key off, alongside the AntD color/radius tokens. The
 * raw settings are also exposed via useThemeSettings() for pages (e.g.
 * login) that need a specific field like login_layout directly.
 *
 * A signed-in user's own light/dark/system preference (theme_mode on their
 * own row) overrides theme.mode when allow_user_override is on - see
 * useThemeMode(). Nothing here fetches or persists that preference; the
 * dashboard shell syncs it in once the user is known.
 */
export function ThemeProvider({
  theme,
  children,
}: {
  theme: ThemeSettings;
  children: React.ReactNode;
}) {
  const [prefersDark, setPrefersDark] = useState(false);
  const [userMode, setUserMode] = useState<ThemeMode | null>(null);

  const effectiveMode = theme.allow_user_override && userMode ? userMode : theme.mode;

  useEffect(() => {
    if (effectiveMode !== "system") return;

    const query = window.matchMedia("(prefers-color-scheme: dark)");
    // Reads the OS preference, a browser API with no server-side
    // equivalent - a synchronous effect read, not a state synchronization bug.
    // eslint-disable-next-line react-hooks/set-state-in-effect
    setPrefersDark(query.matches);

    const listener = (event: MediaQueryListEvent) => setPrefersDark(event.matches);
    query.addEventListener("change", listener);
    return () => query.removeEventListener("change", listener);
  }, [effectiveMode]);

  const isDark = effectiveMode === "dark" || (effectiveMode === "system" && prefersDark);

  useEffect(() => {
    // The Porcelain surfaces, borders and text in globals.css key off this
    // attribute, so plain HTML (outside AntD) follows light/dark too.
    document.documentElement.dataset.theme = isDark ? "dark" : "light";
    document.documentElement.style.colorScheme = isDark ? "dark" : "light";
  }, [isDark]);

  // Each mode has its own primary/success/warning/error/info set (not one
  // set shared between light and dark) - see docs/ARCHITECTURE.md §6.8.
  // AntD derives each semantic color's full palette (backgrounds, borders,
  // hover states, ...) from these tokens. Including a key with an
  // `undefined` value - even just `colorError: undefined` - breaks that
  // derivation (it doesn't fall back to the default the way *omitting* the
  // key does), rendering every "error" surface near-black instead of red.
  // Found live: a failed-login alert with an invisible icon and a
  // background barely distinguishable from the card behind it.
  const colorTokens: Partial<AliasToken> = {
    colorPrimary: isDark ? theme.dark_primary_color : theme.light_primary_color,
    borderRadius: theme.border_radius,
  };
  const successColor = isDark ? theme.dark_success_color : theme.light_success_color;
  const warningColor = isDark ? theme.dark_warning_color : theme.light_warning_color;
  const errorColor = isDark ? theme.dark_error_color : theme.light_error_color;
  const infoColor = isDark ? theme.dark_info_color : theme.light_info_color;
  if (successColor) colorTokens.colorSuccess = successColor;
  if (warningColor) colorTokens.colorWarning = warningColor;
  if (errorColor) colorTokens.colorError = errorColor;
  if (infoColor) colorTokens.colorInfo = infoColor;

  const porcelain = isDark ? PORCELAIN_DARK : PORCELAIN_LIGHT;
  const radius = theme.border_radius;

  return (
    <ThemeSettingsContext.Provider value={theme}>
      <ThemeModeContext.Provider value={{ mode: userMode, setMode: setUserMode, isDark }}>
        <ConfigProvider
          modal={{ mask: { closable: false } }}
          drawer={{ mask: { closable: false } }}
          theme={{
            algorithm: isDark ? antdTheme.darkAlgorithm : antdTheme.defaultAlgorithm,
            token: {
              ...porcelain.token,
              fontFamily: FONT_FAMILY,
              controlHeight: 38,
              ...colorTokens,
            },
            components: {
              Card: { borderRadiusLG: Math.round(radius * 1.8), headerFontSize: 15, boxShadowTertiary: porcelain.shadow },
              Table: {
                headerBg: porcelain.tableHeaderBg,
                headerColor: porcelain.token.colorTextSecondary,
                headerSplitColor: "transparent",
                borderColor: porcelain.token.colorBorderSecondary,
                rowHoverBg: porcelain.rowHoverBg,
                cellPaddingBlock: 12,
                headerBorderRadius: radius,
              },
              Button: { borderRadius: radius, borderRadiusSM: Math.max(radius - 2, 6), borderRadiusLG: radius, fontWeight: 600 },
              Input: { borderRadius: radius },
              Select: { borderRadius: radius },
              DatePicker: { borderRadius: radius },
              InputNumber: { borderRadius: radius },
              Modal: { borderRadiusLG: Math.round(radius * 2), titleFontSize: 18 },
              Drawer: { borderRadiusLG: Math.round(radius * 2) },
              Tag: { borderRadiusSM: Math.max(radius - 4, 6) },
              Segmented: { borderRadius: radius, borderRadiusSM: Math.max(radius - 3, 6) },
              Tabs: { titleFontSize: 14, horizontalMargin: "0 0 12px 0" },
              Menu: { itemBorderRadius: radius, horizontalItemBorderRadius: radius },
            },
          }}
        >
          <App className="min-h-full flex flex-col flex-1">
            <div
              data-spacing={theme.spacing_scale}
              data-card-style={theme.card_style}
              data-button-style={theme.button_style}
              data-background-style={theme.background_style}
              className="min-h-full flex flex-col flex-1"
            >
              {children}
            </div>
          </App>
        </ConfigProvider>
      </ThemeModeContext.Provider>
    </ThemeSettingsContext.Provider>
  );
}
