import { useEffect, useMemo, useState } from 'react';
import {
  ArrowDown, ArrowUp, Copy, Plus, Star, StarOff, Trash2,
  ChevronDown, ChevronRight,
  User, Database, Radio, Cog, Server, Award, Antenna as AntennaIcon,
  Compass, Wifi, Construction,
} from 'lucide-react';
import {
  GetLookupSettings, SaveLookupSettings, ClearLookupCache, TestLookupProvider,
  GetListsSettings, SaveListsSettings,
  GetCATSettings, SaveCATSettings,
  ListProfiles, GetActiveProfile, SaveProfile, DeleteProfile, ActivateProfile, DuplicateProfile,
} from '../../wailsjs/go/main/App';
import type { profile as profileModels } from '../../wailsjs/go/models';
import type { LookupSettingsForm, StationSettingsForm, ListsSettingsForm, ModePresetForm } from '@/types';
import type { main as mainModels } from '../../wailsjs/go/models';

import {
  Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, DialogDescription,
} from '@/components/ui/dialog';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Textarea } from '@/components/ui/textarea';
import { Checkbox } from '@/components/ui/checkbox';
import {
  Select, SelectTrigger, SelectValue, SelectContent, SelectItem,
} from '@/components/ui/select';
import { cn } from '@/lib/utils';

type LookupSettings = LookupSettingsForm;
type StationSettings = StationSettingsForm;
type ListsSettings = ListsSettingsForm;
type ModePreset = ModePresetForm;
type CATSettings = Omit<mainModels.CATSettings, 'convertValues'>;
type Profile = Omit<profileModels.Profile, 'convertValues'>;

const emptyProfile = (): Profile => ({
  id: 0,
  name: '',
  callsign: '', operator: '',
  my_grid: '', my_country: '',
  my_state: '', my_cnty: '',
  my_street: '', my_city: '', my_postal_code: '',
  my_sota_ref: '', my_pota_ref: '',
  my_rig: '', my_antenna: '',
  tx_pwr: undefined,
  is_active: false,
  sort_order: 0,
  created_at: '' as any,
  updated_at: '' as any,
});

interface Props {
  initialSection?: string;
  onClose: () => void;
  onSaved: () => void;
}

/* ====== Tree definition ======
   Section IDs are stable strings — adding new ones means adding a panel below.
   `disabled: true` greys them out and shows the "coming soon" placeholder. */
type SectionId =
  | 'station'
  | 'profiles'
  | 'lookup'
  | 'lists-bands'
  | 'lists-modes'
  | 'cluster'
  | 'backup'
  | 'awards'
  | 'cat'
  | 'rotator'
  | 'antenna'
  | 'audio';

type TreeNode =
  | { kind: 'group'; label: string; icon?: any; defaultOpen?: boolean; children: TreeNode[] }
  | { kind: 'item';  label: string; id: SectionId; disabled?: boolean };

const TREE: TreeNode[] = [
  {
    kind: 'group', label: 'User Configuration', icon: User, defaultOpen: true, children: [
      { kind: 'item', label: 'Station Information', id: 'station' },
      { kind: 'item', label: 'Profiles (portable, home, contest)', id: 'profiles' },
    ],
  },
  {
    kind: 'group', label: 'Software Configuration', icon: Cog, defaultOpen: true, children: [
      { kind: 'item', label: 'Callsign Lookup', id: 'lookup' },
      { kind: 'group', label: 'Lists', icon: Database, defaultOpen: true, children: [
        { kind: 'item', label: 'Bands',      id: 'lists-bands' },
        { kind: 'item', label: 'Modes & default RST', id: 'lists-modes' },
      ]},
      { kind: 'item', label: 'DX Cluster',     id: 'cluster',  disabled: true },
      { kind: 'item', label: 'Backup / Export', id: 'backup',   disabled: true },
      { kind: 'item', label: 'Awards',          id: 'awards',   disabled: true },
    ],
  },
  {
    kind: 'group', label: 'Hardware Configuration', icon: Server, children: [
      { kind: 'item', label: 'CAT interface (OmniRig)',         id: 'cat' },
      { kind: 'item', label: 'Rotator (PstRotator)',            id: 'rotator', disabled: true },
      { kind: 'item', label: 'Antenna (Ultrabeam)',             id: 'antenna', disabled: true },
      { kind: 'item', label: 'Audio devices',                   id: 'audio',   disabled: true },
    ],
  },
];

// Map section id → friendly name (used in breadcrumb / placeholders).
const SECTION_LABELS: Partial<Record<SectionId, string>> = {
  station: 'Station Information',
  profiles: 'Profiles',
  lookup: 'Callsign Lookup',
  'lists-bands': 'Bands',
  'lists-modes': 'Modes & default RST',
  cluster: 'DX Cluster',
  backup: 'Backup / Export',
  awards: 'Awards',
  cat: 'CAT interface',
  rotator: 'Rotator',
  antenna: 'Antenna',
  audio: 'Audio devices',
};

// ===== Tree component =====

interface TreeProps {
  selected: SectionId;
  onSelect: (id: SectionId) => void;
}

function Tree({ selected, onSelect }: TreeProps) {
  return (
    <nav className="text-sm">
      {TREE.map((node, i) => (
        <TreeNodeView key={i} node={node} depth={0} selected={selected} onSelect={onSelect} />
      ))}
    </nav>
  );
}

function TreeNodeView({
  node, depth, selected, onSelect,
}: { node: TreeNode; depth: number; selected: SectionId; onSelect: (id: SectionId) => void }) {
  if (node.kind === 'item') {
    const isActive = selected === node.id;
    return (
      <button
        onClick={() => { if (!node.disabled) onSelect(node.id); }}
        disabled={node.disabled}
        className={cn(
          'w-full text-left px-2 py-1.5 rounded-md text-[12.5px] transition-colors flex items-center',
          isActive ? 'bg-accent text-accent-foreground font-semibold' : 'hover:bg-muted/60',
          node.disabled && 'opacity-50 cursor-not-allowed italic',
        )}
        style={{ paddingLeft: 8 + depth * 14 }}
      >
        <span className="truncate">{node.label}</span>
        {node.disabled && (
          <Construction className="ml-auto size-3 shrink-0 opacity-60" />
        )}
      </button>
    );
  }
  // group
  const [open, setOpen] = useState(node.defaultOpen ?? false);
  const Icon = node.icon;
  return (
    <div>
      <button
        onClick={() => setOpen((v) => !v)}
        className="w-full text-left px-2 py-1.5 rounded-md text-[12px] uppercase tracking-wider text-muted-foreground hover:text-foreground flex items-center gap-1.5 font-semibold"
        style={{ paddingLeft: 8 + depth * 14 }}
      >
        {open ? <ChevronDown className="size-3" /> : <ChevronRight className="size-3" />}
        {Icon && <Icon className="size-3.5 opacity-70" />}
        <span>{node.label}</span>
      </button>
      {open && (
        <div>
          {node.children.map((c, i) => (
            <TreeNodeView key={i} node={c} depth={depth + 1} selected={selected} onSelect={onSelect} />
          ))}
        </div>
      )}
    </div>
  );
}

// ===== Section content panels =====

function SectionHeader({ title, hint }: { title: string; hint?: string }) {
  return (
    <header className="mb-4">
      <h3 className="text-base font-semibold text-foreground">{title}</h3>
      {hint && <p className="text-xs text-muted-foreground mt-0.5">{hint}</p>}
    </header>
  );
}

function ComingSoon({ id, icon: Icon }: { id: SectionId; icon?: any }) {
  const label = SECTION_LABELS[id] ?? id;
  const IconCmp = Icon ?? Construction;
  return (
    <div className="flex flex-col items-center justify-center text-center text-muted-foreground gap-2 py-12">
      <IconCmp className="size-10 opacity-40" />
      <div className="text-base font-semibold text-foreground/70">{label}</div>
      <div className="text-sm">Module coming soon.</div>
    </div>
  );
}

export function SettingsModal({ onClose, onSaved, initialSection }: Props) {
  const [selected, setSelected] = useState<SectionId>((initialSection as SectionId) || 'station');
  const [loading, setLoading] = useState(true);
  const [saving, setSaving] = useState(false);
  const [clearing, setClearing] = useState(false);
  const [msg, setMsg] = useState('');
  const [err, setErr] = useState('');

  const [lookup, setLookup] = useState<LookupSettings>({
    qrz_user: '', qrz_password: '',
    hamqth_user: '', hamqth_password: '',
    primary: '', failsafe: '',
    cache_ttl_days: 30,
  });
  // Per-provider Test state — keeps the success/error feedback adjacent
  // to the button. Cleared on the next test run for that provider.
  type TestResult = { ok: boolean; msg: string };
  const [lookupTest, setLookupTest] = useState<Record<string, TestResult | undefined>>({});
  const [lookupTesting, setLookupTesting] = useState<Record<string, boolean>>({});
  // The Station Information panel now edits the full active profile
  // (not a flat 6-field StationSettings). Profile selection happens in
  // the Profiles panel; any edit here saves back to whichever profile
  // is currently active.
  const [activeProfile, setActiveProfile] = useState<Profile | null>(null);
  const updateActive = (patch: Partial<Profile>) =>
    setActiveProfile((p) => (p ? { ...p, ...patch } : p));
  const [lists, setLists] = useState<ListsSettings>({ bands: [], modes: [] });
  const [bandsText, setBandsText] = useState('');
  const [catCfg, setCatCfg] = useState<CATSettings>({
    enabled: false, backend: 'omnirig', omnirig_rig: 1, poll_ms: 250, delay_ms: 0,
    digital_default: 'FT8',
  });
  const [profiles, setProfiles] = useState<Profile[]>([]);
  // State for ProfilesPanel — lifted here because PANELS[selected]() calls
  // the panel as a plain function, not as a JSX element, so any useState
  // inside the panel function would violate the Rules of Hooks.
  const [profileSelectedId, setProfileSelectedId] = useState<number>(0);
  const [profileNameDraft, setProfileNameDraft] = useState<string>('');

  async function reloadProfiles() {
    try {
      const list = await ListProfiles();
      setProfiles(list);
      // Refresh the active-profile editor in case activation changed.
      const ap = await GetActiveProfile();
      setActiveProfile(ap as Profile);
    } catch (e: any) {
      setErr(String(e?.message ?? e));
    }
  }

  // Keep the ProfilesPanel selector in sync with the loaded list. If the
  // currently-selected profile is gone (post-delete) or none is selected
  // yet, default to the active one.
  useEffect(() => {
    if (!profiles.length) return;
    const stillThere = profiles.some((p) => (p.id as number) === profileSelectedId);
    if (!stillThere) {
      const next = profiles.find((p) => p.is_active) ?? profiles[0];
      setProfileSelectedId(next.id as number);
      setProfileNameDraft(next.name);
    }
  }, [profiles, profileSelectedId]);

  useEffect(() => {
    (async () => {
      try {
        const [l, ls, c, ap] = await Promise.all([
          GetLookupSettings(), GetListsSettings(), GetCATSettings(), GetActiveProfile(),
        ]);
        setLookup(l);
        setActiveProfile(ap as Profile);
        setLists(ls);
        await reloadProfiles();
        setBandsText((ls.bands ?? []).join('\n'));
        setCatCfg(c);
      } catch (e: any) {
        setErr(String(e?.message ?? e));
      } finally {
        setLoading(false);
      }
    })();
  }, []);

  function addMode() {
    setLists((l) => ({
      ...l,
      modes: [...(l.modes ?? []), { name: '', default_rst_sent: '59', default_rst_rcvd: '59' }],
    }));
  }
  function removeMode(i: number) {
    setLists((l) => {
      const next = [...(l.modes ?? [])];
      next.splice(i, 1);
      return { ...l, modes: next };
    });
  }
  function moveMode(i: number, dir: -1 | 1) {
    setLists((l) => {
      const next = [...(l.modes ?? [])];
      const j = i + dir;
      if (j < 0 || j >= next.length) return l;
      [next[i], next[j]] = [next[j], next[i]];
      return { ...l, modes: next };
    });
  }
  function updateMode(i: number, patch: Partial<ModePreset>) {
    setLists((l) => {
      const next = [...(l.modes ?? [])];
      next[i] = { ...next[i], ...patch } as ModePreset;
      return { ...l, modes: next };
    });
  }

  async function save() {
    setSaving(true); setErr(''); setMsg('');
    try {
      // Bands: dedup, lowercase, trim.
      const seen = new Set<string>();
      const bands: string[] = [];
      for (const line of bandsText.split('\n')) {
        const b = line.trim().toLowerCase();
        if (b && !seen.has(b)) { seen.add(b); bands.push(b); }
      }
      const modes = (lists.modes ?? [])
        .map((m) => ({
          name: (m.name ?? '').trim().toUpperCase(),
          default_rst_sent: (m.default_rst_sent ?? '').trim(),
          default_rst_rcvd: (m.default_rst_rcvd ?? '').trim(),
        }))
        .filter((m) => m.name !== '');
      await SaveListsSettings({ bands, modes } as any);

      if (activeProfile) {
        await SaveProfile({
          ...activeProfile,
          callsign:     (activeProfile.callsign     ?? '').trim().toUpperCase(),
          operator:     (activeProfile.operator     ?? '').trim().toUpperCase(),
          my_grid:      (activeProfile.my_grid      ?? '').trim().toUpperCase(),
          my_sota_ref:  (activeProfile.my_sota_ref  ?? '').trim().toUpperCase(),
          my_pota_ref:  (activeProfile.my_pota_ref  ?? '').trim().toUpperCase(),
        } as any);
      }
      await SaveLookupSettings(lookup as any);
      await SaveCATSettings(catCfg as any);

      setMsg('Settings saved.');
      onSaved();
      setTimeout(onClose, 500);
    } catch (e: any) {
      setErr(String(e?.message ?? e));
    } finally {
      setSaving(false);
    }
  }

  async function clearCache() {
    setClearing(true); setErr(''); setMsg('');
    try {
      await ClearLookupCache();
      setMsg('Cache cleared.');
    } catch (e: any) {
      setErr(String(e?.message ?? e));
    } finally {
      setClearing(false);
    }
  }

  async function testProvider(provider: 'qrz' | 'hamqth') {
    setLookupTesting((s) => ({ ...s, [provider]: true }));
    setLookupTest((s) => ({ ...s, [provider]: undefined }));
    const user = provider === 'qrz' ? lookup.qrz_user : lookup.hamqth_user;
    const pwd  = provider === 'qrz' ? lookup.qrz_password : lookup.hamqth_password;
    try {
      const r = await TestLookupProvider(provider, '', user ?? '', pwd ?? '');
      setLookupTest((s) => ({ ...s, [provider]: { ok: true, msg: `OK — ${r.callsign} (${r.name || r.country || 'no name'})` } }));
    } catch (e: any) {
      setLookupTest((s) => ({ ...s, [provider]: { ok: false, msg: String(e?.message ?? e) } }));
    } finally {
      setLookupTesting((s) => ({ ...s, [provider]: false }));
    }
  }

  const breadcrumb = useMemo(() => SECTION_LABELS[selected] ?? selected, [selected]);

  // === Section content renderers ===

  function StationPanel() {
    if (!activeProfile) {
      return <div className="text-muted-foreground text-sm">Loading profile…</div>;
    }
    const p = activeProfile;
    return (
      <>
        <SectionHeader
          title="Station Information"
          hint={`Editing the active profile: ${p.name}. Switch profiles in the Profiles section to edit a different one.`}
        />
        <div className="grid grid-cols-2 gap-3 max-w-2xl">
          <div className="space-y-1">
            <Label>Station callsign</Label>
            <Input className="font-mono uppercase" value={p.callsign ?? ''} onChange={(e) => updateActive({ callsign: e.target.value })} placeholder="F4XYZ" />
          </div>
          <div className="space-y-1">
            <Label>Operator</Label>
            <Input className="font-mono uppercase" value={p.operator ?? ''} onChange={(e) => updateActive({ operator: e.target.value })} placeholder="F4XYZ" />
          </div>
          <div className="space-y-1">
            <Label>My grid</Label>
            <Input className="font-mono uppercase" value={p.my_grid ?? ''} onChange={(e) => updateActive({ my_grid: e.target.value })} placeholder="JN18BU" />
          </div>
          <div className="space-y-1">
            <Label>My country</Label>
            <Input value={p.my_country ?? ''} onChange={(e) => updateActive({ my_country: e.target.value })} placeholder="France" />
          </div>
          <div className="space-y-1">
            <Label>State / pref</Label>
            <Input value={p.my_state ?? ''} onChange={(e) => updateActive({ my_state: e.target.value })} />
          </div>
          <div className="space-y-1">
            <Label>County</Label>
            <Input value={p.my_cnty ?? ''} onChange={(e) => updateActive({ my_cnty: e.target.value })} />
          </div>
          <div className="space-y-1 col-span-2">
            <Label>Street address</Label>
            <Input value={p.my_street ?? ''} onChange={(e) => updateActive({ my_street: e.target.value })} />
          </div>
          <div className="space-y-1">
            <Label>Postal code</Label>
            <Input value={p.my_postal_code ?? ''} onChange={(e) => updateActive({ my_postal_code: e.target.value })} />
          </div>
          <div className="space-y-1">
            <Label>City</Label>
            <Input value={p.my_city ?? ''} onChange={(e) => updateActive({ my_city: e.target.value })} />
          </div>
          <div className="space-y-1">
            <Label>SOTA ref</Label>
            <Input className="font-mono uppercase" value={p.my_sota_ref ?? ''} onChange={(e) => updateActive({ my_sota_ref: e.target.value })} placeholder="F/AB-001" />
          </div>
          <div className="space-y-1">
            <Label>POTA ref</Label>
            <Input className="font-mono uppercase" value={p.my_pota_ref ?? ''} onChange={(e) => updateActive({ my_pota_ref: e.target.value })} placeholder="FF-1234" />
          </div>
          <div className="space-y-1">
            <Label>Rig</Label>
            <Input value={p.my_rig ?? ''} onChange={(e) => updateActive({ my_rig: e.target.value })} placeholder="Flex 8600" />
          </div>
          <div className="space-y-1">
            <Label>Antenna</Label>
            <Input value={p.my_antenna ?? ''} onChange={(e) => updateActive({ my_antenna: e.target.value })} placeholder="Ultrabeam UB40" />
          </div>
          <div className="space-y-1">
            <Label>TX power (W)</Label>
            <Input
              type="number"
              value={p.tx_pwr ?? ''}
              onChange={(e) => updateActive({ tx_pwr: e.target.value === '' ? undefined : (parseFloat(e.target.value) || undefined) })}
              placeholder="100"
            />
          </div>
        </div>
      </>
    );
  }

  // Profile actions — kept at the SettingsModal level so the ProfilesPanel
  // renderer can stay hooks-free (the PANELS map calls it as a plain
  // function, not as a JSX component).
  const activeProfileObj = profiles.find((p) => p.is_active) ?? profiles[0];
  const currentProfile = profiles.find((p) => (p.id as number) === profileSelectedId);

  async function profileActivate() {
    if (!currentProfile) return;
    try { await ActivateProfile(currentProfile.id as number); await reloadProfiles(); onSaved(); }
    catch (e: any) { setErr(String(e?.message ?? e)); }
  }
  async function profileRemove() {
    if (!currentProfile) return;
    if (!confirm(`Delete profile "${currentProfile.name}"? All its settings will be lost.`)) return;
    try { await DeleteProfile(currentProfile.id as number); await reloadProfiles(); }
    catch (e: any) { setErr(String(e?.message ?? e)); }
  }
  async function profileDuplicate() {
    if (!currentProfile) return;
    const name = prompt(`Name for the new profile (copy of "${currentProfile.name}"):`, `${currentProfile.name} Copy`);
    if (!name?.trim()) return;
    try {
      const dup = await DuplicateProfile(currentProfile.id as number, name.trim());
      await reloadProfiles();
      setProfileSelectedId(dup.id as number);
      setProfileNameDraft(dup.name);
    } catch (e: any) { setErr(String(e?.message ?? e)); }
  }
  async function profileCreateBlank() {
    const name = prompt('Name for the new profile:', 'New profile');
    if (!name?.trim()) return;
    try {
      const blank = emptyProfile();
      blank.name = name.trim();
      const saved = await SaveProfile(blank as any);
      await reloadProfiles();
      setProfileSelectedId(saved.id as number);
      setProfileNameDraft(saved.name);
    } catch (e: any) { setErr(String(e?.message ?? e)); }
  }
  async function profileRenameCurrent() {
    if (!currentProfile || profileNameDraft.trim() === currentProfile.name) return;
    try {
      await SaveProfile({ ...currentProfile, name: profileNameDraft.trim() } as any);
      await reloadProfiles();
    } catch (e: any) { setErr(String(e?.message ?? e)); }
  }

  function ProfilesPanel() {
    const current = currentProfile;
    const active = activeProfileObj;
    return (
      <>
        <SectionHeader
          title="Profiles"
          hint="Switch between operating identities (home / portable / SOTA / contest). Pick a profile here, then edit its fields in the other sections (Station Information, etc.) — changes are saved against the selected profile."
        />
        <div className="space-y-4">
          <div className="grid grid-cols-[140px_1fr] items-center gap-x-3 gap-y-3">
            <Label>Configuration ID</Label>
            <Select
              value={String(profileSelectedId)}
              onValueChange={(v) => {
                const id = parseInt(v, 10);
                setProfileSelectedId(id);
                setProfileNameDraft(profiles.find((p) => (p.id as number) === id)?.name ?? '');
              }}
            >
              <SelectTrigger><SelectValue /></SelectTrigger>
              <SelectContent>
                {profiles.map((p) => (
                  <SelectItem key={p.id as number} value={String(p.id)}>
                    {p.name}{p.callsign ? ` — ${p.callsign}` : ''}{p.is_active ? ' (active)' : ''}
                  </SelectItem>
                ))}
              </SelectContent>
            </Select>

            <Label>Description</Label>
            <div className="flex items-center gap-2">
              <Input
                value={profileNameDraft}
                onChange={(e) => setProfileNameDraft(e.target.value)}
                onBlur={profileRenameCurrent}
                placeholder="Profile name"
                disabled={!current}
              />
              {current?.is_active && (
                <span className="inline-flex items-center px-2 py-0.5 rounded text-[10px] font-bold tracking-wider bg-emerald-100 text-emerald-800 border border-emerald-300">
                  ACTIVE
                </span>
              )}
            </div>
          </div>

          <div className="flex items-center gap-2 pt-2 border-t border-border">
            <Button variant="outline" size="sm" onClick={profileCreateBlank} title="Create a new empty profile">
              <Plus className="size-3.5 mr-1" /> New
            </Button>
            <Button variant="outline" size="sm" onClick={profileDuplicate} disabled={!current} title="Clone the selected profile (keeps all its fields)">
              <Copy className="size-3.5 mr-1" /> Duplicate
            </Button>
            <Button
              variant="outline" size="sm"
              onClick={profileActivate}
              disabled={!current || current.is_active}
              title="Activate the selected profile — new QSOs will use its MY_* fields"
            >
              <Star className="size-3.5 mr-1" /> Set active
            </Button>
            <Button
              variant="outline" size="sm"
              onClick={profileRemove}
              disabled={!current || profiles.length <= 1}
              className="text-destructive hover:text-destructive ml-auto"
              title={profiles.length <= 1 ? 'Cannot delete the last profile' : 'Delete the selected profile'}
            >
              <Trash2 className="size-3.5 mr-1" /> Delete
            </Button>
          </div>

          {current && !current.is_active && (
            <div className="text-xs text-muted-foreground bg-muted/30 border border-border rounded-md p-2.5">
              You're viewing <strong>{current.name}</strong>. The active profile is <strong>{active?.name}</strong> — its values are stamped on new QSOs. Click <em>Set active</em> to switch.
            </div>
          )}
        </div>
      </>
    );
  }

  function LookupPanel() {
    // Per-row provider editor — kept inline because it's only used twice
    // and needs closure access to the parent state.
    const row = (
      key: 'qrz' | 'hamqth', label: string, userField: 'qrz_user' | 'hamqth_user',
      pwdField: 'qrz_password' | 'hamqth_password',
    ) => {
      const test = lookupTest[key];
      const testing = lookupTesting[key];
      const hasCreds = !!(lookup[userField] && lookup[pwdField]);
      return (
        <tr className="border-t border-border align-middle">
          <td className="px-3 py-2 font-semibold whitespace-nowrap">{label}</td>
          <td className="px-2 py-2 text-center">
            <input
              type="radio"
              name="lookup-primary"
              checked={lookup.primary === key}
              onChange={() => setLookup((s) => ({ ...s, primary: key, failsafe: s.failsafe === key ? '' : s.failsafe }))}
              disabled={!hasCreds}
            />
          </td>
          <td className="px-2 py-2 text-center">
            <input
              type="radio"
              name="lookup-failsafe"
              checked={lookup.failsafe === key}
              onChange={() => setLookup((s) => ({ ...s, failsafe: key, primary: s.primary === key ? '' : s.primary }))}
              disabled={!hasCreds || lookup.primary === key}
            />
          </td>
          <td className="px-2 py-2">
            <Input
              className="h-8"
              value={lookup[userField] ?? ''}
              onChange={(e) => setLookup((s) => ({ ...s, [userField]: e.target.value }))}
              placeholder="User"
              autoComplete="off"
            />
          </td>
          <td className="px-2 py-2">
            <Input
              className="h-8"
              type="password"
              value={lookup[pwdField] ?? ''}
              onChange={(e) => setLookup((s) => ({ ...s, [pwdField]: e.target.value }))}
              placeholder="Password"
              autoComplete="off"
            />
          </td>
          <td className="px-2 py-2 whitespace-nowrap">
            <Button
              variant="outline" size="sm"
              onClick={() => testProvider(key)}
              disabled={!hasCreds || testing}
              title="Run a sample lookup against the active profile's callsign to verify credentials"
            >
              {testing ? 'Testing…' : 'Test'}
            </Button>
          </td>
          <td className={cn('px-2 py-2 text-xs', test?.ok ? 'text-emerald-700' : 'text-destructive')}>
            {test?.msg}
          </td>
        </tr>
      );
    };
    return (
      <>
        <SectionHeader
          title="Callsign Lookup"
          hint="Pick a Primary provider and an optional Failsafe (queried only when Primary returns no data). Click Test to verify credentials without saving."
        />
        <div className="rounded-md border border-border overflow-hidden">
          <table className="w-full text-sm">
            <thead className="bg-muted/40 text-[10px] uppercase tracking-wider text-muted-foreground">
              <tr>
                <th className="text-left px-3 py-2">Provider</th>
                <th className="px-2 py-2 w-20">Primary</th>
                <th className="px-2 py-2 w-20">Failsafe</th>
                <th className="text-left px-2 py-2">User</th>
                <th className="text-left px-2 py-2">Password</th>
                <th className="px-2 py-2 w-20"></th>
                <th className="text-left px-2 py-2">Result</th>
              </tr>
            </thead>
            <tbody>
              {row('qrz',    'QRZ.com', 'qrz_user',    'qrz_password')}
              {row('hamqth', 'HamQTH',  'hamqth_user', 'hamqth_password')}
            </tbody>
          </table>
        </div>
        <p className="text-xs text-muted-foreground mt-2">
          Failsafe is consulted only when the Primary returns no match or errors. Set both to none (uncheck) during contests to skip the network entirely.
        </p>

        <div className="mt-6 pt-4 border-t border-border">
          <h3 className="text-sm font-semibold mb-2">Cache</h3>
          <p className="text-xs text-muted-foreground mb-3">
            Successful lookups are cached locally so the same callsign isn't fetched twice. TTL controls how long before a fresh query is made.
          </p>
          <div className="flex gap-3 items-end">
            <div className="space-y-1 w-40">
              <Label>TTL (days)</Label>
              <Input
                type="number" min={1} max={3650}
                value={lookup.cache_ttl_days}
                onChange={(e) => setLookup((s) => ({ ...s, cache_ttl_days: parseInt(e.target.value) || 30 }))}
              />
            </div>
            <Button variant="outline" onClick={clearCache} disabled={clearing}>
              {clearing ? 'Clearing…' : 'Clear cache now'}
            </Button>
          </div>
        </div>
      </>
    );
  }

  function BandsPanel() {
    return (
      <>
        <SectionHeader title="Bands" hint="One ADIF band per line (e.g. 20m, 2m, 70cm). Order = display order in the entry form and the band-slot grid." />
        <Textarea
          className="font-mono min-h-[260px] max-w-md"
          value={bandsText}
          onChange={(e) => setBandsText(e.target.value)}
        />
      </>
    );
  }

  function ModesPanel() {
    return (
      <>
        <SectionHeader
          title="Modes & default RST"
          hint="When you pick a mode in the entry form, RST sent/rcvd auto-fill with these defaults (unless you've typed something)."
        />
        <div className="rounded-md border border-border overflow-hidden max-w-2xl">
          <div className="grid grid-cols-[auto_1fr_1fr_1fr_auto] gap-2 px-3 py-2 bg-muted text-[10px] uppercase tracking-wider font-semibold text-muted-foreground">
            <span className="w-12">Order</span>
            <span>Mode (ADIF)</span>
            <span>RST sent</span>
            <span>RST rcvd</span>
            <span className="w-8"></span>
          </div>
          <div className="divide-y divide-border">
            {(lists.modes ?? []).map((m, i) => (
              <div key={i} className="grid grid-cols-[auto_1fr_1fr_1fr_auto] gap-2 px-3 py-1.5 items-center">
                <div className="flex gap-0.5 w-12">
                  <Button size="icon" variant="ghost" className="size-6" onClick={() => moveMode(i, -1)} disabled={i === 0}>
                    <ArrowUp className="size-3" />
                  </Button>
                  <Button size="icon" variant="ghost" className="size-6" onClick={() => moveMode(i, 1)} disabled={i === (lists.modes?.length ?? 0) - 1}>
                    <ArrowDown className="size-3" />
                  </Button>
                </div>
                <Input className="font-mono uppercase h-7" value={m.name} onChange={(e) => updateMode(i, { name: e.target.value })} placeholder="SSB" />
                <Input className="font-mono h-7" value={m.default_rst_sent ?? ''} onChange={(e) => updateMode(i, { default_rst_sent: e.target.value })} placeholder="59" />
                <Input className="font-mono h-7" value={m.default_rst_rcvd ?? ''} onChange={(e) => updateMode(i, { default_rst_rcvd: e.target.value })} placeholder="59" />
                <Button size="icon" variant="ghost" className="size-6 text-destructive hover:bg-destructive/10" onClick={() => removeMode(i)}>
                  <Trash2 className="size-3" />
                </Button>
              </div>
            ))}
          </div>
        </div>
        <Button variant="outline" size="sm" onClick={addMode} className="mt-3">
          <Plus className="size-3.5" /> Add mode
        </Button>
      </>
    );
  }

  function CATPanel() {
    return (
      <>
        <SectionHeader
          title="CAT interface (OmniRig)"
          hint="Reads the rig's current frequency / band / mode and pushes them into the entry strip in real time. Requires OmniRig (free) installed and configured separately for your rig — HamLog just talks to it."
        />
        <div className="space-y-4 max-w-lg">
          <label className="flex items-center gap-2 text-sm cursor-pointer">
            <Checkbox checked={catCfg.enabled} onCheckedChange={(c) => setCatCfg((s) => ({ ...s, enabled: !!c }))} />
            Enable CAT
          </label>

          <div className="grid grid-cols-2 gap-3">
            <div className="space-y-1">
              <Label>Backend</Label>
              <Select value={catCfg.backend} onValueChange={(v) => setCatCfg((s) => ({ ...s, backend: v }))}>
                <SelectTrigger><SelectValue /></SelectTrigger>
                <SelectContent>
                  <SelectItem value="omnirig">OmniRig (Windows COM)</SelectItem>
                  <SelectItem value="flex" disabled>Flex SmartSDR (coming soon)</SelectItem>
                </SelectContent>
              </Select>
            </div>
            <div className="space-y-1">
              <Label>OmniRig rig slot</Label>
              <Select value={String(catCfg.omnirig_rig)} onValueChange={(v) => setCatCfg((s) => ({ ...s, omnirig_rig: parseInt(v) as 1 | 2 }))}>
                <SelectTrigger><SelectValue /></SelectTrigger>
                <SelectContent>
                  <SelectItem value="1">Rig 1</SelectItem>
                  <SelectItem value="2">Rig 2</SelectItem>
                </SelectContent>
              </Select>
            </div>
            <div className="space-y-1">
              <Label>Poll interval (ms)</Label>
              <Input
                type="number" min={50} max={2000} step={50}
                value={catCfg.poll_ms}
                onChange={(e) => setCatCfg((s) => ({ ...s, poll_ms: parseInt(e.target.value) || 250 }))}
              />
            </div>
            <div className="space-y-1">
              <Label>CAT delay (ms)</Label>
              <Input
                type="number" min={0} max={500} step={10}
                value={catCfg.delay_ms}
                onChange={(e) => setCatCfg((s) => ({ ...s, delay_ms: parseInt(e.target.value) || 0 }))}
              />
            </div>
            <div className="space-y-1 col-span-2">
              <Label>Default digital mode (when rig reports DIG)</Label>
              <Select
                value={catCfg.digital_default || 'FT8'}
                onValueChange={(v) => setCatCfg((s) => ({ ...s, digital_default: v }))}
              >
                <SelectTrigger><SelectValue /></SelectTrigger>
                <SelectContent>
                  {['FT8','FT4','RTTY','PSK31','MFSK','JS8','JT65','JT9','OLIVIA','DIGITALVOICE','DATA'].map(m => (
                    <SelectItem key={m} value={m}>{m}</SelectItem>
                  ))}
                </SelectContent>
              </Select>
            </div>
          </div>
          <p className="text-xs text-muted-foreground">
            Configure your rig (COM port, baud rate, model) in OmniRig's own settings GUI first.
            HamLog will read whichever Rig slot you select here. Set <strong>CAT delay</strong>
            {' '}above 0 if your rig drops commands sent back-to-back (some older Kenwood/Yaesu).
            OmniRig only reports generic "DIG" for digital modes — <strong>Default digital mode</strong>
            {' '}is the specific mode HamLog will surface (and log).
          </p>
        </div>
      </>
    );
  }

  // Map sections to their content + icon (for placeholder).
  const PANELS: Record<SectionId, () => JSX.Element> = {
    station:        StationPanel,
    profiles:       ProfilesPanel,
    lookup:         LookupPanel,
    'lists-bands':  BandsPanel,
    'lists-modes':  ModesPanel,
    cluster:        () => <ComingSoon id="cluster" icon={Wifi} />,
    backup:         () => <ComingSoon id="backup" icon={Database} />,
    awards:         () => <ComingSoon id="awards" icon={Award} />,
    cat:            CATPanel,
    rotator:        () => <ComingSoon id="rotator" icon={Compass} />,
    antenna:        () => <ComingSoon id="antenna" icon={AntennaIcon} />,
    audio:          () => <ComingSoon id="audio" icon={Server} />,
  };

  return (
    <Dialog open onOpenChange={(o) => { if (!o) onClose(); }}>
      <DialogContent className="max-w-[960px] w-full max-h-[90vh] grid grid-rows-[auto_1fr_auto] gap-0 p-0">
        <DialogHeader>
          <DialogTitle>Preferences</DialogTitle>
          <DialogDescription className="sr-only">Configure HamLog modules — station, lookup, hardware…</DialogDescription>
        </DialogHeader>

        {loading ? (
          <div className="p-6 text-muted-foreground">Loading…</div>
        ) : (
          <div className="grid grid-cols-[260px_1fr] min-h-0 overflow-hidden">
            {/* Left sidebar tree */}
            <aside className="border-r border-border bg-muted/30 overflow-y-auto p-2">
              <Tree selected={selected} onSelect={setSelected} />
            </aside>

            {/* Right content pane */}
            <div className="overflow-y-auto p-6">
              <div className="text-[10px] uppercase tracking-wider text-muted-foreground mb-3 font-semibold">
                {breadcrumb}
              </div>
              {PANELS[selected]?.()}

              {err && (
                <div className="mt-6 text-xs text-destructive bg-destructive/10 border border-destructive/30 rounded-md px-3 py-2 max-w-2xl">
                  {err}
                </div>
              )}
              {msg && (
                <div className="mt-6 text-xs rounded-md px-3 py-2 max-w-2xl text-[color:var(--color-ok)] bg-[color:var(--color-ok)]/10 border border-[color:var(--color-ok)]/30">
                  {msg}
                </div>
              )}
            </div>
          </div>
        )}

        <DialogFooter>
          <Button variant="outline" onClick={onClose} disabled={saving}>Cancel</Button>
          <Button onClick={save} disabled={saving || loading}>{saving ? 'Saving…' : 'Save all'}</Button>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}
