import { useEffect, useMemo, useRef, useState } from "react";
import { X, Search, Check } from "lucide-react";
import { COLORS, FONT, ACCENTS, BACKGROUNDS, CUSTOM_BACKGROUND } from "./theme";
import { setConfig, restartDaemon, listFonts } from "./socketBridge";
import type { ConfigView, DaemonHealth } from "./socketBridge";
import { HOTKEYS, defaultBindings, eventBinding, formatBinding, hasModifier } from "./hotkeys";
import type { Bindings, HotkeyId } from "./hotkeys";

// Pinned defaults shown first; the rest are the user's installed families (list_fonts).
const DEFAULT_FONTS = ["JetBrains Mono", "Menlo", "Monaco", "SF Mono", "Fira Code", "Cascadia Code"];

/** Searchable font picker: type to filter, click to apply. Defaults pinned on top. */
function FontPicker({ value, onPick }: { value: string; onPick: (family: string) => void }) {
  const [installed, setInstalled] = useState<string[]>([]);
  const [query, setQuery] = useState("");
  const [open, setOpen] = useState(false);
  const boxRef = useRef<HTMLDivElement>(null);

  useEffect(() => { void listFonts().then(setInstalled).catch(() => {}); }, []);

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

  const options = useMemo(() => {
    const seen = new Set<string>();
    const merged: string[] = [];
    for (const f of [...DEFAULT_FONTS, ...installed]) {
      const k = f.toLowerCase();
      if (!seen.has(k)) { seen.add(k); merged.push(f); }
    }
    const q = query.trim().toLowerCase();
    return q ? merged.filter((f) => f.toLowerCase().includes(q)) : merged;
  }, [installed, query]);

  return (
    <div ref={boxRef} style={{ position: "relative", marginBottom: 10 }}>
      <div style={{ position: "relative" }}>
        <Search size={14} style={{ position: "absolute", left: 9, top: "50%", transform: "translateY(-50%)", color: COLORS.textMuted }} />
        <input
          value={open ? query : value}
          placeholder={value}
          onFocus={() => { setOpen(true); setQuery(""); }}
          onChange={(e) => { setQuery(e.target.value); setOpen(true); }}
          style={{ width: "100%", padding: "8px 8px 8px 30px", background: COLORS.bgPanel, color: COLORS.textPrimary, border: `1px solid ${open ? COLORS.accent : COLORS.borderStrong}`, borderRadius: 8, fontFamily: FONT.ui }}
        />
      </div>
      {open && (
        <div style={{ position: "absolute", top: "calc(100% + 4px)", left: 0, right: 0, zIndex: 10, maxHeight: 240, overflowY: "auto",
          background: COLORS.bgPanel, border: `1px solid ${COLORS.borderStrong}`, borderRadius: 8, boxShadow: "0 8px 24px rgba(0,0,0,0.4)" }}>
          {options.length === 0 && <div style={{ padding: 10, fontSize: 12, color: COLORS.textMuted }}>Ничего не найдено</div>}
          {options.map((f) => {
            const isDefault = DEFAULT_FONTS.some((d) => d.toLowerCase() === f.toLowerCase());
            return (
              <button key={f} onClick={() => { onPick(f); setOpen(false); }}
                style={{ display: "flex", alignItems: "center", gap: 8, width: "100%", textAlign: "left", padding: "7px 10px",
                  background: f === value ? COLORS.bgElevated : "transparent", border: "none", color: COLORS.textPrimary,
                  fontFamily: `'${f}', ${FONT.mono}`, fontSize: 13 }}>
                <Check size={13} style={{ opacity: f === value ? 1 : 0, color: COLORS.accent, flex: "0 0 auto" }} />
                <span style={{ flex: 1, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{f}</span>
                {isDefault && <span style={{ fontFamily: FONT.ui, fontSize: 10, color: COLORS.textMuted }}>default</span>}
              </button>
            );
          })}
        </div>
      )}
    </div>
  );
}

export function Settings({ config, health, bindings, onBindingsChange, onClose, onReload }: { config: ConfigView; health: DaemonHealth | null; bindings: Bindings; onBindingsChange: (b: Bindings) => void; onClose: () => void; onReload: () => void }) {
  const ref = useRef<HTMLDivElement>(null);
  useEffect(() => { ref.current?.focus(); }, []);

  // Fix 2: local state for font-size slider — committed only on pointer release.
  const [sizeLocal, setSizeLocal] = useState(config.font_size);
  useEffect(() => { setSizeLocal(config.font_size); }, [config.font_size]);

  // Fix 3: controlled shell input — synced from config, committed on blur.
  const [shellLocal, setShellLocal] = useState(config.default_shell);
  useEffect(() => { setShellLocal(config.default_shell); }, [config.default_shell]);

  // Custom background image path — committed on blur (switches background to "custom").
  const [bgPathLocal, setBgPathLocal] = useState(config.background_image);
  useEffect(() => { setBgPathLocal(config.background_image); }, [config.background_image]);
  return (
    <div onMouseDown={onClose} style={{ position: "fixed", inset: 0, zIndex: 2000, background: "#000A", display: "flex", alignItems: "center", justifyContent: "center" }}>
      <div ref={ref} tabIndex={-1} onMouseDown={(e) => e.stopPropagation()} onKeyDown={(e) => { e.stopPropagation(); if (e.key === "Escape") onClose(); }}
        style={{ width: 520, maxHeight: "80vh", overflowY: "auto", background: COLORS.bgApp, border: `1px solid ${COLORS.borderStrong}`, borderRadius: 14, padding: 24, color: COLORS.textPrimary, fontFamily: FONT.ui }}>
        <div style={{ display: "flex", alignItems: "center", marginBottom: 16 }}>
          <span style={{ fontWeight: 700, fontSize: 16, flex: 1 }}>Settings</span>
          <button onClick={onClose} aria-label="Close" title="Close (Esc)"
            style={{ display: "flex", alignItems: "center", justifyContent: "center", width: 26, height: 26, borderRadius: 6, background: "transparent", border: "none", color: COLORS.textMuted, cursor: "pointer" }}>
            <X size={16} />
          </button>
        </div>

        <div style={{ fontSize: 12, color: COLORS.textSecondary, marginBottom: 6 }}>Terminal font</div>
        <FontPicker value={config.font_family} onPick={(f) => void setConfig({ font_family: f })} />
        <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 18 }}>
          <span style={{ fontSize: 12, color: COLORS.textSecondary }}>Size {sizeLocal}</span>
          <input type="range" min={10} max={20} value={sizeLocal}
            onChange={(e) => setSizeLocal(Number(e.target.value))}
            onPointerUp={() => void setConfig({ font_size: sizeLocal })}
            style={{ flex: 1 }} />
        </div>

        <div style={{ fontSize: 12, color: COLORS.textSecondary, marginBottom: 6 }}>Theme</div>
        <div style={{ display: "flex", gap: 8, marginBottom: 12 }}>
          {(["dark", "light"] as const).map((t) => (
            <button key={t} onClick={() => void setConfig({ theme: t })}
              style={{ flex: 1, padding: "8px 0", borderRadius: 8, fontSize: 13, textTransform: "capitalize",
                background: config.theme === t ? COLORS.accent : COLORS.bgElevated, color: config.theme === t ? "#fff" : COLORS.textPrimary,
                border: `1px solid ${COLORS.borderStrong}` }}>{t}</button>
          ))}
        </div>
        <div style={{ fontSize: 12, color: COLORS.textSecondary, marginBottom: 6 }}>Accent</div>
        <div style={{ display: "flex", gap: 10, marginBottom: 18 }}>
          {Object.entries(ACCENTS).map(([id, hex]) => (
            <button key={id} onClick={() => void setConfig({ accent: id })} aria-label={id}
              style={{ width: 26, height: 26, borderRadius: "50%", background: hex, cursor: "pointer",
                border: config.accent === id ? `2px solid ${COLORS.textPrimary}` : "2px solid transparent" }} />
          ))}
        </div>

        <div style={{ fontSize: 12, color: COLORS.textSecondary, marginBottom: 6 }}>Background</div>
        <div style={{ display: "grid", gridTemplateColumns: "repeat(4, 1fr)", gap: 8, marginBottom: 10 }}>
          {Object.entries(BACKGROUNDS).map(([id, bg]) => {
            const selected = config.background === id;
            return (
              <button key={id} onClick={() => void setConfig({ background: id })} aria-label={bg.label} title={bg.label}
                style={{ display: "flex", flexDirection: "column", gap: 4, padding: 0, cursor: "pointer", background: "transparent", border: "none" }}>
                <div style={{ height: 40, borderRadius: 6, background: bg.swatch,
                  border: selected ? `2px solid ${COLORS.accent}` : `2px solid ${COLORS.borderSubtle}`, boxSizing: "border-box" }} />
                <span style={{ fontSize: 10, color: selected ? COLORS.textPrimary : COLORS.textMuted, textAlign: "center", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{bg.label}</span>
              </button>
            );
          })}
          <button onClick={() => { if (bgPathLocal) void setConfig({ background: CUSTOM_BACKGROUND, background_image: bgPathLocal }); }}
            aria-label="Custom image" title="Custom image"
            style={{ display: "flex", flexDirection: "column", gap: 4, padding: 0, cursor: "pointer", background: "transparent", border: "none" }}>
            <div style={{ height: 40, borderRadius: 6, display: "flex", alignItems: "center", justifyContent: "center", color: COLORS.textMuted, fontSize: 10, background: COLORS.bgPanel,
              border: config.background === CUSTOM_BACKGROUND ? `2px solid ${COLORS.accent}` : `2px solid ${COLORS.borderSubtle}`, boxSizing: "border-box" }}>Image</div>
            <span style={{ fontSize: 10, color: config.background === CUSTOM_BACKGROUND ? COLORS.textPrimary : COLORS.textMuted, textAlign: "center" }}>Custom</span>
          </button>
        </div>
        <input value={bgPathLocal} onChange={(e) => setBgPathLocal(e.target.value)}
          onBlur={() => { if (bgPathLocal) void setConfig({ background: CUSTOM_BACKGROUND, background_image: bgPathLocal }); }}
          placeholder="/path/to/wallpaper.png — image for the Custom theme"
          style={{ width: "100%", padding: 8, marginBottom: 18, background: COLORS.bgPanel, color: COLORS.textPrimary, border: `1px solid ${COLORS.borderStrong}`, borderRadius: 8, fontSize: 12 }} />

        <div style={{ fontSize: 12, color: COLORS.textSecondary, marginBottom: 6 }}>Default shell (empty = auto)</div>
        <input value={shellLocal} onChange={(e) => setShellLocal(e.target.value)} onBlur={() => void setConfig({ default_shell: shellLocal })}
          style={{ width: "100%", padding: 8, marginBottom: 18, background: COLORS.bgPanel, color: COLORS.textPrimary, border: `1px solid ${COLORS.borderStrong}`, borderRadius: 8 }} />

        <div style={{ fontSize: 12, color: COLORS.textSecondary, marginBottom: 6 }}>Events</div>
        <label style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 18, cursor: "pointer" }}>
          <button role="switch" aria-checked={config.log_shell_commands} onClick={() => void setConfig({ log_shell_commands: !config.log_shell_commands })}
            style={{ position: "relative", width: 38, height: 22, flex: "0 0 auto", borderRadius: 11, border: "none", cursor: "pointer",
              background: config.log_shell_commands ? COLORS.accent : COLORS.bgElevated, transition: "background 0.15s" }}>
            <span style={{ position: "absolute", top: 2, left: config.log_shell_commands ? 18 : 2, width: 18, height: 18, borderRadius: "50%", background: "#fff", transition: "left 0.15s" }} />
          </button>
          <span style={{ fontSize: 13, color: COLORS.textPrimary }}>
            Log shell commands
            <span style={{ display: "block", fontSize: 11, color: COLORS.textMuted }}>Off: only agent activity is logged & notified. Status rings still update.</span>
          </span>
        </label>

        <HotkeysSection bindings={bindings} onChange={onBindingsChange} />

        <DaemonSection health={health} onReload={onReload} />
      </div>
    </div>
  );
}

/** Hotkeys list with click-to-record rebinding. */
function HotkeysSection({ bindings, onChange }: { bindings: Bindings; onChange: (b: Bindings) => void }) {
  const [recordingId, setRecordingId] = useState<HotkeyId | null>(null);

  useEffect(() => {
    if (!recordingId) return;
    const onKey = (e: KeyboardEvent) => {
      e.preventDefault();
      e.stopPropagation();
      if (e.key === "Escape") { setRecordingId(null); return; }
      const b = eventBinding(e);
      if (!b || !hasModifier(b)) return; // wait for a real chord with a modifier
      onChange({ ...bindings, [recordingId]: b });
      setRecordingId(null);
    };
    // Capture phase so we intercept before the modal's own key handling.
    window.addEventListener("keydown", onKey, true);
    return () => window.removeEventListener("keydown", onKey, true);
  }, [recordingId, bindings, onChange]);

  const groups = ["Workspace", "Panel"] as const;
  return (
    <div style={{ marginTop: 8, paddingTop: 16, borderTop: `1px solid ${COLORS.borderSubtle}` }}>
      <div style={{ display: "flex", alignItems: "center", marginBottom: 6 }}>
        <span style={{ fontSize: 12, color: COLORS.textSecondary, flex: 1 }}>Hotkeys</span>
        <button onClick={() => onChange(defaultBindings())}
          style={{ fontSize: 11, color: COLORS.textMuted, background: "transparent", border: `1px solid ${COLORS.borderStrong}`, borderRadius: 6, padding: "3px 8px", cursor: "pointer" }}>
          Reset
        </button>
      </div>
      {groups.map((g) => (
        <div key={g} style={{ marginBottom: 6 }}>
          <div style={{ fontSize: 10, textTransform: "uppercase", letterSpacing: "0.05em", color: COLORS.textMuted, margin: "6px 0 2px" }}>{g}</div>
          {HOTKEYS.filter((h) => h.group === g).map((h) => (
            <div key={h.id} style={{ display: "flex", alignItems: "center", height: 28 }}>
              <span style={{ flex: 1, fontSize: 13, color: COLORS.textPrimary }}>{h.label}</span>
              <button onClick={() => setRecordingId(h.id)}
                style={{ minWidth: 70, fontFamily: FONT.mono, fontSize: 12, padding: "3px 10px", borderRadius: 6, cursor: "pointer",
                  background: recordingId === h.id ? COLORS.accent : COLORS.bgPanel,
                  color: recordingId === h.id ? COLORS.bgApp : COLORS.textPrimary,
                  border: `1px solid ${recordingId === h.id ? COLORS.accent : COLORS.borderStrong}` }}>
                {recordingId === h.id ? "Press keys…" : formatBinding(bindings[h.id])}
              </button>
            </div>
          ))}
        </div>
      ))}
    </div>
  );
}

function fmtUptime(ms: number): string {
  const s = Math.max(0, Math.floor((Date.now() - ms) / 1000));
  if (s < 60) return `${s}s`;
  if (s < 3600) return `${Math.floor(s / 60)}m`;
  return `${Math.floor(s / 3600)}h ${Math.floor((s % 3600) / 60)}m`;
}

function DaemonSection({ health, onReload }: { health: DaemonHealth | null; onReload: () => void }) {
  const [confirm, setConfirm] = useState(false);
  // Tick so uptime counts up live while the modal is open.
  const [, setTick] = useState(0);
  useEffect(() => {
    const t = setInterval(() => setTick((n) => n + 1), 1000);
    return () => clearInterval(t);
  }, []);
  return (
    <div style={{ marginTop: 8, paddingTop: 16, borderTop: `1px solid ${COLORS.borderSubtle}` }}>
      <div style={{ fontSize: 12, color: COLORS.textSecondary, marginBottom: 8 }}>Daemon</div>
      <div style={{ fontFamily: FONT.mono, fontSize: 12, color: COLORS.textSecondary, lineHeight: 1.7 }}>
        {health ? (<>
          <div>version {health.version}{health.build ? ` · ${health.build}` : ""} · pid {health.pid}</div>
          <div>uptime {fmtUptime(health.started_at_ms)}</div>
        </>) : <div>offline</div>}
      </div>
      <div style={{ display: "flex", gap: 8, marginTop: 12 }}>
        <button onClick={() => setConfirm(true)} style={{ padding: "7px 14px", background: COLORS.bgElevated, color: COLORS.textPrimary, border: `1px solid ${COLORS.borderStrong}`, borderRadius: 7, fontSize: 13 }}>Restart</button>
      </div>
      {confirm && (
        <div style={{ marginTop: 10, padding: 10, borderRadius: 8, background: COLORS.bgPanel, border: `1px solid ${COLORS.borderStrong}` }}>
          <div style={{ fontSize: 12, color: COLORS.textSecondary, marginBottom: 8 }}>
            Restart the daemon? Running sessions end and respawn; panels re-attach automatically.
          </div>
          <div style={{ display: "flex", gap: 8, justifyContent: "flex-end" }}>
            <button onClick={() => setConfirm(false)} style={{ padding: "5px 12px", background: COLORS.bgElevated, color: COLORS.textPrimary, border: `1px solid ${COLORS.borderStrong}`, borderRadius: 6, fontSize: 12 }}>Cancel</button>
            <button onClick={() => { setConfirm(false); void restartDaemon().then(onReload); }}
              style={{ padding: "5px 12px", background: COLORS.stError, color: "#fff", border: "none", borderRadius: 6, fontSize: 12, fontWeight: 600 }}>
              Restart
            </button>
          </div>
        </div>
      )}
    </div>
  );
}
