'use client';

import { useEffect, useState } from 'react';
import { Save, Loader2, X, ArrowRight, Plus, Trash2 } from 'lucide-react';
import type { AiConsoleConfig, AiClass, PublicAiPreset } from '@/lib/ai/types';
import { DEFAULT_AI_CONSOLE_CONFIG } from '@/lib/ai/types';
import type { AiEntitlementState, MeteringEntry } from '@/lib/ai/entitlement';
import { apiFetch } from '@/lib/browser-navigation';
import { useAdminTabStore } from '@/stores/admin-tab-store';

type EntitlementResponse = AiEntitlementState & { recentUsage: MeteringEntry[] };

const CLASS_INFO: Record<AiClass, { name: string; desc: string }> = {
  local: { name: 'Local', desc: "Ollama on the user's own machine. Free, unmetered, never reaches this server." },
  server: { name: 'Server', desc: 'VNC-hosted. Entitlement-enforced, seat + usage tracked below.' },
  opencode: { name: 'OpenCode', desc: 'A locally-running OpenCode agent server. Holds its own provider credentials; nothing metered here.' },
  public: { name: 'Public (BYOK)', desc: "User's own API key, direct from their browser to the provider." },
};

function AllowlistEditor({
  values, onChange, placeholder,
}: { values: string[] | null; onChange: (next: string[] | null) => void; placeholder: string }) {
  const [draft, setDraft] = useState('');
  const restricted = values !== null;

  return (
    <>
      <div className="flex gap-3.5 px-4 pt-2.5 pb-0.5 text-xs">
        <label className="flex items-center gap-1.5 cursor-pointer text-muted-foreground">
          <input type="radio" checked={!restricted} onChange={() => onChange(null)} />
          Unrestricted (current)
        </label>
        <label className={`flex items-center gap-1.5 cursor-pointer ${restricted ? 'text-foreground font-medium' : 'text-muted-foreground'}`}>
          <input type="radio" checked={restricted} onChange={() => onChange(values ?? [])} />
          Restrict to selected
        </label>
      </div>
      {restricted && (
        <>
          <div className="flex flex-wrap gap-1.5 px-4 pt-2.5">
            {(values ?? []).map((v) => (
              <span key={v} className="inline-flex items-center gap-1.5 bg-muted border border-border rounded-full py-1 pl-3 pr-1.5 text-xs">
                {v}
                <button onClick={() => onChange((values ?? []).filter((x) => x !== v))} className="text-muted-foreground hover:text-foreground">
                  <X className="w-3 h-3" />
                </button>
              </span>
            ))}
          </div>
          <div className="flex gap-2 px-4 py-3">
            <input
              value={draft}
              onChange={(e) => setDraft(e.target.value)}
              placeholder={placeholder}
              className="flex-1 h-8 rounded border border-input bg-background px-2.5 text-xs"
              onKeyDown={(e) => {
                if (e.key === 'Enter' && draft.trim()) {
                  onChange([...(values ?? []), draft.trim()]);
                  setDraft('');
                }
              }}
            />
            <button
              onClick={() => { if (draft.trim()) { onChange([...(values ?? []), draft.trim()]); setDraft(''); } }}
              className="h-8 px-3 rounded border border-border bg-muted text-xs font-medium hover:bg-muted/70"
            >
              Add
            </button>
          </div>
        </>
      )}
    </>
  );
}

function newPresetId(): string {
  return `preset-${Math.random().toString(36).slice(2, 10)}`;
}

/**
 * The Paperclip-style env-var-key picker (decision 2026-08-07): an admin
 * names a preset and an env var; the actual secret value is never entered
 * here — it's whatever ops has set in the server's real environment. This is
 * what lets a user in Settings pick a provider from a dropdown instead of
 * pasting a key.
 */
function PublicPresetsEditor({
  presets, onChange,
}: { presets: PublicAiPreset[]; onChange: (next: PublicAiPreset[]) => void }) {
  const [name, setName] = useState('');
  const [baseUrl, setBaseUrl] = useState('https://api.deepseek.com');
  const [model, setModel] = useState('');
  const [envVar, setEnvVar] = useState('');

  const canAdd = name.trim() && baseUrl.trim() && model.trim() && envVar.trim();

  function addPreset() {
    if (!canAdd) return;
    onChange([...presets, { id: newPresetId(), name: name.trim(), baseUrl: baseUrl.trim(), model: model.trim(), apiKeyEnvVar: envVar.trim() }]);
    setName('');
    setBaseUrl('https://api.deepseek.com');
    setModel('');
    setEnvVar('');
  }

  return (
    <>
      {presets.length > 0 && (
        <div className="divide-y divide-border">
          {presets.map((p) => (
            <div key={p.id} className="px-4 py-2.5 flex items-center justify-between gap-3">
              <div className="min-w-0">
                <span className="text-sm font-medium">{p.name}</span>
                <p className="text-xs text-muted-foreground truncate">
                  {p.model} · {p.baseUrl} · reads <code className="text-[11px]">{p.apiKeyEnvVar}</code>
                </p>
              </div>
              <button
                onClick={() => onChange(presets.filter((x) => x.id !== p.id))}
                className="shrink-0 text-muted-foreground hover:text-destructive"
                aria-label={`Remove ${p.name}`}
              >
                <Trash2 className="w-3.5 h-3.5" />
              </button>
            </div>
          ))}
        </div>
      )}
      <div className="px-4 py-3 flex flex-col gap-2 border-t border-border">
        <div className="flex gap-2 flex-wrap">
          <input value={name} onChange={(e) => setName(e.target.value)} placeholder="Name, e.g. DeepSeek (org)"
            className="flex-1 min-w-[160px] h-8 rounded border border-input bg-background px-2.5 text-xs" />
          <input value={model} onChange={(e) => setModel(e.target.value)} placeholder="Model, e.g. deepseek-chat"
            className="flex-1 min-w-[160px] h-8 rounded border border-input bg-background px-2.5 text-xs" />
        </div>
        <div className="flex gap-2 flex-wrap">
          <input value={baseUrl} onChange={(e) => setBaseUrl(e.target.value)} placeholder="API base URL"
            className="flex-1 min-w-[200px] h-8 rounded border border-input bg-background px-2.5 text-xs" />
          <input value={envVar} onChange={(e) => setEnvVar(e.target.value)} placeholder="Env var, e.g. DEEPSEEK_API_KEY"
            className="flex-1 min-w-[200px] h-8 rounded border border-input bg-background px-2.5 text-xs" />
          <button onClick={addPreset} disabled={!canAdd}
            className="h-8 px-3 rounded border border-border bg-muted text-xs font-medium hover:bg-muted/70 disabled:opacity-50 inline-flex items-center gap-1.5">
            <Plus className="w-3 h-3" /> Add
          </button>
        </div>
        <p className="text-xs text-muted-foreground">
          Only the env var <em>name</em> is stored here — provision the actual key as a real environment variable on
          the server (k8s secret, .env, Electron packaging). This app never sees or stores the value.
        </p>
      </div>
    </>
  );
}

export function AiPolicyTab() {
  const setActiveTab = useAdminTabStore((s) => s.setActiveTab);
  const [config, setConfig] = useState<AiConsoleConfig>({ ...DEFAULT_AI_CONSOLE_CONFIG });
  const [entitlement, setEntitlement] = useState<EntitlementResponse | null>(null);
  const [serverModels, setServerModels] = useState<string[]>([]);
  const [loading, setLoading] = useState(true);
  const [saving, setSaving] = useState(false);
  const [dirty, setDirty] = useState(false);
  const [message, setMessage] = useState<{ type: 'success' | 'error'; text: string } | null>(null);

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

  async function load() {
    setLoading(true);
    try {
      const [policyRes, entitlementRes, modelsRes] = await Promise.all([
        apiFetch('/api/admin/ai/policy'),
        apiFetch('/api/admin/ai/entitlement'),
        apiFetch('/api/ai/server/models').catch(() => null),
      ]);
      if (policyRes.ok) setConfig(await policyRes.json());
      if (entitlementRes.ok) setEntitlement(await entitlementRes.json());
      if (modelsRes?.ok) {
        const data = await modelsRes.json();
        setServerModels(data.models ?? []);
      }
    } finally {
      setLoading(false);
    }
  }

  function update(patch: Partial<AiConsoleConfig>) {
    setConfig((prev) => ({ ...prev, ...patch }));
    setDirty(true);
    setMessage(null);
  }

  function toggleClass(cls: AiClass) {
    const current = config.classesEnabled[cls] !== false;
    update({ classesEnabled: { ...config.classesEnabled, [cls]: !current } });
  }

  async function handleSave() {
    setSaving(true);
    setMessage(null);
    const res = await apiFetch('/api/admin/ai/policy', {
      method: 'PUT',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(config),
    });
    if (res.ok) {
      setConfig(await res.json());
      setDirty(false);
      setMessage({ type: 'success', text: 'Saved.' });
    } else {
      const data = await res.json().catch(() => ({}));
      setMessage({ type: 'error', text: data.error || 'Failed to save' });
    }
    setSaving(false);
  }

  async function setSeatTotal(total: number) {
    const res = await apiFetch('/api/admin/ai/entitlement', {
      method: 'PUT',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ seatsTotal: total }),
    });
    if (res.ok) {
      const data = await res.json();
      setEntitlement((prev) => (prev ? { ...prev, ...data } : prev));
    }
  }

  async function revokeSeat(username: string) {
    const res = await apiFetch('/api/admin/ai/entitlement', {
      method: 'PUT',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ revokeUsername: username }),
    });
    if (res.ok) {
      const data = await res.json();
      setEntitlement((prev) => (prev ? { ...prev, ...data } : prev));
    }
  }

  if (loading) {
    return <div className="flex items-center justify-center py-12 text-muted-foreground text-sm">Loading...</div>;
  }

  const serverInfraAvailable = serverModels.length > 0 || entitlement !== null;
  const usageToday = (entitlement?.recentUsage ?? []).filter((u) => u.timestamp.slice(0, 10) === new Date().toISOString().slice(0, 10));
  const tokensToday = usageToday.reduce((sum, u) => sum + u.promptTokens + u.completionTokens, 0);
  const avgLatency = usageToday.length ? Math.round(usageToday.reduce((sum, u) => sum + u.latencyMs, 0) / usageToday.length) : 0;

  return (
    <div className="space-y-6">
      <div className="flex flex-wrap items-start justify-between gap-3">
        <div className="min-w-0">
          <h1 className="text-2xl font-semibold text-foreground">AI</h1>
          <p className="text-sm text-muted-foreground mt-1">Provider classes, allow-lists, seats, usage, and BYOK consent for the AI Assistant.</p>
        </div>
        {dirty && (
          <button onClick={handleSave} disabled={saving}
            className="inline-flex items-center gap-2 h-9 px-4 rounded-md bg-primary text-primary-foreground text-sm font-medium hover:bg-primary/90 disabled:opacity-50 transition-all shadow-sm">
            {saving ? <Loader2 className="w-4 h-4 animate-spin" /> : <Save className="w-4 h-4" />}
            Save changes
          </button>
        )}
      </div>

      {message && (
        <div className={`text-sm rounded-md px-3 py-2 ${message.type === 'success' ? 'bg-emerald-50 text-emerald-700 dark:bg-emerald-950/30 dark:text-emerald-300' : 'bg-destructive/10 text-destructive'}`}>
          {message.text}
        </div>
      )}

      <button onClick={() => setActiveTab('policy')}
        className="w-full flex items-center gap-2 text-xs text-muted-foreground bg-muted border border-border rounded-md px-3.5 py-2.5 hover:bg-muted/70 transition-colors text-left">
        <span>The master AI Assistant on/off switch lives in</span>
        <span className="text-primary font-medium inline-flex items-center gap-1">Policy → Feature Gates <ArrowRight className="w-3 h-3" /></span>
      </button>

      <div className="border border-border rounded-lg">
        <div className="px-4 py-3 border-b border-border bg-muted/30">
          <h2 className="text-sm font-medium text-foreground">Provider classes</h2>
          <p className="text-xs text-muted-foreground mt-0.5">Which of the three AI classes users can reach at all.</p>
        </div>
        <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-3 p-4">
          {(['local', 'server', 'opencode', 'public'] as AiClass[]).map((cls) => {
            const enabled = config.classesEnabled[cls] !== false;
            const disabledByInfra = cls === 'server' && !serverInfraAvailable;
            return (
              <div key={cls} className={`border border-border rounded-md p-3.5 ${disabledByInfra ? 'opacity-55' : ''}`}>
                <div className="flex items-center justify-between mb-1.5">
                  <span className="text-sm font-semibold">{CLASS_INFO[cls].name}</span>
                  <button
                    onClick={() => !disabledByInfra && toggleClass(cls)}
                    disabled={disabledByInfra}
                    className={`shrink-0 relative inline-flex h-5 w-9 items-center rounded-full transition-colors ${enabled && !disabledByInfra ? 'bg-primary' : 'bg-muted-foreground/25 dark:bg-muted-foreground/50'} ${disabledByInfra ? 'cursor-not-allowed' : ''}`}>
                    <span className={`inline-block h-3.5 w-3.5 transform rounded-full bg-background shadow transition-transform ${enabled && !disabledByInfra ? 'translate-x-[18px]' : 'translate-x-[3px]'}`} />
                  </button>
                </div>
                <p className="text-xs text-muted-foreground">{CLASS_INFO[cls].desc}</p>
                {disabledByInfra && <p className="text-xs text-amber-600 dark:text-amber-400 mt-1.5">Not configured (AI_SERVER_BASE_URL unset)</p>}
              </div>
            );
          })}
        </div>
      </div>

      <div className="border border-border rounded-lg">
        <div className="px-4 py-3 border-b border-border bg-muted/30">
          <h2 className="text-sm font-medium text-foreground">Server — model allow-list</h2>
          <p className="text-xs text-muted-foreground mt-0.5">Restrict which Ollama models users may select for the Server class. Also enforced on every chat call, not just the picker.</p>
        </div>
        <AllowlistEditor
          values={config.serverModelAllowlist}
          onChange={(v) => update({ serverModelAllowlist: v })}
          placeholder={serverModels.length ? `e.g. ${serverModels[0]}` : 'e.g. qwen2.5:32b'}
        />
      </div>

      <div className="border border-border rounded-lg">
        <div className="px-4 py-3 border-b border-border bg-muted/30">
          <h2 className="text-sm font-medium text-foreground">Public (BYOK) — provider allow-list</h2>
          <p className="text-xs text-muted-foreground mt-0.5">Restrict which base URLs users may point a bring-your-own-key profile at. Checked client-side at save time — advisory, not a network boundary.</p>
        </div>
        <AllowlistEditor
          values={config.publicProviderAllowlist}
          onChange={(v) => update({ publicProviderAllowlist: v })}
          placeholder="e.g. https://api.openai.com"
        />
      </div>

      <div className="border border-border rounded-lg">
        <div className="px-4 py-3 border-b border-border bg-muted/30">
          <h2 className="text-sm font-medium text-foreground">Public — org-managed presets</h2>
          <p className="text-xs text-muted-foreground mt-0.5">
            Paperclip-style: publish a provider by name instead of making every user paste their own key. Users pick
            one of these in Settings with no key field at all — the server resolves the named env var at request time.
          </p>
        </div>
        <PublicPresetsEditor presets={config.publicPresets} onChange={(v) => update({ publicPresets: v })} />
      </div>

      <div className="border border-border rounded-lg">
        <div className="px-4 py-3 border-b border-border bg-muted/30">
          <h2 className="text-sm font-medium text-foreground">Entitlement &amp; seats</h2>
          <p className="text-xs text-muted-foreground mt-0.5">Server class only. First successful use auto-assigns a seat.</p>
        </div>
        <div className="px-4 py-3 flex items-center gap-3 border-b border-border">
          <span className="text-sm flex-1">Seats licensed</span>
          <input
            type="number" min={0}
            value={entitlement?.seatsTotal ?? 0}
            onChange={(e) => setSeatTotal(Math.max(0, Number.parseInt(e.target.value, 10) || 0))}
            className="w-20 h-8 rounded border border-input bg-background px-2 text-sm text-center"
          />
          <span className="text-xs text-muted-foreground">{entitlement?.assignedTo.length ?? 0} of {entitlement?.seatsTotal ?? 0} assigned</span>
        </div>
        <div className="divide-y divide-border">
          {(entitlement?.assignedTo ?? []).length === 0 && (
            <div className="px-4 py-3 text-xs text-muted-foreground">No seats assigned yet.</div>
          )}
          {(entitlement?.assignedTo ?? []).map((username) => (
            <div key={username} className="px-4 py-2.5 flex items-center justify-between gap-3">
              <span className="text-sm">{username}</span>
              <button onClick={() => revokeSeat(username)}
                className="text-xs font-medium text-destructive border border-border rounded px-2.5 py-1 hover:bg-destructive/10">
                Revoke
              </button>
            </div>
          ))}
        </div>
      </div>

      <div className="border border-border rounded-lg">
        <div className="px-4 py-3 border-b border-border bg-muted/30">
          <h2 className="text-sm font-medium text-foreground">Usage</h2>
          <p className="text-xs text-muted-foreground mt-0.5">Last 200 metered calls. Read-only.</p>
        </div>
        <div className="flex gap-6 px-4 py-3 border-b border-border flex-wrap">
          <div><span className="text-lg font-semibold tabular-nums block">{usageToday.length}</span><span className="text-[11px] uppercase tracking-wide text-muted-foreground">Calls today</span></div>
          <div><span className="text-lg font-semibold tabular-nums block">{tokensToday.toLocaleString()}</span><span className="text-[11px] uppercase tracking-wide text-muted-foreground">Tokens today</span></div>
          <div><span className="text-lg font-semibold tabular-nums block">{avgLatency}ms</span><span className="text-[11px] uppercase tracking-wide text-muted-foreground">Avg latency</span></div>
        </div>
        <div className="overflow-x-auto">
          <table className="w-full text-xs">
            <thead>
              <tr className="text-muted-foreground uppercase text-[10px] tracking-wide">
                <th className="text-left px-4 py-2 font-medium">Time</th>
                <th className="text-left px-4 py-2 font-medium">User</th>
                <th className="text-left px-4 py-2 font-medium">Model</th>
                <th className="text-left px-4 py-2 font-medium">Prompt tok</th>
                <th className="text-left px-4 py-2 font-medium">Compl. tok</th>
                <th className="text-left px-4 py-2 font-medium">Latency</th>
              </tr>
            </thead>
            <tbody className="divide-y divide-border">
              {(entitlement?.recentUsage ?? []).length === 0 && (
                <tr><td colSpan={6} className="px-4 py-3 text-muted-foreground">No usage recorded yet.</td></tr>
              )}
              {[...(entitlement?.recentUsage ?? [])].reverse().slice(0, 50).map((u, i) => (
                <tr key={i} className="tabular-nums">
                  <td className="px-4 py-2">{new Date(u.timestamp).toLocaleTimeString()}</td>
                  <td className="px-4 py-2">{u.username}</td>
                  <td className="px-4 py-2">{u.model}</td>
                  <td className="px-4 py-2">{u.promptTokens}</td>
                  <td className="px-4 py-2">{u.completionTokens}</td>
                  <td className="px-4 py-2">{u.latencyMs}ms</td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </div>

      <div className="border border-border rounded-lg">
        <div className="px-4 py-3 border-b border-border bg-muted/30">
          <h2 className="text-sm font-medium text-foreground">Retrieval &amp; consent</h2>
          <p className="text-xs text-muted-foreground mt-0.5">Mail-content augmentation and the BYOK consent prompt.</p>
        </div>
        <div className="px-4 py-3 flex items-center justify-between gap-4 border-b border-border">
          <div>
            <div className="text-sm">Retrieval leg</div>
            <p className="text-xs text-muted-foreground mt-0.5">Send recent mail content to the Server class's embedding model to answer questions grounded in the user's own mail.</p>
          </div>
          <button onClick={() => update({ retrievalEnabled: !config.retrievalEnabled })}
            className={`shrink-0 relative inline-flex h-5 w-9 items-center rounded-full transition-colors ${config.retrievalEnabled ? 'bg-primary' : 'bg-muted-foreground/25 dark:bg-muted-foreground/50'}`}>
            <span className={`inline-block h-3.5 w-3.5 transform rounded-full bg-background shadow transition-transform ${config.retrievalEnabled ? 'translate-x-[18px]' : 'translate-x-[3px]'}`} />
          </button>
        </div>
        <div className="px-4 py-3.5 space-y-2">
          <label className="text-sm block">Consent text (shown once per version, before first BYOK/Public use)</label>
          <textarea
            value={config.consent?.text ?? ''}
            onChange={(e) => update({ consent: { version: config.consent?.version ?? '1', text: e.target.value } })}
            className="w-full min-h-20 rounded border border-input bg-background px-2.5 py-2 text-xs"
            placeholder="Using a bring-your-own-key provider sends your question — and, if retrieval is on, related excerpts from your mail — to that provider's servers, outside this organisation. Continue?"
          />
        </div>
        <div className="px-4 py-3 flex items-center gap-2.5 flex-wrap">
          <span className="text-sm">Version</span>
          <input
            value={config.consent?.version ?? ''}
            onChange={(e) => update({ consent: { version: e.target.value, text: config.consent?.text ?? '' } })}
            className="w-20 h-8 rounded border border-input bg-background px-2 text-xs text-center"
          />
          <button
            onClick={() => update({ consent: { version: String(Number.parseInt(config.consent?.version || '0', 10) + 1), text: config.consent?.text ?? '' } })}
            className="h-8 px-3 rounded border border-border bg-muted text-xs font-medium hover:bg-muted/70">
            Bump version (re-prompt everyone)
          </button>
        </div>
      </div>
    </div>
  );
}
