import { useCallback, useEffect, useState } from 'react';
import { X, Loader2, Save, FileDown, GitCompare, CheckCircle2, AlertTriangle, ShieldCheck, Download } from 'lucide-react';

interface HostInfo {
  providerId: string;
  sshHost: string | null;
  sshUser: string | null;
  sshKeyPath: string | null;
  configPath: string | null;
  restartCmd: string | null;
  sshMode: 'shell' | 'wrapper';
  sshConfigured: boolean;
}

interface ApplyResult {
  ok: boolean;
  step: string;
  backupPath?: string;
  error?: string;
  diff?: string;
}

/**
 * P9.1: SSH config editor for a single llama-swap host. Set SSH settings, load
 * the remote config, validate against the fork schema, preview a diff, and apply
 * (backup -> write -> restart -> health-wait) behind a confirmation.
 */
export function HostConfigEditor({ providerId, onClose }: { providerId: string; onClose: () => void }) {
  const [host, setHost] = useState<HostInfo | null>(null);
  const [form, setForm] = useState<Partial<HostInfo>>({});
  const [content, setContent] = useState('');
  const [busy, setBusy] = useState<string | null>(null);
  const [validation, setValidation] = useState<{ valid: boolean; errors: string[] } | null>(null);
  const [diff, setDiff] = useState<string | null>(null);
  const [applyResult, setApplyResult] = useState<ApplyResult | null>(null);
  const [message, setMessage] = useState<string | null>(null);
  const [pullRepo, setPullRepo] = useState('');
  const [pullMsg, setPullMsg] = useState<string | null>(null);

  useEffect(() => {
    const handleKeyDown = (event: KeyboardEvent) => {
      if (event.key === 'Escape') onClose();
    };
    document.addEventListener('keydown', handleKeyDown);
    return () => document.removeEventListener('keydown', handleKeyDown);
  }, [onClose]);

  const loadHost = useCallback(async () => {
    const res = await fetch('/api/control/hosts');
    const data = await res.json() as { hosts: HostInfo[] };
    const h = data.hosts.find((x) => x.providerId === providerId) ?? null;
    setHost(h);
    if (h) setForm({ sshHost: h.sshHost, sshUser: h.sshUser, sshKeyPath: h.sshKeyPath, configPath: h.configPath, restartCmd: h.restartCmd, sshMode: h.sshMode ?? 'shell' });
  }, [providerId]);

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

  const saveSettings = async () => {
    setBusy('settings');
    setMessage(null);
    try {
      const res = await fetch(`/api/control/hosts/${providerId}`, {
        method: 'PATCH',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(form),
      });
      if (res.ok) { setMessage('SSH settings saved'); await loadHost(); }
      else setMessage(`Save failed: ${res.status}`);
    } finally { setBusy(null); }
  };

  const loadConfig = async () => {
    setBusy('load');
    setMessage(null);
    setDiff(null); setValidation(null); setApplyResult(null);
    try {
      const res = await fetch(`/api/control/hosts/${providerId}/config`);
      const data = await res.json() as { content?: string; error?: string };
      if (res.ok && data.content != null) setContent(data.content);
      else setMessage(data.error ?? `Load failed: ${res.status}`);
    } finally { setBusy(null); }
  };

  const validate = async () => {
    setBusy('validate');
    try {
      const res = await fetch(`/api/control/hosts/${providerId}/config/validate`, {
        method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ content }),
      });
      setValidation(await res.json());
    } finally { setBusy(null); }
  };

  const showDiff = async () => {
    setBusy('diff');
    try {
      const res = await fetch(`/api/control/hosts/${providerId}/config/diff`, {
        method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ content }),
      });
      const data = await res.json() as { diff?: string; error?: string };
      setDiff(data.diff ?? data.error ?? '(no changes)');
    } finally { setBusy(null); }
  };

  const apply = async () => {
    if (!confirm('Apply config: backup, write, restart llama-swap, and health-wait?')) return;
    setBusy('apply');
    setApplyResult(null);
    try {
      const res = await fetch(`/api/control/hosts/${providerId}/config/apply`, {
        method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ content, confirm: true }),
      });
      setApplyResult(await res.json());
    } finally { setBusy(null); }
  };

  const pull = async () => {
    const repo = pullRepo.trim();
    if (!repo) return;
    setBusy('pull');
    setPullMsg(null);
    try {
      const res = await fetch(`/api/control/hosts/${providerId}/pull`, {
        method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ repo }),
      });
      const data = await res.json() as { jobId?: string; error?: string };
      setPullMsg(res.ok ? `queued (job ${data.jobId}) - track it in the Jobs tab` : (data.error ?? `failed: ${res.status}`));
    } finally { setBusy(null); }
  };

  return (
    <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50" onClick={onClose}>
      <div
        className="bg-background border border-border rounded-lg w-[min(900px,92vw)] max-h-[88vh] flex flex-col"
        role="dialog"
        aria-modal="true"
        onClick={(e) => e.stopPropagation()}
      >
        <div className="flex items-center justify-between px-4 py-2 border-b border-border/40">
          <h2 className="text-sm font-medium">SSH config — {providerId}</h2>
          <button type="button" onClick={onClose} className="text-muted-foreground hover:text-foreground" aria-label="Close config editor"><X className="size-4" /></button>
        </div>

        <div className="flex-1 overflow-auto p-4 space-y-4 min-h-0">
          {/* SSH settings */}
          <div className="grid grid-cols-2 gap-2">
            {([
              ['sshHost', 'SSH host (Tailscale IP)'],
              ['sshUser', 'SSH user'],
              ['sshKeyPath', 'SSH key path (secrets/...)'],
              ['configPath', 'Remote config path'],
              ['restartCmd', 'Restart command (nssm/systemctl)'],
            ] as const).map(([key, label]) => (
              <input
                key={key}
                placeholder={label}
                value={(form[key] as string) ?? ''}
                onChange={(e) => setForm({ ...form, [key]: e.target.value })}
                className="text-xs bg-background border border-border rounded-md px-2 py-1 font-mono"
              />
            ))}
            <select
              value={form.sshMode ?? 'shell'}
              onChange={(e) => setForm({ ...form, sshMode: e.target.value as 'shell' | 'wrapper' })}
              className="text-xs bg-background border border-border rounded-md px-2 py-1"
              title="shell = raw commands; wrapper = forced-command verbs (locked-down key)"
            >
              <option value="shell">SSH mode: shell (raw)</option>
              <option value="wrapper">SSH mode: wrapper (forced-command)</option>
            </select>
          </div>
          <div className="flex items-center gap-2">
            <button onClick={saveSettings} disabled={busy !== null} className="flex items-center gap-1 px-3 py-1 text-xs bg-primary text-primary-foreground rounded-md hover:bg-primary/90 disabled:opacity-50">
              <Save className="size-3" /> Save settings
            </button>
            <button onClick={loadConfig} disabled={busy !== null || !host?.sshConfigured} className="flex items-center gap-1 px-3 py-1 text-xs border border-border rounded-md hover:bg-muted/30 disabled:opacity-50">
              <FileDown className="size-3" /> Load remote config
            </button>
            {!host?.sshConfigured && <span className="text-xs text-muted-foreground">Set SSH host/user/key/config path, then save.</span>}
            {message && <span className="text-xs text-muted-foreground">{message}</span>}
          </div>

          {/* Editor */}
          <textarea
            value={content}
            onChange={(e) => { setContent(e.target.value); setValidation(null); setDiff(null); setApplyResult(null); }}
            placeholder="Load the remote config or paste a candidate config.yaml…"
            className="w-full h-64 text-xs font-mono bg-background border border-border rounded-md px-2 py-1"
            spellCheck={false}
          />

          <div className="flex items-center gap-2">
            <button onClick={validate} disabled={busy !== null || !content} className="flex items-center gap-1 px-3 py-1 text-xs border border-border rounded-md hover:bg-muted/30 disabled:opacity-50">
              <ShieldCheck className="size-3" /> Validate
            </button>
            <button onClick={showDiff} disabled={busy !== null || !content || !host?.sshConfigured} className="flex items-center gap-1 px-3 py-1 text-xs border border-border rounded-md hover:bg-muted/30 disabled:opacity-50">
              <GitCompare className="size-3" /> Diff vs remote
            </button>
            <button onClick={apply} disabled={busy !== null || !content || !host?.sshConfigured || validation?.valid === false} className="flex items-center gap-1 px-3 py-1 text-xs bg-amber-500/20 text-amber-300 border border-amber-500/40 rounded-md hover:bg-amber-500/30 disabled:opacity-50">
              {busy === 'apply' ? <Loader2 className="size-3 animate-spin" /> : <CheckCircle2 className="size-3" />} Apply (backup + restart)
            </button>
          </div>

          {validation && (
            <div className={`text-xs rounded-md border p-2 ${validation.valid ? 'border-green-500/30 bg-green-500/10 text-green-400' : 'border-red-500/30 bg-red-500/10 text-red-400'}`}>
              {validation.valid ? 'Config is valid against the llama-swap schema.' : (
                <>
                  <div className="flex items-center gap-1 font-medium"><AlertTriangle className="size-3" /> Invalid config</div>
                  <ul className="mt-1 list-disc list-inside">{validation.errors.map((e, i) => <li key={i}>{e}</li>)}</ul>
                </>
              )}
            </div>
          )}

          {diff !== null && (
            <pre className="text-xs font-mono bg-muted/20 border border-border/40 rounded-md p-2 overflow-auto max-h-48 whitespace-pre-wrap">{diff || '(no changes)'}</pre>
          )}

          {applyResult && (
            <div className={`text-xs rounded-md border p-2 ${applyResult.ok ? 'border-green-500/30 bg-green-500/10 text-green-400' : 'border-red-500/30 bg-red-500/10 text-red-400'}`}>
              <div className="font-medium">{applyResult.ok ? 'Applied successfully' : `Failed at step: ${applyResult.step}`}</div>
              {applyResult.backupPath && <div className="text-muted-foreground">Backup: {applyResult.backupPath}</div>}
              {applyResult.error && <div>{applyResult.error}</div>}
            </div>
          )}

          {/* Pull model from HuggingFace */}
          <div className="border-t border-border/40 pt-3">
            <div className="text-xs font-medium mb-1">Pull model from HuggingFace</div>
            <div className="flex items-center gap-2">
              <input
                placeholder="org/name (e.g. Qwen/Qwen3.5-9B)"
                value={pullRepo}
                onChange={(e) => setPullRepo(e.target.value)}
                className="flex-1 text-xs font-mono bg-background border border-border rounded-md px-2 py-1"
              />
              <button
                onClick={pull}
                disabled={busy !== null || !pullRepo.trim() || !host?.sshConfigured}
                className="flex items-center gap-1 px-3 py-1 text-xs border border-border rounded-md hover:bg-muted/30 disabled:opacity-50"
              >
                {busy === 'pull' ? <Loader2 className="size-3 animate-spin" /> : <Download className="size-3" />} Pull
              </button>
            </div>
            {pullMsg && <div className="mt-1 text-xs text-muted-foreground">{pullMsg}</div>}
          </div>
        </div>
      </div>
    </div>
  );
}
