import { useEffect, useRef, useState } from 'react';
import { Loader2, Plus, RefreshCw, Stethoscope } from 'lucide-react';
import { api } from '@/api/client';
import type { CoderProvidersFile, ProviderOverride, ProviderSnapshotEntry } from '@/api/types';
import { useProviderSnapshot, refreshProviderSnapshot } from '@/hooks/useProviderSnapshot';
import { Button } from '@/components/ui/button';
import { AddProviderModal } from './AddProviderModal';
import { cn } from '@/lib/utils';

/** Map a snapshot entry to a status badge (design.md §7.1 labels). */
function statusBadge(e: ProviderSnapshotEntry): { label: string; cls: string } {
  if (e.status === 'loading') return { label: 'Loading', cls: 'bg-muted text-muted-foreground' };
  if (!e.enabled) return { label: 'Disabled', cls: 'bg-muted text-muted-foreground' };
  if (e.status === 'ready')
    return { label: 'Available', cls: 'bg-green-500/15 text-green-600 dark:text-green-400' };
  if (e.status === 'error')
    return { label: 'Error', cls: 'bg-red-500/15 text-red-600 dark:text-red-400' };
  if (!e.installed)
    return { label: 'Not installed', cls: 'bg-amber-500/15 text-amber-600 dark:text-amber-400' };
  return { label: 'Unavailable', cls: 'bg-muted text-muted-foreground' };
}

/**
 * v2.3 — provider management as a Settings tab section (design.md §7.1). Lists
 * ALL registered providers (including the disabled/unavailable ones the composer
 * picker hides). Per row: label + model count, status badge, per-id refresh,
 * diagnostic, and an enable/disable toggle. Native boocode is always-on.
 *
 * Uses the home-cwd snapshot (no project arg) — provider management is global,
 * not per-project (design.md §4.5).
 */
export function ProvidersSettings() {
  const allEntries = useProviderSnapshot();
  const [config, setConfig] = useState<CoderProvidersFile | null>(null);
  const [busyId, setBusyId] = useState<string | null>(null);
  const [error, setError] = useState<string | null>(null);
  const [addOpen, setAddOpen] = useState(false);
  const [diagId, setDiagId] = useState<string | null>(null);
  const [diagText, setDiagText] = useState<string | null>(null);

  // The raw config is needed to preserve a provider's FULL override when
  // toggling: the PATCH replaces an id's override wholesale, so a bare
  // { enabled } would wipe a custom ACP provider's command/label.
  useEffect(() => {
    api.coder
      .getProvidersConfig()
      .then(setConfig)
      .catch(() => setConfig({ providers: {} }));
  }, []);

  // While any entry is loading, refetch until terminal (capped, no WS frame).
  const pollsRef = useRef(0);
  useEffect(() => {
    const anyLoading = allEntries?.some((e) => e.status === 'loading') ?? false;
    if (!anyLoading) {
      pollsRef.current = 0;
      return;
    }
    if (pollsRef.current >= 10) return;
    const t = setTimeout(() => {
      pollsRef.current += 1;
      void refreshProviderSnapshot();
    }, 2000);
    return () => clearTimeout(t);
  }, [allEntries]);

  async function toggle(e: ProviderSnapshotEntry): Promise<void> {
    setBusyId(e.name);
    setError(null);
    try {
      const existing: ProviderOverride = config?.providers[e.name] ?? {};
      const resp = await api.coder.patchProvidersConfig({
        providers: { [e.name]: { ...existing, enabled: !e.enabled } },
      });
      setConfig({ providers: resp.providers });
      await refreshProviderSnapshot();
    } catch (err) {
      setError(err instanceof Error ? err.message : 'failed to update provider');
    } finally {
      setBusyId(null);
    }
  }

  async function refreshOne(id: string): Promise<void> {
    setBusyId(id);
    setError(null);
    try {
      await api.coder.refreshProviders([id]);
      await refreshProviderSnapshot();
    } catch (err) {
      setError(err instanceof Error ? err.message : 'failed to refresh');
    } finally {
      setBusyId(null);
    }
  }

  async function openDiagnostic(id: string): Promise<void> {
    if (diagId === id) {
      setDiagId(null);
      setDiagText(null);
      return;
    }
    setDiagId(id);
    setDiagText('Loading…');
    try {
      const { diagnostic } = await api.coder.getProviderDiagnostic(id);
      setDiagText(diagnostic);
    } catch (err) {
      setDiagText(err instanceof Error ? err.message : 'failed to load diagnostic');
    }
  }

  const entries = allEntries ?? [];

  return (
    <div className="space-y-3">
      <div className="flex items-center justify-between gap-2">
        <p className="text-xs text-muted-foreground">
          Enable, disable, refresh, or add coding agents. Disabled and unavailable providers are
          hidden from the composer picker but managed here.
        </p>
        <Button variant="outline" size="sm" onClick={() => setAddOpen(true)} className="shrink-0">
          <Plus className="size-3.5" /> Add provider
        </Button>
      </div>

      <div className="rounded-md border divide-y">
        {allEntries === null && (
          <div className="px-3 py-2 text-sm text-muted-foreground">Loading…</div>
        )}
        {entries.map((e) => {
          const badge = statusBadge(e);
          const isNative = e.transport === 'native';
          const busy = busyId === e.name;
          return (
            <div key={e.name} className="px-3 py-2.5">
              <div className="flex items-center gap-2">
                <div className="min-w-0 flex-1">
                  <div className="text-sm font-medium truncate">{e.label}</div>
                  <div className="text-xs text-muted-foreground">
                    {e.models.length} model{e.models.length === 1 ? '' : 's'}
                  </div>
                </div>
                <span
                  className={cn(
                    'inline-flex items-center rounded px-1.5 py-0.5 text-[11px] font-medium',
                    badge.cls,
                  )}
                >
                  {e.status === 'loading' && <Loader2 className="size-3 mr-1 animate-spin" />}
                  {badge.label}
                </span>
                <button
                  type="button"
                  onClick={() => void refreshOne(e.name)}
                  disabled={busy}
                  className="inline-flex items-center justify-center size-7 rounded text-muted-foreground hover:text-foreground disabled:opacity-40"
                  aria-label={`Refresh ${e.label}`}
                  title="Refresh"
                >
                  <RefreshCw className={cn('size-3.5', busy && 'animate-spin')} />
                </button>
                <button
                  type="button"
                  onClick={() => void openDiagnostic(e.name)}
                  className="inline-flex items-center justify-center size-7 rounded text-muted-foreground hover:text-foreground"
                  aria-label={`Diagnostic for ${e.label}`}
                  title="Diagnostic"
                >
                  <Stethoscope className="size-3.5" />
                </button>
                {isNative ? (
                  <span className="text-[11px] text-muted-foreground w-14 text-center">
                    Always on
                  </span>
                ) : (
                  <button
                    type="button"
                    role="switch"
                    aria-checked={e.enabled}
                    disabled={busy}
                    onClick={() => void toggle(e)}
                    className={cn(
                      'relative inline-flex h-5 w-9 shrink-0 items-center rounded-full transition-colors disabled:opacity-40',
                      e.enabled ? 'bg-primary' : 'bg-muted-foreground/30',
                    )}
                    aria-label={`${e.enabled ? 'Disable' : 'Enable'} ${e.label}`}
                    title={e.enabled ? 'Enabled — click to disable' : 'Disabled — click to enable'}
                  >
                    <span
                      className={cn(
                        'inline-block size-4 rounded-full bg-background transition-transform',
                        e.enabled ? 'translate-x-4' : 'translate-x-0.5',
                      )}
                    />
                  </button>
                )}
              </div>
              {diagId === e.name && (
                <pre className="mt-2 max-h-48 overflow-auto rounded bg-muted/50 p-2 text-[11px] font-mono whitespace-pre-wrap">
                  {diagText}
                </pre>
              )}
            </div>
          );
        })}
      </div>

      {error && <div className="text-sm text-destructive">{error}</div>}

      <AddProviderModal
        open={addOpen}
        onOpenChange={setAddOpen}
        onAdded={() => void refreshProviderSnapshot()}
      />
    </div>
  );
}
