import { useState, useRef, useEffect, useMemo, type CSSProperties, type ReactNode } from "react";
import { useTranslation } from "react-i18next";
import type { FontOption } from "../../utils/fontSettings";
import { useSettingsOverlay } from "../../hooks/useSettingsOverlay";
import styles from "./Settings.module.css";

interface FontSelectProps {
  options: FontOption[];
  value: string;
  onChange: (value: string) => void;
  /** If true, the current value is a custom font not in the options list. */
  isCustom?: boolean;
  /** "sans" or "mono" — determines the fallback stack for previews. */
  kind?: "sans" | "mono";
}

// Inline styles can't use var() — must use real font names as fallbacks.
const SANS_FALLBACK = "Inter, -apple-system, BlinkMacSystemFont, sans-serif";
const MONO_FALLBACK = '"JetBrains Mono", ui-monospace, "SF Mono", monospace';

/**
 * Custom dropdown that renders each font option in its own typeface.
 * Replaces the native <select> for font pickers so users can preview fonts.
 */
export function FontSelect({ options, value, onChange, isCustom, kind = "sans" }: FontSelectProps) {
  const { t } = useTranslation("settings");
  const [open, setOpen] = useState(false);
  const [search, setSearch] = useState("");
  const ref = useRef<HTMLDivElement>(null);
  const listId = useRef(`font-list-${Math.random().toString(36).slice(2, 8)}`).current;
  const fallback = kind === "mono" ? MONO_FALLBACK : SANS_FALLBACK;

  useSettingsOverlay(open);

  // Close on outside click
  useEffect(() => {
    if (!open) return;
    const handler = (e: MouseEvent) => {
      if (ref.current && !ref.current.contains(e.target as Node)) {
        setOpen(false);
      }
    };
    document.addEventListener("mousedown", handler);
    return () => document.removeEventListener("mousedown", handler);
  }, [open]);

  // Close on Escape
  useEffect(() => {
    if (!open) return;
    const handler = (e: KeyboardEvent) => {
      if (e.key === "Escape") {
        e.stopPropagation();
        setOpen(false);
      }
    };
    document.addEventListener("keydown", handler);
    return () => document.removeEventListener("keydown", handler);
  }, [open]);

  const filteredOptions = useMemo(() => {
    const q = search.trim().toLowerCase();
    if (!q) return options;
    return options.filter((opt) => {
      if (opt.value === "" || opt.value === "__custom__") return false;
      return opt.label.toLowerCase().includes(q);
    });
  }, [options, search]);

  const selectedLabel = isCustom
    ? value || t("font_select_custom")
    : (options.find((o) => o.value === value)?.label ?? t("font_select_default"));

  // Every option gets an explicit fontFamily so it doesn't inherit the
  // current --font-sans (which may be a decorative font like Zapfino).
  const fontStyle = (v: string): CSSProperties => {
    if (v && v !== "__custom__") return { fontFamily: `"${v}", ${fallback}` };
    return { fontFamily: fallback };
  };

  const renderOptions = (): ReactNode[] => {
    const elements: ReactNode[] = [];
    let lastGroup: string | undefined;

    filteredOptions.forEach((opt, i) => {
      if (opt.group && opt.group !== lastGroup) {
        elements.push(
          <div
            key={`group-${opt.group}`}
            className={styles.fontPickerGroupLabel}
            role="presentation"
          >
            {opt.group === "sans" ? t("font_select_group_sans") : t("font_select_group_mono")}
          </div>,
        );
        lastGroup = opt.group;
      }

      const isSelected = !isCustom && opt.value === value;
      const cls = isSelected
        ? styles.fontPickerOptionSelected
        : styles.fontPickerOption;

      elements.push(
        <button
          key={`opt-${i}-${opt.value}`}
          type="button"
          role="option"
          aria-selected={isSelected}
          className={cls}
          style={fontStyle(opt.value)}
          onClick={() => {
            onChange(opt.value);
            if (opt.value !== "__custom__") setOpen(false);
          }}
        >
          {opt.label}
        </button>,
      );
    });

    return elements;
  };

  return (
    <div className={styles.fontPicker} ref={ref}>
      <button
        type="button"
        className={styles.fontPickerButton}
        style={fontStyle(isCustom ? "" : value)}
        onClick={() => {
          setOpen((prev) => {
            if (!prev) setSearch("");
            return !prev;
          });
        }}
        aria-haspopup="listbox"
        aria-expanded={open}
        aria-controls={open ? listId : undefined}
      >
        {selectedLabel}
      </button>
      {open && (
        <div className={styles.fontPickerDropdown}>
          <input
            className={styles.fontPickerSearch}
            type="text"
            placeholder={t("font_select_search_placeholder")}
            value={search}
            onChange={(e) => setSearch(e.target.value)}
            autoFocus
          />
          {filteredOptions.length === 0 && search.trim() ? (
            <div className={styles.fontPickerNoResults}>{t("font_select_no_matches")}</div>
          ) : (
            <div role="listbox" id={listId} aria-label={t("font_select_listbox_aria")}>
              {renderOptions()}
            </div>
          )}
        </div>
      )}
    </div>
  );
}
