import { useEffect, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import { useAppStore } from "../../../stores/useAppStore";
import { setAppSetting } from "../../../services/tauri";
import { applyTheme, applyUserFonts, clearUserFont, loadAllThemes, findTheme, cacheThemePreference, getThemeDataAttr } from "../../../utils/theme";
import {
  UI_FONT_SIZE_MIN,
  UI_FONT_SIZE_MAX,
  UI_FONT_SIZE_DEFAULT,
  TERMINAL_FONT_SIZE_MIN,
  TERMINAL_FONT_SIZE_MAX,
  buildFontOptions,
} from "../../../utils/fontSettings";
import type { ThemeDefinition } from "../../../types/theme";
import { FontSelect } from "../FontSelect";
import styles from "../Settings.module.css";

export function AppearanceSettings() {
  const { t } = useTranslation("settings");
  const setCurrentThemeId = useAppStore((s) => s.setCurrentThemeId);
  const themeMode = useAppStore((s) => s.themeMode);
  const setThemeMode = useAppStore((s) => s.setThemeMode);
  const themeDark = useAppStore((s) => s.themeDark);
  const setThemeDark = useAppStore((s) => s.setThemeDark);
  const themeLight = useAppStore((s) => s.themeLight);
  const setThemeLight = useAppStore((s) => s.setThemeLight);
  const terminalFontSize = useAppStore((s) => s.terminalFontSize);
  const setTerminalFontSize = useAppStore((s) => s.setTerminalFontSize);
  const uiFontSize = useAppStore((s) => s.uiFontSize);
  const setUiFontSize = useAppStore((s) => s.setUiFontSize);
  const fontFamilySans = useAppStore((s) => s.fontFamilySans);
  const setFontFamilySans = useAppStore((s) => s.setFontFamilySans);
  const fontFamilyMono = useAppStore((s) => s.fontFamilyMono);
  const setFontFamilyMono = useAppStore((s) => s.setFontFamilyMono);
  const systemFonts = useAppStore((s) => s.systemFonts);
  const showSidebarRunningCommands = useAppStore((s) => s.showSidebarRunningCommands);
  const setShowSidebarRunningCommands = useAppStore((s) => s.setShowSidebarRunningCommands);
  const toolDisplayMode = useAppStore((s) => s.toolDisplayMode);
  const setToolDisplayMode = useAppStore((s) => s.setToolDisplayMode);
  const extendedToolCallOutput = useAppStore((s) => s.extendedToolCallOutput);
  const setExtendedToolCallOutput = useAppStore((s) => s.setExtendedToolCallOutput);

  const isFollowSystem = themeMode === "system";

  const [availableThemes, setAvailableThemes] = useState<ThemeDefinition[]>([]);
  const [termFontSize, setTermFontSize] = useState(String(terminalFontSize));
  const [uiFontSizeStr, setUiFontSizeStr] = useState(String(uiFontSize));
  const [error, setError] = useState<string | null>(null);

  // Sync the UI font size input when it changes externally (Cmd+/-, View menu).
  useEffect(() => {
    setUiFontSizeStr(String(uiFontSize));
  }, [uiFontSize]);

  // Derive font option lists from the pre-loaded system fonts in the store.
  const { sans: sansFontOptions, mono: monoFontOptions } = useMemo(
    () => buildFontOptions(systemFonts),
    [systemFonts],
  );

  // Custom font input state — shown when "Custom..." is selected or
  // the stored font isn't in the system font list.
  const isCustomSans = fontFamilySans !== "" &&
    !sansFontOptions.some((o) => o.value === fontFamilySans);
  const isCustomMono = fontFamilyMono !== "" &&
    !monoFontOptions.some((o) => o.value === fontFamilyMono);
  const [showCustomSans, setShowCustomSans] = useState(false);
  const [showCustomMono, setShowCustomMono] = useState(false);
  const [customSans, setCustomSans] = useState("");
  const [customMono, setCustomMono] = useState("");

  // Sync custom state when system fonts load or stored font changes.
  useEffect(() => {
    setShowCustomSans(isCustomSans);
    setCustomSans(isCustomSans ? fontFamilySans : "");
  }, [isCustomSans, fontFamilySans]);
  useEffect(() => {
    setShowCustomMono(isCustomMono);
    setCustomMono(isCustomMono ? fontFamilyMono : "");
  }, [isCustomMono, fontFamilyMono]);

  useEffect(() => {
    loadAllThemes().then(setAvailableThemes).catch(() => {});
  }, []);

  const handleModeChange = async (mode: "light" | "dark" | "system") => {
    if (availableThemes.length === 0) return;
    const systemIsDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
    const effectiveId =
      mode === "system" ? (systemIsDark ? themeDark : themeLight) :
      mode === "light" ? themeLight : themeDark;
    const theme = findTheme(availableThemes, effectiveId);
    applyTheme(theme);
    applyUserFonts(fontFamilySans, fontFamilyMono, uiFontSize);
    setThemeMode(mode);
    setCurrentThemeId(theme.id);
    cacheThemePreference(
      mode,
      getThemeDataAttr(findTheme(availableThemes, themeDark)),
      getThemeDataAttr(findTheme(availableThemes, themeLight)),
    );
    try {
      setError(null);
      await setAppSetting("theme_mode", mode);
    } catch (e) {
      setError(String(e));
    }
  };

  const handleDarkThemeChange = async (id: string) => {
    const theme = findTheme(availableThemes, id);
    const isEffective =
      themeMode === "dark" ||
      (themeMode === "system" && window.matchMedia("(prefers-color-scheme: dark)").matches);
    if (isEffective) {
      applyTheme(theme);
      applyUserFonts(fontFamilySans, fontFamilyMono, uiFontSize);
      setCurrentThemeId(theme.id);
    }
    setThemeDark(theme.id);
    cacheThemePreference(
      themeMode,
      getThemeDataAttr(theme),
      getThemeDataAttr(findTheme(availableThemes, themeLight)),
    );
    try {
      setError(null);
      await setAppSetting("theme_dark", theme.id);
    } catch (e) {
      setError(String(e));
    }
  };

  const handleLightThemeChange = async (id: string) => {
    const theme = findTheme(availableThemes, id);
    const isEffective =
      themeMode === "light" ||
      (themeMode === "system" && !window.matchMedia("(prefers-color-scheme: dark)").matches);
    if (isEffective) {
      applyTheme(theme);
      applyUserFonts(fontFamilySans, fontFamilyMono, uiFontSize);
      setCurrentThemeId(theme.id);
    }
    setThemeLight(theme.id);
    cacheThemePreference(
      themeMode,
      getThemeDataAttr(findTheme(availableThemes, themeDark)),
      getThemeDataAttr(theme),
    );
    try {
      setError(null);
      await setAppSetting("theme_light", theme.id);
    } catch (e) {
      setError(String(e));
    }
  };

  const handleFollowSystemToggle = async () => {
    if (isFollowSystem) {
      const systemIsDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
      await handleModeChange(systemIsDark ? "dark" : "light");
    } else {
      await handleModeChange("system");
    }
  };

  const handleTermFontSizeBlur = async () => {
    const size = parseInt(termFontSize, 10);
    if (
      isNaN(size) ||
      size < TERMINAL_FONT_SIZE_MIN ||
      size > TERMINAL_FONT_SIZE_MAX
    ) {
      setTermFontSize(String(terminalFontSize));
      return;
    }
    if (size !== terminalFontSize) {
      try {
        setError(null);
        await setAppSetting("terminal_font_size", String(size));
        setTerminalFontSize(size);
      } catch (e) {
        setTermFontSize(String(terminalFontSize));
        setError(String(e));
      }
    }
  };

  const handleUiFontSizeBlur = async () => {
    const size = parseInt(uiFontSizeStr, 10);
    if (isNaN(size) || size < UI_FONT_SIZE_MIN || size > UI_FONT_SIZE_MAX) {
      setUiFontSizeStr(String(uiFontSize));
      return;
    }
    if (size !== uiFontSize) {
      try {
        setError(null);
        setUiFontSize(size);
        applyUserFonts(fontFamilySans, fontFamilyMono, size);
        await setAppSetting("ui_font_size", String(size));
      } catch (e) {
        setUiFontSizeStr(String(uiFontSize));
        setError(String(e));
      }
    }
  };

  const applyFontFamily = async (
    key: "font_family_sans" | "font_family_mono",
    value: string,
  ) => {
    try {
      setError(null);
      const cssVar = key === "font_family_sans" ? "font-sans" : "font-mono";
      if (key === "font_family_sans") {
        setFontFamilySans(value);
        if (value) {
          applyUserFonts(value, fontFamilyMono, uiFontSize);
        } else {
          clearUserFont(cssVar);
        }
      } else {
        setFontFamilyMono(value);
        if (value) {
          applyUserFonts(fontFamilySans, value, uiFontSize);
        } else {
          clearUserFont(cssVar);
        }
      }
      await setAppSetting(key, value);
    } catch (e) {
      setError(String(e));
    }
  };

  const handleSansChange = (selectValue: string) => {
    if (selectValue === "__custom__") {
      setShowCustomSans(true);
      return;
    }
    setShowCustomSans(false);
    setCustomSans("");
    applyFontFamily("font_family_sans", selectValue);
  };

  const handleMonoChange = (selectValue: string) => {
    if (selectValue === "__custom__") {
      setShowCustomMono(true);
      return;
    }
    setShowCustomMono(false);
    setCustomMono("");
    applyFontFamily("font_family_mono", selectValue);
  };

  const handleCustomSansBlur = () => {
    const trimmed = customSans.trim();
    if (trimmed) {
      applyFontFamily("font_family_sans", trimmed);
    }
  };

  const handleCustomMonoBlur = () => {
    const trimmed = customMono.trim();
    if (trimmed) {
      applyFontFamily("font_family_mono", trimmed);
    }
  };

  const isMac =
    ((navigator as unknown as Record<string, unknown>).userAgentData as
      | { platform?: string }
      | undefined)
      ?.platform?.toLowerCase()
      .startsWith("mac") ?? navigator.platform.startsWith("Mac");

  return (
    <div>
      <h2 className={styles.sectionTitle}>{t("appearance_title")}</h2>

      {error && <div className={styles.error}>{error}</div>}

      <div className={styles.settingRow}>
        <div className={styles.settingInfo}>
          <div className={styles.settingLabel}>{t("appearance_follow_system")}</div>
          <div className={styles.settingDescription}>
            {t("appearance_follow_system_desc")}
          </div>
        </div>
        <div className={styles.settingControl}>
          <button
            className={styles.toggle}
            role="switch"
            aria-checked={isFollowSystem}
            aria-label={t("appearance_follow_system_aria")}
            data-checked={isFollowSystem}
            onClick={handleFollowSystemToggle}
          >
            <div className={styles.toggleKnob} />
          </button>
        </div>
      </div>

      {themeMode !== "system" ? (
        <div className={styles.settingRow}>
          <div className={styles.settingInfo}>
            <div className={styles.settingLabel}>{t("appearance_color_theme")}</div>
            <div className={styles.settingDescription}>
              {t("appearance_color_theme_desc")}
            </div>
          </div>
          <div className={styles.settingControl}>
            <select
              className={styles.select}
              value={themeMode === "dark" ? themeDark : themeLight}
              onChange={(e) =>
                themeMode === "dark"
                  ? handleDarkThemeChange(e.target.value)
                  : handleLightThemeChange(e.target.value)
              }
            >
              {availableThemes.map((theme) => (
                <option key={theme.id} value={theme.id}>
                  {theme.name}
                </option>
              ))}
            </select>
          </div>
        </div>
      ) : (
        <>
          <div className={styles.settingRow}>
            <div className={styles.settingInfo}>
              <div className={styles.settingLabel}>{t("appearance_dark_theme")}</div>
              <div className={styles.settingDescription}>
                {t("appearance_color_theme_desc")}
              </div>
            </div>
            <div className={styles.settingControl}>
              <select
                className={styles.select}
                value={themeDark}
                onChange={(e) => handleDarkThemeChange(e.target.value)}
              >
                {availableThemes.map((theme) => (
                  <option key={theme.id} value={theme.id}>
                    {theme.name}
                  </option>
                ))}
              </select>
            </div>
          </div>
          <div className={styles.settingRow}>
            <div className={styles.settingInfo}>
              <div className={styles.settingLabel}>{t("appearance_light_theme")}</div>
            </div>
            <div className={styles.settingControl}>
              <select
                className={styles.select}
                value={themeLight}
                onChange={(e) => handleLightThemeChange(e.target.value)}
              >
                {availableThemes.map((theme) => (
                  <option key={theme.id} value={theme.id}>
                    {theme.name}
                  </option>
                ))}
              </select>
            </div>
          </div>
        </>
      )}

      <div className={styles.settingRow}>
        <div className={styles.settingInfo}>
          <div className={styles.settingLabel}>{t("appearance_ui_font_size")}</div>
          <div className={styles.settingDescription}>
            {t(isMac ? "appearance_ui_font_size_desc_mac" : "appearance_ui_font_size_desc_other", {
              min: UI_FONT_SIZE_MIN,
              max: UI_FONT_SIZE_MAX,
              default: UI_FONT_SIZE_DEFAULT,
            })}
          </div>
        </div>
        <div className={styles.settingControl}>
          <input
            className={styles.numberInput}
            type="number"
            min={UI_FONT_SIZE_MIN}
            max={UI_FONT_SIZE_MAX}
            value={uiFontSizeStr}
            onChange={(e) => setUiFontSizeStr(e.target.value)}
            onBlur={handleUiFontSizeBlur}
          />
        </div>
      </div>

      <div className={styles.settingRow}>
        <div className={styles.settingInfo}>
          <div className={styles.settingLabel}>{t("appearance_terminal_font_size")}</div>
          <div className={styles.settingDescription}>{t("appearance_terminal_font_size_desc")}</div>
        </div>
        <div className={styles.settingControl}>
          <input
            className={styles.numberInput}
            type="number"
            min={TERMINAL_FONT_SIZE_MIN}
            max={TERMINAL_FONT_SIZE_MAX}
            value={termFontSize}
            onChange={(e) => setTermFontSize(e.target.value)}
            onBlur={handleTermFontSizeBlur}
          />
        </div>
      </div>

      <div className={styles.settingRow}>
        <div className={styles.settingInfo}>
          <div className={styles.settingLabel}>{t("appearance_group_tool_calls")}</div>
          <div className={styles.settingDescription}>
            {t("appearance_group_tool_calls_desc")}
          </div>
        </div>
        <div className={styles.settingControl}>
          <button
            className={styles.toggle}
            role="switch"
            aria-checked={toolDisplayMode === "grouped"}
            aria-label={t("appearance_group_tool_calls")}
            data-checked={toolDisplayMode === "grouped"}
            onClick={async () => {
              const previous = toolDisplayMode;
              const next = previous === "grouped" ? "inline" : "grouped";
              setToolDisplayMode(next);
              try {
                setError(null);
                await setAppSetting("tool_display_mode", next);
              } catch (e) {
                setToolDisplayMode(previous);
                setError(String(e));
              }
            }}
          >
            <div className={styles.toggleKnob} />
          </button>
        </div>
      </div>

      <div className={styles.settingRow}>
        <div className={styles.settingInfo}>
          <div className={styles.settingLabel}>{t("appearance_extended_tool_call_output")}</div>
          <div className={styles.settingDescription}>
            {t("appearance_extended_tool_call_output_desc")}
          </div>
        </div>
        <div className={styles.settingControl}>
          <button
            className={styles.toggle}
            role="switch"
            aria-checked={extendedToolCallOutput}
            aria-label={t("appearance_extended_tool_call_output")}
            data-checked={extendedToolCallOutput}
            onClick={async () => {
              const next = !extendedToolCallOutput;
              setExtendedToolCallOutput(next);
              try {
                setError(null);
                await setAppSetting("extended_tool_call_output", next ? "true" : "false");
              } catch (e) {
                setExtendedToolCallOutput(!next);
                setError(String(e));
              }
            }}
          >
            <div className={styles.toggleKnob} />
          </button>
        </div>
      </div>

      <div className={styles.settingRow}>
        <div className={styles.settingInfo}>
          <div className={styles.settingLabel}>{t("appearance_show_running_commands")}</div>
          <div className={styles.settingDescription}>
            {t("appearance_show_running_commands_desc")}
          </div>
        </div>
        <div className={styles.settingControl}>
          <button
            className={styles.toggle}
            role="switch"
            aria-checked={showSidebarRunningCommands}
            aria-label={t("appearance_show_running_commands")}
            data-checked={showSidebarRunningCommands}
            onClick={async () => {
              const next = !showSidebarRunningCommands;
              setShowSidebarRunningCommands(next);
              try {
                setError(null);
                await setAppSetting("show_sidebar_running_commands", next ? "true" : "false");
              } catch (e) {
                setShowSidebarRunningCommands(!next);
                setError(String(e));
              }
            }}
          >
            <div className={styles.toggleKnob} />
          </button>
        </div>
      </div>

      <div className={styles.settingRow}>
        <div className={styles.settingInfo}>
          <div className={styles.settingLabel}>{t("appearance_interface_font")}</div>
          <div className={styles.settingDescription}>
            {t("appearance_interface_font_desc")}
          </div>
        </div>
        <div className={styles.settingControl}>
          <FontSelect
            options={sansFontOptions}
            value={showCustomSans ? customSans : fontFamilySans}
            isCustom={showCustomSans}
            onChange={handleSansChange}
          />
        </div>
      </div>
      {showCustomSans && (
        <div className={styles.settingRow}>
          <div className={styles.settingInfo}>
            <div className={styles.settingLabel}>{t("appearance_custom_interface_font")}</div>
            <div className={styles.settingDescription}>
              {t("appearance_custom_interface_font_desc")}
            </div>
          </div>
          <div className={styles.settingControl}>
            <input
              className={styles.input}
              type="text"
              placeholder={t("appearance_interface_font_placeholder")}
              value={customSans}
              onChange={(e) => setCustomSans(e.target.value)}
              onBlur={handleCustomSansBlur}
            />
          </div>
        </div>
      )}

      <div className={styles.settingRow}>
        <div className={styles.settingInfo}>
          <div className={styles.settingLabel}>{t("appearance_monospace_font")}</div>
          <div className={styles.settingDescription}>
            {t("appearance_monospace_font_desc")}
          </div>
        </div>
        <div className={styles.settingControl}>
          <FontSelect
            options={monoFontOptions}
            value={showCustomMono ? customMono : fontFamilyMono}
            isCustom={showCustomMono}
            onChange={handleMonoChange}
            kind="mono"
          />
        </div>
      </div>
      {showCustomMono && (
        <div className={styles.settingRow}>
          <div className={styles.settingInfo}>
            <div className={styles.settingLabel}>{t("appearance_custom_monospace_font")}</div>
            <div className={styles.settingDescription}>
              {t("appearance_custom_monospace_font_desc")}
            </div>
          </div>
          <div className={styles.settingControl}>
            <input
              className={styles.input}
              type="text"
              placeholder={t("appearance_monospace_font_placeholder")}
              value={customMono}
              onChange={(e) => setCustomMono(e.target.value)}
              onBlur={handleCustomMonoBlur}
            />
          </div>
        </div>
      )}
    </div>
  );
}
