'use client';

import { useEffect, useState } from 'react';
import { Loader2, Plus, Trash2, Star } from 'lucide-react';
import { MODEL_MENU } from '@/lib/ai/pricing';

/**
 * v1.1.0:4 — Multi-config AI integration panel.
 *
 * Lets the user save multiple AI configurations (one per provider, or
 * several of the same provider with different models) and toggle one
 * as active. Per-config "Test connection" so you can verify before
 * activating. Dropdowns of recommended models for major providers.
 * Ollama auto-detect: probes the StartOS internal address + offers a
 * dropdown of installed models when reachable.
 */

const PROVIDERS = [
  { id: 'claude', label: 'Anthropic Claude', requiresKey: true, requiresUrl: false },
  { id: 'openai', label: 'OpenAI', requiresKey: true, requiresUrl: false },
  {
    id: 'openai-compatible',
    label: 'OpenAI-compatible (custom URL)',
    requiresKey: true,
    requiresUrl: true,
  },
  { id: 'gemini', label: 'Google Gemini', requiresKey: true, requiresUrl: false },
  { id: 'ollama', label: 'Ollama (self-hosted)', requiresKey: false, requiresUrl: true },
] as const;

type ProviderId = (typeof PROVIDERS)[number]['id'];

interface SavedConfig {
  id: string;
  name: string;
  provider: ProviderId;
  model: string;
  baseUrl: string | null;
  keyConfigured: boolean;
  createdAt: string;
}

type TestResult =
  | { ok: true; sample: string; tokensIn?: number; tokensOut?: number; ms: number }
  | { ok: false; error: string; ms?: number };

export default function AIIntegration() {
  const [configs, setConfigs] = useState<SavedConfig[]>([]);
  const [activeId, setActiveId] = useState<string | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [showForm, setShowForm] = useState(false);
  const [editingId, setEditingId] = useState<string | null>(null);

  const refresh = async () => {
    setError(null);
    try {
      const r = await fetch('/api/ai/configs');
      if (!r.ok) throw new Error(`HTTP ${r.status}`);
      const body = await r.json();
      setConfigs(body.configs ?? []);
      setActiveId(body.activeId ?? null);
    } catch (e) {
      setError((e as Error).message);
    } finally {
      setLoading(false);
    }
  };

  useEffect(() => {
    refresh();
  }, []);

  const handleActivate = async (id: string) => {
    const r = await fetch(`/api/ai/configs/${id}/activate`, { method: 'POST' });
    if (r.ok) await refresh();
    else alert('Failed to activate.');
  };

  const handleDelete = async (id: string, name: string) => {
    if (!confirm(`Delete the AI config "${name}"? You'll need to re-enter it to use it again.`))
      return;
    const r = await fetch(`/api/ai/configs/${id}`, { method: 'DELETE' });
    if (r.ok) await refresh();
    else alert('Failed to delete.');
  };

  return (
    <section className="bg-zinc-900 border border-zinc-800 rounded-lg p-6 space-y-4" id="ai-integration">
      <header>
        <h2 className="text-lg font-bold text-white">AI integration</h2>
        <p className="text-sm text-zinc-500 mt-1">
          Save multiple AI configurations and toggle which one the{' '}
          <span className="text-zinc-300">AI → Generate</span> page uses.
          Self-hosted Ollama on StartOS auto-detects — no key needed.
        </p>
      </header>

      {error && (
        <div className="rounded bg-red-900/50 px-3 py-2 border border-red-800 text-xs text-red-400">
          {error}
        </div>
      )}

      {loading ? (
        <div className="text-zinc-500 text-sm flex items-center gap-2">
          <Loader2 className="w-4 h-4 animate-spin" />
          Loading configs…
        </div>
      ) : (
        <>
          {configs.length === 0 && !showForm && (
            <div className="rounded border border-zinc-800 px-4 py-6 text-sm text-zinc-400 text-center">
              No AI configs yet. Add one to start generating programs.
            </div>
          )}

          {configs.length > 0 && (
            <ul className="space-y-2">
              {configs.map((c) => (
                <ConfigRow
                  key={c.id}
                  cfg={c}
                  isActive={c.id === activeId}
                  isEditing={editingId === c.id}
                  onActivate={() => handleActivate(c.id)}
                  onDelete={() => handleDelete(c.id, c.name)}
                  onEdit={() => setEditingId(editingId === c.id ? null : c.id)}
                  onSaved={() => {
                    setEditingId(null);
                    refresh();
                  }}
                />
              ))}
            </ul>
          )}

          {showForm ? (
            <ConfigForm
              onCancel={() => setShowForm(false)}
              onCreated={() => {
                setShowForm(false);
                refresh();
              }}
            />
          ) : (
            <button
              type="button"
              onClick={() => setShowForm(true)}
              className="inline-flex items-center gap-2 px-4 py-2 rounded border border-zinc-700 text-zinc-200 text-xs uppercase tracking-wider hover:bg-zinc-800"
            >
              <Plus className="w-4 h-4" />
              Add AI config
            </button>
          )}
        </>
      )}
    </section>
  );
}

/**
 * One saved config row. Shows provider/model/key indicator + active
 * badge. Click "Test" to ping the model. Click "Set active" to make
 * this the one Generate uses. Click "Edit" to expand an inline form
 * for renaming, swapping the model, or rotating the key.
 */
function ConfigRow({
  cfg,
  isActive,
  isEditing,
  onActivate,
  onDelete,
  onEdit,
  onSaved,
}: {
  cfg: SavedConfig;
  isActive: boolean;
  isEditing: boolean;
  onActivate: () => void;
  onDelete: () => void;
  onEdit: () => void;
  onSaved: () => void;
}) {
  const [testing, setTesting] = useState(false);
  const [testResult, setTestResult] = useState<TestResult | null>(null);

  const handleTest = async () => {
    setTesting(true);
    setTestResult(null);
    try {
      const r = await fetch('/api/ai/test', {
        method: 'POST',
        headers: { 'content-type': 'application/json' },
        // Test the saved config by id; the server pulls the stored key.
        body: JSON.stringify({ useSavedKeyForId: cfg.id }),
      });
      setTestResult(await r.json());
    } catch (e) {
      setTestResult({ ok: false, error: (e as Error).message });
    } finally {
      setTesting(false);
    }
  };

  const providerMeta = PROVIDERS.find((p) => p.id === cfg.provider);

  return (
    <li
      className={`rounded border ${
        isActive ? 'border-emerald-700 bg-emerald-950/20' : 'border-zinc-800 bg-zinc-950'
      } p-3 space-y-2`}
    >
      <div className="flex items-start justify-between gap-3">
        <div className="min-w-0 flex-1">
          <div className="flex items-center gap-2">
            <span className="font-semibold text-white text-sm truncate">
              {cfg.name}
            </span>
            {isActive && (
              <span className="inline-flex items-center gap-1 text-[10px] uppercase tracking-wider text-emerald-400 font-bold">
                <Star className="w-3 h-3 fill-emerald-400" />
                Active
              </span>
            )}
          </div>
          <div className="text-xs text-zinc-500 mt-0.5">
            {providerMeta?.label ?? cfg.provider} · {cfg.model}
            {cfg.baseUrl && (
              <>
                {' · '}
                <code className="text-zinc-400">{cfg.baseUrl}</code>
              </>
            )}
            {providerMeta?.requiresKey && (
              <>
                {' · '}
                <span className={cfg.keyConfigured ? 'text-zinc-400' : 'text-amber-400'}>
                  {cfg.keyConfigured ? 'Key saved' : 'No key'}
                </span>
              </>
            )}
          </div>
        </div>
        <div className="flex items-center gap-1">
          {!isActive && (
            <button
              type="button"
              onClick={onActivate}
              className="px-2 py-1 text-[11px] uppercase tracking-wider rounded text-zinc-300 hover:bg-zinc-800"
              title="Make this the AI config that Generate uses"
            >
              Set active
            </button>
          )}
          <button
            type="button"
            onClick={handleTest}
            disabled={testing}
            className="px-2 py-1 text-[11px] uppercase tracking-wider rounded text-zinc-300 hover:bg-zinc-800 disabled:opacity-50"
          >
            {testing ? (
              <>
                <Loader2 className="inline w-3 h-3 animate-spin mr-1" />
                Testing
              </>
            ) : (
              'Test'
            )}
          </button>
          <button
            type="button"
            onClick={onEdit}
            className="px-2 py-1 text-[11px] uppercase tracking-wider rounded text-zinc-300 hover:bg-zinc-800"
          >
            {isEditing ? 'Cancel' : 'Edit'}
          </button>
          <button
            type="button"
            onClick={onDelete}
            className="p-1 text-red-400 hover:text-red-300"
            title="Delete this config"
          >
            <Trash2 className="w-3.5 h-3.5" />
          </button>
        </div>
      </div>

      {testResult && (
        <div
          className={`rounded px-2 py-1.5 border text-xs ${
            testResult.ok
              ? 'bg-emerald-900/40 border-emerald-800 text-emerald-300'
              : 'bg-red-900/50 border-red-800 text-red-400'
          }`}
        >
          {testResult.ok ? (
            <>
              ✓ Connected in {(testResult.ms / 1000).toFixed(1)}s
              {testResult.tokensIn != null &&
                ` · ${testResult.tokensIn} in / ${testResult.tokensOut ?? '?'} out`}
              <div className="mt-0.5 text-zinc-400">
                Sample reply: <span className="text-zinc-200">{testResult.sample}</span>
              </div>
            </>
          ) : (
            <>✗ {testResult.error}</>
          )}
        </div>
      )}

      {isEditing && (
        <div className="border-t border-zinc-800 pt-3">
          <ConfigForm
            initial={cfg}
            onCancel={onEdit}
            onCreated={onSaved}
          />
        </div>
      )}
    </li>
  );
}

interface ConfigFormProps {
  /** When set: editing this saved config (PATCH). Otherwise: creating new (POST). */
  initial?: SavedConfig;
  onCancel: () => void;
  onCreated: () => void;
}

/**
 * Add-or-edit form for a single AI config. Logic worth noting:
 *
 * - Model field is a dropdown of `MODEL_MENU[provider]` for major
 *   providers; falls through to free text for openai-compatible / ollama
 *   / "Other (type your own)".
 * - For Ollama: probes /api/ai/ollama/models on provider-or-baseUrl
 *   change and (a) pre-fills the URL if the default StartOS address
 *   responds, (b) replaces the model dropdown with the actual
 *   installed models.
 * - For Anthropic/OpenAI/Gemini: exposes a "Test draft" button that
 *   tests the in-progress form values without saving — handy for
 *   checking a key before committing.
 */
function ConfigForm({ initial, onCancel, onCreated }: ConfigFormProps) {
  const isEdit = !!initial;
  const [name, setName] = useState(initial?.name ?? '');
  const [provider, setProvider] = useState<ProviderId>(initial?.provider ?? 'claude');
  const [model, setModel] = useState(initial?.model ?? '');
  const [modelMode, setModelMode] = useState<'menu' | 'custom'>(
    initial && !MODEL_MENU[initial.provider]?.find((m) => m.id === initial.model)
      ? 'custom'
      : 'menu',
  );
  const [baseUrl, setBaseUrl] = useState(initial?.baseUrl ?? '');
  const [apiKey, setApiKey] = useState('');
  const [setActive, setSetActive] = useState(!isEdit); // new configs default to active
  const [showKey, setShowKey] = useState(false);
  const [saving, setSaving] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [testResult, setTestResult] = useState<TestResult | null>(null);
  const [testing, setTesting] = useState(false);

  // Ollama auto-detect.
  const [ollamaModels, setOllamaModels] = useState<{ name: string }[] | null>(null);
  const [ollamaProbing, setOllamaProbing] = useState(false);
  const [ollamaProbeError, setOllamaProbeError] = useState<string | null>(null);

  const meta = PROVIDERS.find((p) => p.id === provider);

  // Probe Ollama on provider switch (or baseUrl change while ollama).
  useEffect(() => {
    if (provider !== 'ollama') {
      setOllamaModels(null);
      setOllamaProbeError(null);
      return;
    }
    let cancelled = false;
    setOllamaProbing(true);
    setOllamaProbeError(null);
    const url = baseUrl
      ? `/api/ai/ollama/models?baseUrl=${encodeURIComponent(baseUrl)}`
      : '/api/ai/ollama/models';
    fetch(url)
      .then((r) => r.json())
      .then((b) => {
        if (cancelled) return;
        if (b.ok) {
          setOllamaModels(b.models ?? []);
          // Pre-fill URL if the user hadn't typed one yet.
          if (!baseUrl && b.baseUrl) setBaseUrl(b.baseUrl);
          // Pre-pick a model if there's exactly one and we're in create mode.
          if (!isEdit && !model && (b.models?.length ?? 0) === 1) {
            setModel(b.models[0].name);
          }
        } else {
          setOllamaModels(null);
          setOllamaProbeError(b.error ?? 'Probe failed');
        }
      })
      .catch((e) => {
        if (!cancelled) setOllamaProbeError((e as Error).message);
      })
      .finally(() => {
        if (!cancelled) setOllamaProbing(false);
      });
    return () => {
      cancelled = true;
    };
    // We deliberately depend on baseUrl too so changing the URL re-probes.
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [provider, baseUrl]);

  // Reset draft test result whenever the user changes any input — so the
  // green "✓ Connected" indicator never lingers from a previous attempt.
  useEffect(() => {
    setTestResult(null);
  }, [provider, model, baseUrl, apiKey]);

  const menu = MODEL_MENU[provider] ?? [];
  const showMenu = modelMode === 'menu' && menu.length > 0;

  const handleSave = async () => {
    setSaving(true);
    setError(null);
    try {
      const body: Record<string, unknown> = {
        name: name || undefined,
        provider,
        model,
        baseUrl: baseUrl || null,
      };
      if (apiKey) body.apiKey = apiKey;
      if (!isEdit) body.setActive = setActive;

      const url = isEdit ? `/api/ai/configs/${initial.id}` : '/api/ai/configs';
      const method = isEdit ? 'PATCH' : 'POST';
      const r = await fetch(url, {
        method,
        headers: { 'content-type': 'application/json' },
        body: JSON.stringify(body),
      });
      if (!r.ok) {
        const b = await r.json().catch(() => ({}));
        throw new Error(b.error ?? `HTTP ${r.status}`);
      }
      onCreated();
    } catch (e) {
      setError((e as Error).message);
    } finally {
      setSaving(false);
    }
  };

  const handleTestDraft = async () => {
    setTesting(true);
    setTestResult(null);
    try {
      const r = await fetch('/api/ai/test', {
        method: 'POST',
        headers: { 'content-type': 'application/json' },
        body: JSON.stringify({
          provider,
          model,
          baseUrl: baseUrl || null,
          apiKey: apiKey || null,
          // If we're editing and the user didn't change the key field,
          // borrow the saved key for the test.
          useSavedKeyForId: isEdit ? initial!.id : undefined,
        }),
      });
      setTestResult(await r.json());
    } catch (e) {
      setTestResult({ ok: false, error: (e as Error).message });
    } finally {
      setTesting(false);
    }
  };

  return (
    <div className="space-y-3 bg-zinc-900 border border-zinc-800 rounded p-3">
      <Field label="Name (optional)">
        <input
          value={name}
          onChange={(e) => setName(e.target.value)}
          placeholder="e.g. Local Ollama, Claude (work)"
          className={inputClass}
        />
      </Field>

      <Field label="Provider">
        <select
          value={provider}
          onChange={(e) => {
            setProvider(e.target.value as ProviderId);
            setModel(''); // reset on provider change
            setModelMode('menu');
          }}
          className={inputClass}
          disabled={isEdit}
        >
          {PROVIDERS.map((p) => (
            <option key={p.id} value={p.id}>
              {p.label}
            </option>
          ))}
        </select>
        {isEdit && (
          <p className="text-[11px] text-zinc-500 mt-1">
            Provider can&apos;t be changed; delete this config and add a new one.
          </p>
        )}
      </Field>

      {/* Ollama: replace the model dropdown with installed models if probe succeeded */}
      {provider === 'ollama' ? (
        <Field
          label={
            <>
              Model{' '}
              {ollamaProbing ? (
                <span className="text-zinc-500 normal-case font-normal">· probing…</span>
              ) : ollamaModels ? (
                <span className="text-emerald-400 normal-case font-normal">
                  · {ollamaModels.length} installed
                </span>
              ) : ollamaProbeError ? (
                <span className="text-amber-400 normal-case font-normal">
                  · could not reach Ollama (type a name)
                </span>
              ) : null}
            </>
          }
        >
          {ollamaModels && ollamaModels.length > 0 ? (
            <select
              value={model}
              onChange={(e) => setModel(e.target.value)}
              className={inputClass}
            >
              <option value="">— Pick an installed model —</option>
              {ollamaModels.map((m) => (
                <option key={m.name} value={m.name}>
                  {m.name}
                </option>
              ))}
            </select>
          ) : (
            <input
              value={model}
              onChange={(e) => setModel(e.target.value)}
              placeholder="llama3.1:8b · qwen2.5:14b · mistral:7b"
              className={inputClass}
            />
          )}
        </Field>
      ) : showMenu ? (
        <Field label="Model">
          <select
            value={model}
            onChange={(e) => {
              if (e.target.value === '__custom__') {
                setModelMode('custom');
                setModel('');
              } else {
                setModel(e.target.value);
              }
            }}
            className={inputClass}
          >
            <option value="">— Pick a model —</option>
            {menu.map((m) => (
              <option key={m.id} value={m.id}>
                {m.recommended ? '★ ' : ''}
                {m.label}
              </option>
            ))}
            <option value="__custom__">Other (type your own)</option>
          </select>
        </Field>
      ) : (
        <Field
          label={
            <>
              Model{' '}
              {provider !== 'openai-compatible' && menu.length > 0 && (
                <button
                  type="button"
                  onClick={() => setModelMode('menu')}
                  className="text-zinc-500 hover:text-zinc-300 normal-case font-normal text-[11px]"
                >
                  · use dropdown
                </button>
              )}
            </>
          }
        >
          <input
            value={model}
            onChange={(e) => setModel(e.target.value)}
            placeholder="exact model id"
            className={inputClass}
          />
        </Field>
      )}

      {meta?.requiresUrl && (
        <Field label="Base URL">
          <input
            value={baseUrl}
            onChange={(e) => setBaseUrl(e.target.value)}
            placeholder={
              meta.id === 'ollama'
                ? 'http://ollama.startos:11434'
                : 'https://your-gateway.example.com/v1'
            }
            className={inputClass}
          />
        </Field>
      )}

      {meta?.requiresKey && (
        <Field
          label={
            <>
              API key{' '}
              {isEdit && initial?.keyConfigured && !apiKey && (
                <span className="text-zinc-500 normal-case font-normal">
                  · key saved (leave blank to keep)
                </span>
              )}
            </>
          }
        >
          <div className="relative">
            <input
              type={showKey ? 'text' : 'password'}
              value={apiKey}
              onChange={(e) => setApiKey(e.target.value)}
              placeholder={
                isEdit && initial?.keyConfigured ? '••••••••  (saved)' : 'sk-...'
              }
              className={`${inputClass} pr-12`}
            />
            <button
              type="button"
              onClick={() => setShowKey(!showKey)}
              className="absolute right-3 top-2 text-xs text-zinc-500 hover:text-zinc-300"
            >
              {showKey ? 'hide' : 'show'}
            </button>
          </div>
          <p className="text-[11px] text-zinc-500 mt-1">
            Stored plaintext in /data/app.db on your StartOS host. Never sent
            anywhere except the provider you pick.
          </p>
        </Field>
      )}

      {!isEdit && (
        <label className="flex items-center gap-2 text-xs text-zinc-300">
          <input
            type="checkbox"
            checked={setActive}
            onChange={(e) => setSetActive(e.target.checked)}
          />
          Make this the active config
        </label>
      )}

      {error && (
        <div className="rounded bg-red-900/50 px-3 py-2 border border-red-800 text-xs text-red-400">
          {error}
        </div>
      )}
      {testResult && (
        <div
          className={`rounded px-3 py-2 border text-xs ${
            testResult.ok
              ? 'bg-emerald-900/40 border-emerald-800 text-emerald-300'
              : 'bg-red-900/50 border-red-800 text-red-400'
          }`}
        >
          {testResult.ok ? (
            <>
              ✓ Connected in {(testResult.ms / 1000).toFixed(1)}s
              {testResult.tokensIn != null &&
                ` · ${testResult.tokensIn} in / ${testResult.tokensOut ?? '?'} out`}
              <div className="mt-0.5 text-zinc-400">
                Sample reply: <span className="text-zinc-200">{testResult.sample}</span>
              </div>
            </>
          ) : (
            <>✗ {testResult.error}</>
          )}
        </div>
      )}

      <div className="flex items-center gap-2 pt-1">
        <button
          type="button"
          onClick={handleSave}
          disabled={saving || !provider || !model}
          className="px-4 py-2 rounded bg-white text-black font-bold text-xs uppercase tracking-wider hover:bg-gray-100 disabled:bg-zinc-700 disabled:text-zinc-500"
        >
          {saving ? (
            <>
              <Loader2 className="inline w-4 h-4 animate-spin mr-2" />
              Saving…
            </>
          ) : isEdit ? (
            'Save changes'
          ) : (
            'Add this config'
          )}
        </button>
        <button
          type="button"
          onClick={handleTestDraft}
          disabled={
            testing ||
            !provider ||
            !model ||
            (meta?.requiresUrl && !baseUrl) ||
            (meta?.requiresKey && !apiKey && !(isEdit && initial?.keyConfigured))
          }
          className="px-4 py-2 rounded border border-zinc-700 text-zinc-300 hover:bg-zinc-800 text-xs uppercase tracking-wider disabled:opacity-50"
          title="Send a tiny test prompt to verify these credentials"
        >
          {testing ? (
            <>
              <Loader2 className="inline w-3.5 h-3.5 animate-spin mr-2" />
              Testing…
            </>
          ) : (
            'Test draft'
          )}
        </button>
        <button
          type="button"
          onClick={onCancel}
          className="px-3 py-2 text-zinc-500 hover:text-zinc-200 text-xs uppercase tracking-wider"
        >
          Cancel
        </button>
      </div>
    </div>
  );
}

const inputClass =
  'w-full px-3 py-2 text-sm rounded border border-zinc-700 bg-zinc-800 text-white placeholder:text-zinc-500 focus:outline-none focus:ring-2 focus:ring-white/30';

function Field({
  label,
  children,
}: {
  label: React.ReactNode;
  children: React.ReactNode;
}) {
  return (
    <label className="block">
      <span className="text-[11px] font-semibold text-zinc-400 uppercase tracking-wider block mb-1">
        {label}
      </span>
      {children}
    </label>
  );
}
