import { useCallback, useEffect, useState } from 'react';
import { Plus, Trash2, Edit2, RefreshCcw, ArrowDownToLine, ArrowUpFromLine } from 'lucide-react';
import {
  ListUDPIntegrations, SaveUDPIntegration, DeleteUDPIntegration, ReloadUDPIntegrations,
} from '../../wailsjs/go/main/App';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Checkbox } from '@/components/ui/checkbox';
import {
  Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, DialogDescription,
} from '@/components/ui/dialog';
import {
  Select, SelectTrigger, SelectValue, SelectContent, SelectItem,
} from '@/components/ui/select';
import { cn } from '@/lib/utils';

// Local mirror of the Go struct — we duplicate the type rather than depend
// on the generated Wails model because the inline `as any` casts are
// noisier than just owning the shape here.
type UDPConfig = {
  id: number;
  direction: 'inbound' | 'outbound';
  name: string;
  port: number;
  service_type: 'wsjt' | 'adif' | 'n1mm' | 'remote_call' | 'db_updated';
  multicast: boolean;
  multicast_group: string;
  destination_ip: string;
  enabled: boolean;
  sort_order: number;
};

// Service-type catalog used by the dropdowns; each entry is restricted to
// inbound or outbound and carries a hint suggesting reasonable defaults
// for the "preset" button.
const SERVICE_TYPES: Array<{
  id: UDPConfig['service_type'];
  direction: UDPConfig['direction'];
  label: string;
  hint: string;
  defaults: Partial<UDPConfig>;
}> = [
  {
    id: 'wsjt',
    direction: 'inbound',
    label: 'WSJT-X / JTDX / MSHV',
    hint: 'Auto-logs FT8/FT4/etc. QSOs and fills the entry callsign live.',
    defaults: { port: 2237, multicast: true, multicast_group: '224.0.0.1' },
  },
  {
    id: 'adif',
    direction: 'inbound',
    label: 'ADIF message (JTAlert, GridTracker)',
    hint: 'Receives a single ADIF record per packet and logs it.',
    defaults: { port: 2333, multicast: false },
  },
  {
    id: 'n1mm',
    direction: 'inbound',
    label: 'N1MM Logger+ (contest XML)',
    hint: 'Receives contest QSOs as XML messages.',
    defaults: { port: 12060, multicast: false },
  },
  {
    id: 'remote_call',
    direction: 'inbound',
    label: 'Remote callsign (DXHunter, custom)',
    hint: 'A short text packet containing just a callsign — fills the entry field.',
    defaults: { port: 12090, multicast: false },
  },
  {
    id: 'db_updated',
    direction: 'outbound',
    label: 'DB updated → notify other apps',
    hint: 'Sends the ADIF of every QSO you log to a remote listener (Cloudlog UDP, N1MM, …).',
    defaults: { port: 2333, destination_ip: '127.0.0.1' },
  },
];

type Props = { onError: (msg: string) => void };

export function UDPIntegrationsPanel({ onError }: Props) {
  const [items, setItems] = useState<UDPConfig[]>([]);
  const [loading, setLoading] = useState(true);
  const [editing, setEditing] = useState<UDPConfig | null>(null);

  const reload = useCallback(async () => {
    try {
      const list = await ListUDPIntegrations();
      setItems(((list ?? []) as any[]) as UDPConfig[]);
    } catch (e: any) { onError(String(e?.message ?? e)); }
    finally { setLoading(false); }
  }, [onError]);

  useEffect(() => { void reload(); }, [reload]);

  function addNew(direction: UDPConfig['direction']) {
    const preset = SERVICE_TYPES.find((s) => s.direction === direction)!;
    setEditing({
      id: 0,
      direction,
      name: '',
      port: preset.defaults.port ?? 2237,
      service_type: preset.id,
      multicast: !!preset.defaults.multicast,
      multicast_group: preset.defaults.multicast_group ?? '',
      destination_ip: preset.defaults.destination_ip ?? '',
      enabled: true,
      sort_order: items.filter((i) => i.direction === direction).length,
    });
  }

  async function save(cfg: UDPConfig) {
    try {
      const saved = await SaveUDPIntegration(cfg as any) as UDPConfig;
      setItems((prev) => {
        if (cfg.id === 0) return [...prev, saved];
        return prev.map((x) => x.id === saved.id ? saved : x);
      });
      setEditing(null);
    } catch (e: any) { onError(String(e?.message ?? e)); }
  }

  async function remove(id: number) {
    if (!confirm('Delete this UDP connection?')) return;
    try {
      await DeleteUDPIntegration(id);
      setItems((prev) => prev.filter((x) => x.id !== id));
    } catch (e: any) { onError(String(e?.message ?? e)); }
  }

  async function toggleEnabled(cfg: UDPConfig) {
    await save({ ...cfg, enabled: !cfg.enabled });
  }

  async function reloadServers() {
    try {
      const errs = await ReloadUDPIntegrations();
      if (errs && (errs as string[]).length > 0) {
        onError((errs as string[]).join(' • '));
      }
    } catch (e: any) { onError(String(e?.message ?? e)); }
  }

  if (loading) return <div className="text-xs text-muted-foreground italic">Loading…</div>;

  const inbound = items.filter((i) => i.direction === 'inbound');
  const outbound = items.filter((i) => i.direction === 'outbound');

  return (
    <div className="space-y-4">
      <div className="text-[11px] text-muted-foreground max-w-2xl leading-relaxed">
        UDP connections let OpsLog talk to other ham radio software. Inbound
        connections receive QSOs or callsigns and update the logbook live;
        outbound connections notify other apps when you log a QSO locally.
        Enable multicast to share a port with another listener without
        conflict — required for the typical WSJT-X 2237 setup.
      </div>

      <Section
        title="Inbound — OpsLog listens"
        icon={<ArrowDownToLine className="size-4" />}
        items={inbound}
        onAdd={() => addNew('inbound')}
        onEdit={(c) => setEditing(c)}
        onDelete={remove}
        onToggle={toggleEnabled}
      />
      <Section
        title="Outbound — OpsLog sends"
        icon={<ArrowUpFromLine className="size-4" />}
        items={outbound}
        onAdd={() => addNew('outbound')}
        onEdit={(c) => setEditing(c)}
        onDelete={remove}
        onToggle={toggleEnabled}
      />

      <div className="border-t border-border/60 pt-3 flex items-center gap-3">
        <Button size="sm" variant="outline" onClick={reloadServers}>
          <RefreshCcw className="size-3.5" /> Reload all
        </Button>
        <span className="text-[11px] text-muted-foreground">
          Restarts every enabled listener after a manual change.
        </span>
      </div>

      {editing && (
        <EditDialog
          cfg={editing}
          onCancel={() => setEditing(null)}
          onSave={save}
        />
      )}
    </div>
  );
}

// ── Section listing ────────────────────────────────────────────────────

function Section({
  title, icon, items, onAdd, onEdit, onDelete, onToggle,
}: {
  title: string;
  icon: React.ReactNode;
  items: UDPConfig[];
  onAdd: () => void;
  onEdit: (c: UDPConfig) => void;
  onDelete: (id: number) => void;
  onToggle: (c: UDPConfig) => void;
}) {
  return (
    <div className="rounded-md border border-border bg-card">
      <div className="flex items-center gap-2 px-3 py-2 border-b border-border/60 bg-muted/30">
        {icon}
        <span className="text-[11px] font-semibold uppercase tracking-wider text-muted-foreground">{title}</span>
        <div className="flex-1" />
        <Button size="sm" variant="outline" className="h-7 text-xs" onClick={onAdd}>
          <Plus className="size-3" /> Add
        </Button>
      </div>
      {items.length === 0 ? (
        <div className="px-3 py-3 text-xs text-muted-foreground italic">No connection.</div>
      ) : (
        <div className="divide-y divide-border/60">
          {items.map((c) => {
            const svc = SERVICE_TYPES.find((s) => s.id === c.service_type);
            return (
              <div key={c.id} className="flex items-center gap-2 px-3 py-2">
                <Checkbox checked={c.enabled} onCheckedChange={() => onToggle(c)} />
                <div className="flex-1 min-w-0">
                  <div className="flex items-center gap-2">
                    <span className="font-semibold text-sm truncate">{c.name || '(unnamed)'}</span>
                    <span className="text-[10px] uppercase tracking-wider text-muted-foreground bg-muted px-1.5 py-0.5 rounded">
                      {svc?.label ?? c.service_type}
                    </span>
                  </div>
                  <div className="text-[11px] text-muted-foreground font-mono">
                    {c.multicast
                      ? <>multicast <strong>{c.multicast_group || '?'}</strong>:{c.port}</>
                      : c.direction === 'outbound'
                        ? <>→ {c.destination_ip || '?'}:{c.port}</>
                        : <>:{c.port}</>
                    }
                  </div>
                </div>
                <Button size="icon" variant="ghost" className="size-7" onClick={() => onEdit(c)}>
                  <Edit2 className="size-3.5" />
                </Button>
                <Button size="icon" variant="ghost" className="size-7 text-destructive hover:bg-destructive/10" onClick={() => onDelete(c.id)}>
                  <Trash2 className="size-3.5" />
                </Button>
              </div>
            );
          })}
        </div>
      )}
    </div>
  );
}

// ── Edit dialog ────────────────────────────────────────────────────────

function EditDialog({
  cfg, onCancel, onSave,
}: {
  cfg: UDPConfig;
  onCancel: () => void;
  onSave: (c: UDPConfig) => void;
}) {
  const [draft, setDraft] = useState<UDPConfig>(cfg);
  // Service-type list filtered to this connection's direction.
  const services = SERVICE_TYPES.filter((s) => s.direction === draft.direction);
  const currentService = services.find((s) => s.id === draft.service_type);

  function applyPreset(id: UDPConfig['service_type']) {
    const preset = SERVICE_TYPES.find((s) => s.id === id);
    if (!preset) return;
    setDraft((d) => ({
      ...d,
      service_type: id,
      port: preset.defaults.port ?? d.port,
      multicast: preset.defaults.multicast ?? d.multicast,
      multicast_group: preset.defaults.multicast_group ?? d.multicast_group,
      destination_ip: preset.defaults.destination_ip ?? d.destination_ip,
    }));
  }

  return (
    <Dialog open onOpenChange={(o) => { if (!o) onCancel(); }}>
      <DialogContent className="max-w-xl">
        <DialogHeader>
          <DialogTitle>
            {cfg.id === 0 ? 'New' : 'Edit'} {draft.direction} connection
          </DialogTitle>
          <DialogDescription>
            {currentService?.hint}
          </DialogDescription>
        </DialogHeader>

        <div className="space-y-4 px-5 py-4">
          <div className="space-y-1">
            <Label>Name</Label>
            <Input
              autoFocus
              placeholder={draft.direction === 'inbound' ? 'WSJT-X log' : 'Cloudlog notify'}
              value={draft.name}
              onChange={(e) => setDraft((d) => ({ ...d, name: e.target.value }))}
            />
          </div>

          <div className="space-y-1">
            <Label>Service type</Label>
            <Select value={draft.service_type} onValueChange={(v) => applyPreset(v as UDPConfig['service_type'])}>
              <SelectTrigger><SelectValue /></SelectTrigger>
              <SelectContent>
                {services.map((s) => (
                  <SelectItem key={s.id} value={s.id}>{s.label}</SelectItem>
                ))}
              </SelectContent>
            </Select>
          </div>

          <div className="grid grid-cols-[1fr_auto] gap-2 items-end">
            <div className="space-y-1">
              <Label>Port</Label>
              <Input
                type="number"
                min={1} max={65535}
                className="font-mono"
                value={draft.port}
                onChange={(e) => {
                  const n = Number(e.target.value);
                  if (Number.isFinite(n)) setDraft((d) => ({ ...d, port: Math.floor(n) }));
                }}
              />
            </div>
            <label className="flex items-center gap-2 text-sm cursor-pointer pb-2">
              <Checkbox
                checked={draft.multicast}
                onCheckedChange={(c) => setDraft((d) => ({ ...d, multicast: !!c }))}
              />
              <span>Multicast</span>
            </label>
          </div>

          {draft.multicast && (
            <div className="space-y-1">
              <Label>Multicast group</Label>
              <Input
                className="font-mono"
                placeholder="224.0.0.1"
                value={draft.multicast_group}
                onChange={(e) => setDraft((d) => ({ ...d, multicast_group: e.target.value }))}
              />
              <div className="text-[10px] text-muted-foreground">
                Use the same group address as the sending app. WSJT-X default is 224.0.0.1.
              </div>
            </div>
          )}

          {draft.direction === 'outbound' && (
            <div className="space-y-1">
              <Label>Destination IP</Label>
              <Input
                className="font-mono"
                placeholder="127.0.0.1"
                value={draft.destination_ip}
                onChange={(e) => setDraft((d) => ({ ...d, destination_ip: e.target.value }))}
              />
            </div>
          )}

          <label className="flex items-center gap-2 text-sm cursor-pointer">
            <Checkbox
              checked={draft.enabled}
              onCheckedChange={(c) => setDraft((d) => ({ ...d, enabled: !!c }))}
            />
            <span>Enabled</span>
          </label>
        </div>

        <DialogFooter>
          <Button variant="ghost" onClick={onCancel}>Cancel</Button>
          <Button
            onClick={() => onSave(draft)}
            disabled={!draft.name.trim() || !draft.port}
          >
            Save
          </Button>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}

// silence unused-import for cn — kept for future styling tweaks
void cn;
