'use client';

import { useCallback, useEffect, useMemo, useState } from 'react';
import { RefreshCw, CheckCircle, AlertTriangle, Loader2, Plus, Trash2, Sparkles, X } from 'lucide-react';
import { SettingsSection, SettingItem, ToggleSwitch, RadioGroup, Select } from './settings-section';
import { Button } from '@/components/ui/button';
import { apiFetch } from '@/lib/browser-navigation';
import { useAccountStore } from '@/stores/account-store';
import { DEFAULT_AI_POLICY, type AiPolicy } from '@/lib/ai/types';
import { supportsLocalLlm, localLlmNeedsCorsSetup } from '@/lib/platform-capabilities';
import { getAiApiKey, setAiApiKey, clearAiApiKey } from '@/lib/ai/key-store';
import {
  loadAiSettings, saveAiSettings, createProfile, presetActiveId, presetIdFromActiveId,
  type AiLocalSettings,
} from '@/lib/ai/local-settings';
import { ensureDefaultProvider } from '@/lib/ai/auto-provision';
import {
  discoverLocalOllama,
  recommendDefaultModel,
  largestModel,
  isLocalDiscoveryDismissed,
  dismissLocalDiscovery,
  type LocalDiscoveryResult,
} from '@/lib/ai/local-discovery';
import {
  askMail,
  listLocalModels,
  listServerModels,
  listOpencodeModels,
  listOpencodeProviders,
  addOpencodeProvider,
  removeOpencodeProvider,
  type OpencodeProviderOption,
  type OpencodeModelOption,
  testLocalConnection,
  type AskResult,
} from '@/lib/ai/local-client';

const inputClass =
  'px-3 py-1.5 text-sm rounded-md bg-muted border border-border text-foreground focus:outline-none focus:ring-2 focus:ring-ring transition-colors duration-150 flex-1 min-w-[220px]';

/**
 * Decisions recorded 2026-08-05 (see lib/ai/types.ts, lib/ai/entitlement.ts):
 * `local` (loopback Ollama) ships free, no entitlement check. `server`
 * (centrally-hosted, proxied through this app's own backend) is real and
 * entitlement-enforced — every call re-checks a licensed seat server-side.
 * `public` (BYOK) supports several named provider profiles, picked case by
 * case per question, and is explicitly unmonitored for now.
 */
export function AiAssistantSettings() {
  const [policy, setPolicy] = useState<AiPolicy>(DEFAULT_AI_POLICY);
  const [policyLoading, setPolicyLoading] = useState(true);
  const [settings, setSettings] = useState<AiLocalSettings>(() => loadAiSettings());
  // The index is written under the ACTIVE account's cookie slot, so retrieval
  // must read the same one — see fetchLocalLeg in lib/ai/local-client.ts.
  const activeSlot = useAccountStore((s) => s.accounts.find((a) => a.id === s.activeAccountId)?.cookieSlot);

  useEffect(() => {
    let cancelled = false;
    (async () => {
      try {
        const res = await apiFetch('/api/ai/policy');
        if (res.ok && !cancelled) {
          const loadedPolicy = (await res.json()) as AiPolicy;
          setPolicy(loadedPolicy);
          // Zero-config default (lib/ai/auto-provision.ts) — a no-op once a
          // provider is already chosen, so this is safe to run on every
          // visit to this pane, not just first-run.
          const next = await ensureDefaultProvider(loadedPolicy);
          // Separately, once an admin has published at least one org-managed
          // preset, make IT the default "Answer with" pick too — pasting a
          // personal key should be the fallback a user reaches for, not the
          // thing they have to do to get any answer at all.
          if (!next.activeProfileId && loadedPolicy.publicPresets[0]) {
            next.activeProfileId = presetActiveId(loadedPolicy.publicPresets[0].id);
            saveAiSettings(next);
          }
          if (!cancelled) setSettings(next);
        }
      } finally {
        if (!cancelled) setPolicyLoading(false);
      }
    })();
    return () => {
      cancelled = true;
    };
  }, []);

  const update = useCallback(<K extends keyof AiLocalSettings>(key: K, value: AiLocalSettings[K]) => {
    setSettings((prev) => {
      const next = { ...prev, [key]: value };
      saveAiSettings(next);
      return next;
    });
  }, []);

  const canUseLocal = supportsLocalLlm() && policy.entitlement.classes.includes('local');
  const canUseServer = policy.entitlement.classes.includes('server');
  const canUsePublic = policy.entitlement.classes.includes('public');
  const canUseOpencode = policy.entitlement.classes.includes('opencode');

  // ── Local provider ──
  const [localModels, setLocalModels] = useState<string[]>([]);
  const [refreshingLocal, setRefreshingLocal] = useState(false);
  const [testStatus, setTestStatus] = useState<'idle' | 'testing' | 'ok' | 'error'>('idle');
  const [testError, setTestError] = useState<string | null>(null);

  const refreshLocalModels = useCallback(async () => {
    setRefreshingLocal(true);
    try {
      const models = await listLocalModels(settings.localBaseUrl);
      setLocalModels(models);
      if (!settings.localModel && models[0]) update('localModel', models[0]);
    } catch {
      setLocalModels([]);
    } finally {
      setRefreshingLocal(false);
    }
  }, [settings.localBaseUrl, settings.localModel, update]);

  const runTestConnection = useCallback(async () => {
    setTestStatus('testing');
    setTestError(null);
    const result = await testLocalConnection(settings.localBaseUrl);
    if (result.ok) {
      setTestStatus('ok');
    } else {
      setTestStatus('error');
      setTestError(result.error ?? 'Connection failed');
    }
  }, [settings.localBaseUrl]);

  // ── Local discovery — proactively find an already-running Ollama and
  // offer a one-click connect, rather than making the user hunt down and
  // type a base URL + model name by hand. ──
  const [discovery, setDiscovery] = useState<LocalDiscoveryResult | null>(null);
  const [discoveryDismissed, setDiscoveryDismissed] = useState(true);

  useEffect(() => {
    setDiscoveryDismissed(isLocalDiscoveryDismissed());
  }, []);

  useEffect(() => {
    if (!canUseLocal || discoveryDismissed || settings.localModel) return;
    let cancelled = false;
    (async () => {
      const result = await discoverLocalOllama();
      if (!cancelled) setDiscovery(result);
    })();
    return () => {
      cancelled = true;
    };
  }, [canUseLocal, discoveryDismissed, settings.localModel]);

  const connectDiscoveredLocal = useCallback(() => {
    if (!discovery) return;
    const recommended = recommendDefaultModel(discovery.models) ?? discovery.models[0]?.name ?? null;
    if (!recommended) return;
    setSettings((prev) => {
      const next: AiLocalSettings = { ...prev, provider: 'local', localBaseUrl: discovery.baseUrl, localModel: recommended };
      saveAiSettings(next);
      return next;
    });
    setLocalModels(discovery.models.filter((m) => m.capabilities.includes('completion')).map((m) => m.name));
    setDiscovery(null);
  }, [discovery]);

  const dismissDiscoveryBanner = useCallback(() => {
    dismissLocalDiscovery();
    setDiscoveryDismissed(true);
    setDiscovery(null);
  }, []);

  // ── OpenCode provider — a locally-running `opencode serve`. No key to
  // manage (opencode holds provider auth itself) and a real model list, which
  // is why this is its own class rather than another BYOK profile. ──
  const [opencodeModels, setOpencodeModels] = useState<OpencodeModelOption[]>([]);
  const [refreshingOpencode, setRefreshingOpencode] = useState(false);
  const [opencodeError, setOpencodeError] = useState<string | null>(null);

  // ── OpenCode provider management — "add any LLM OpenCode supports" from
  // inside this app, not only whatever its own CLI already authenticated. ──
  const [opencodeProviders, setOpencodeProviders] = useState<OpencodeProviderOption[]>([]);
  const [loadingProviders, setLoadingProviders] = useState(false);
  const [providerSearch, setProviderSearch] = useState('');
  const [addingProviderId, setAddingProviderId] = useState<string | null>(null);
  const [newProviderKey, setNewProviderKey] = useState('');
  const [providerBusyId, setProviderBusyId] = useState<string | null>(null);
  const [providerActionError, setProviderActionError] = useState<string | null>(null);
  const [showProviderManager, setShowProviderManager] = useState(false);

  const refreshOpencodeProviders = useCallback(async () => {
    setLoadingProviders(true);
    setProviderActionError(null);
    try {
      setOpencodeProviders(await listOpencodeProviders());
    } catch (err) {
      setProviderActionError(err instanceof Error ? err.message : String(err));
    } finally {
      setLoadingProviders(false);
    }
  }, []);

  const handleAddProvider = useCallback(async (providerId: string) => {
    if (!newProviderKey.trim()) return;
    setProviderBusyId(providerId);
    setProviderActionError(null);
    try {
      await addOpencodeProvider(providerId, newProviderKey.trim());
      setAddingProviderId(null);
      setNewProviderKey('');
      await refreshOpencodeProviders();
    } catch (err) {
      setProviderActionError(err instanceof Error ? err.message : String(err));
    } finally {
      setProviderBusyId(null);
    }
  }, [newProviderKey, refreshOpencodeProviders]);

  const handleRemoveProvider = useCallback(async (providerId: string) => {
    setProviderBusyId(providerId);
    setProviderActionError(null);
    try {
      await removeOpencodeProvider(providerId);
      await refreshOpencodeProviders();
    } catch (err) {
      setProviderActionError(err instanceof Error ? err.message : String(err));
    } finally {
      setProviderBusyId(null);
    }
  }, [refreshOpencodeProviders]);

  const refreshOpencodeModels = useCallback(async () => {
    setRefreshingOpencode(true);
    setOpencodeError(null);
    try {
      const models = await listOpencodeModels();
      setOpencodeModels(models);
      if (!settings.opencodeModel && models[0]) update('opencodeModel', models[0].ref);
    } catch (err) {
      setOpencodeModels([]);
      setOpencodeError(err instanceof Error ? err.message : String(err));
    } finally {
      setRefreshingOpencode(false);
    }
  }, [settings.opencodeModel, update]);

  // ── Server provider ──
  const [serverModels, setServerModels] = useState<string[]>([]);
  const [refreshingServer, setRefreshingServer] = useState(false);
  const [serverError, setServerError] = useState<string | null>(null);
  const [seatNotice, setSeatNotice] = useState<string | null>(null);

  const refreshServerModels = useCallback(async () => {
    setRefreshingServer(true);
    setServerError(null);
    try {
      const models = await listServerModels();
      setServerModels(models);
      if (!settings.serverModel && models[0]) update('serverModel', models[0]);
    } catch (err) {
      setServerModels([]);
      setServerError(err instanceof Error ? err.message : String(err));
    } finally {
      setRefreshingServer(false);
    }
  }, [settings.serverModel, update]);

  // ── Public provider — several named profiles, one picked per question ──
  const [newProfileName, setNewProfileName] = useState('');
  const [newProfileBaseUrl, setNewProfileBaseUrl] = useState('https://openrouter.ai/api/v1');
  const [newProfileModel, setNewProfileModel] = useState('');
  const [newProfileKey, setNewProfileKey] = useState('');
  const [profileError, setProfileError] = useState<string | null>(null);

  const addProfile = useCallback(() => {
    if (!newProfileName || !newProfileBaseUrl || !newProfileModel || !newProfileKey) return;
    setProfileError(null);
    // Admin allow-list (docs/ADMIN-AI-POLICY-CONSOLE-SPEC.md §6.1) — advisory,
    // client-side only, checked here at save time.
    const allowlist = policy.publicProviderAllowlist;
    if (allowlist && !allowlist.some((prefix) => newProfileBaseUrl.startsWith(prefix))) {
      setProfileError(`This base URL isn't on the admin-approved list (${allowlist.join(', ')}).`);
      return;
    }
    const profile = createProfile(newProfileName, newProfileBaseUrl, newProfileModel);
    setAiApiKey(profile.id, newProfileKey);
    update('publicProfiles', [...settings.publicProfiles, profile]);
    if (!settings.activeProfileId) update('activeProfileId', profile.id);
    setNewProfileName('');
    setNewProfileBaseUrl('https://openrouter.ai/api/v1');
    setNewProfileModel('');
    setNewProfileKey('');
  }, [newProfileName, newProfileBaseUrl, newProfileModel, newProfileKey, settings.publicProfiles, settings.activeProfileId, update, policy.publicProviderAllowlist]);

  const removeProfile = useCallback(
    (id: string) => {
      clearAiApiKey(id);
      const remaining = settings.publicProfiles.filter((p) => p.id !== id);
      update('publicProfiles', remaining);
      if (settings.activeProfileId === id) update('activeProfileId', remaining[0]?.id ?? null);
    },
    [settings.publicProfiles, settings.activeProfileId, update],
  );

  // ── Ask ──
  const [question, setQuestion] = useState('');
  const [asking, setAsking] = useState(false);
  const [askResult, setAskResult] = useState<AskResult | null>(null);
  const [askError, setAskError] = useState<string | null>(null);

  const activePresetId = presetIdFromActiveId(settings.activeProfileId);
  const activeProfile = activePresetId ? null : settings.publicProfiles.find((p) => p.id === settings.activeProfileId) ?? null;
  const activePublicSelection = !!activeProfile || (!!activePresetId && policy.publicPresets.some((p) => p.id === activePresetId));

  const canAsk =
    question.trim().length > 0 &&
    (settings.provider === 'local'
      ? canUseLocal && !!settings.localModel
      : settings.provider === 'server'
        ? canUseServer && !!settings.serverModel
        : settings.provider === 'opencode'
          ? canUseOpencode && !!settings.opencodeModel
          : settings.provider === 'public'
            ? canUsePublic && activePublicSelection && settings.publicConsentAccepted
            : false);

  const runAsk = useCallback(async () => {
    setAsking(true);
    setAskError(null);
    setAskResult(null);
    setSeatNotice(null);
    try {
      const key = activeProfile ? getAiApiKey(activeProfile.id) : null;
      const result = await askMail(question.trim(), {
        provider: settings.provider as 'local' | 'server' | 'public' | 'opencode',
        localBaseUrl: settings.localBaseUrl,
        localModel: settings.localModel,
        serverModel: settings.serverModel,
        opencodeModel: settings.opencodeModel,
        slot: activeSlot,
        publicProfile: activeProfile && key ? { baseUrl: activeProfile.baseUrl, model: activeProfile.model, apiKey: key } : null,
        publicPresetId: activePresetId,
      });
      setAskResult(result);
      if (result.seatJustAssigned) {
        setSeatNotice('A licensed seat on the server-hosted class was just assigned to your account.');
      }
    } catch (err) {
      setAskError(err instanceof Error ? err.message : String(err));
    } finally {
      setAsking(false);
    }
  }, [question, settings, activeProfile, activePresetId, activeSlot]);

  const providerOptions = useMemo(
    () => [
      ...(canUseLocal ? [{ value: 'local', label: 'Local (Ollama)' }] : []),
      ...(canUseServer ? [{ value: 'server', label: 'Server (VNC-hosted)' }] : []),
      ...(canUseOpencode ? [{ value: 'opencode', label: 'OpenCode (local agent)' }] : []),
      ...(canUsePublic ? [{ value: 'public', label: 'Public (your API keys)' }] : []),
    ],
    [canUseLocal, canUseServer, canUsePublic, canUseOpencode],
  );

  if (policyLoading) {
    return (
      <div className="flex items-center gap-2 text-sm text-muted-foreground">
        <Loader2 className="w-3.5 h-3.5 animate-spin" /> Loading…
      </div>
    );
  }

  const discoveryRecommended = discovery ? recommendDefaultModel(discovery.models) : null;
  const discoveryLargest = discovery ? largestModel(discovery.models) : null;

  return (
    <div className="space-y-6">
      {discovery && discoveryRecommended && (
        <div className="flex items-start gap-3 rounded-lg border border-primary/30 bg-primary/5 p-4">
          <Sparkles className="w-5 h-5 mt-0.5 text-primary shrink-0" />
          <div className="flex-1 min-w-0 space-y-2">
            <p className="text-sm font-medium text-foreground">Local AI found on this machine</p>
            <p className="text-sm text-muted-foreground">
              Ollama is running at {discovery.baseUrl} with {discovery.models.length} model{discovery.models.length === 1 ? '' : 's'} installed.
              Recommended for quick answers: <span className="font-medium text-foreground">{discoveryRecommended}</span>.
              {discoveryLargest && discoveryLargest !== discoveryRecommended && (
                <> Also available for higher-quality answers: <span className="font-medium text-foreground">{discoveryLargest}</span>.</>
              )}
            </p>
            <div className="flex gap-2">
              <Button size="sm" onClick={connectDiscoveredLocal}>
                <Sparkles className="w-3.5 h-3.5 me-1.5" /> Connect
              </Button>
              <Button size="sm" variant="outline" onClick={dismissDiscoveryBanner}>
                <X className="w-3.5 h-3.5 me-1.5" /> Not now
              </Button>
            </div>
          </div>
        </div>
      )}

      <SettingsSection
        title="AI Assistant"
        description="Ask questions about your synced mail. Local runs entirely on this machine's own model runtime; server is centrally hosted and licensed per seat; public sends your question to a provider you choose, using your own API key."
      >
        <SettingItem label="Provider">
          {providerOptions.length > 0 ? (
            <RadioGroup
              value={settings.provider ?? ''}
              onChange={(v) => update('provider', v as 'local' | 'server' | 'public' | 'opencode')}
              options={providerOptions}
            />
          ) : (
            <span className="text-sm text-muted-foreground">No provider class available.</span>
          )}
        </SettingItem>
      </SettingsSection>

      {settings.provider === 'local' && canUseLocal && (
        <SettingsSection
          title="Local runtime"
          description={
            localLlmNeedsCorsSetup()
              ? "Reaches Ollama on this machine directly from the browser. If the test below fails, Ollama's OLLAMA_ORIGINS setting likely doesn't allow this page's origin yet."
              : 'Reaches Ollama on this machine directly — no extra setup needed in the desktop app.'
          }
        >
          <SettingItem label="Base URL">
            <input
              type="text"
              value={settings.localBaseUrl}
              onChange={(e) => update('localBaseUrl', e.target.value)}
              spellCheck={false}
              className={inputClass}
            />
          </SettingItem>
          <SettingItem label="Model" description={localModels.length === 0 ? 'Refresh to list installed models.' : undefined}>
            <div className="flex items-center gap-2 flex-wrap">
              {localModels.length > 0 ? (
                <Select
                  value={settings.localModel ?? ''}
                  onChange={(v) => update('localModel', v)}
                  options={localModels.map((m) => ({ value: m, label: m }))}
                />
              ) : (
                <span className="text-sm text-muted-foreground">{settings.localModel || 'None selected'}</span>
              )}
              <Button variant="outline" size="sm" onClick={refreshLocalModels} disabled={refreshingLocal}>
                <RefreshCw className={`w-3.5 h-3.5 me-1.5 ${refreshingLocal ? 'animate-spin' : ''}`} />
                Refresh
              </Button>
            </div>
          </SettingItem>
          <SettingItem label="Connection">
            <div className="flex items-center gap-2 flex-wrap">
              <Button variant="outline" size="sm" onClick={runTestConnection} disabled={testStatus === 'testing'}>
                {testStatus === 'testing' && <Loader2 className="w-3.5 h-3.5 me-1.5 animate-spin" />}
                Test connection
              </Button>
              {testStatus === 'ok' && (
                <span className="flex items-center gap-1.5 text-sm text-green-600 dark:text-green-500">
                  <CheckCircle className="w-3.5 h-3.5" /> Reachable
                </span>
              )}
              {testStatus === 'error' && (
                <span className="flex items-center gap-1.5 text-sm text-destructive">
                  <AlertTriangle className="w-3.5 h-3.5 shrink-0" /> {testError}
                </span>
              )}
            </div>
          </SettingItem>
        </SettingsSection>
      )}

      {settings.provider === 'opencode' && canUseOpencode && (
        <SettingsSection
          title="OpenCode (local agent)"
          description="Uses a locally-running OpenCode server on this machine. OpenCode holds its own provider credentials, so there is no API key to enter here — and it reports the exact models it can reach, so there is nothing to type by hand."
        >
          <SettingItem label="Model" description={opencodeModels.length === 0 ? 'Refresh to list the models OpenCode can reach.' : undefined}>
            <div className="flex items-center gap-2 flex-wrap">
              {opencodeModels.length > 0 ? (
                <Select
                  value={settings.opencodeModel ?? ''}
                  onChange={(v) => update('opencodeModel', v)}
                  options={opencodeModels.map((m) => ({ value: m.ref, label: m.label }))}
                />
              ) : (
                <span className="text-sm text-muted-foreground">{settings.opencodeModel || 'None selected'}</span>
              )}
              <Button variant="outline" size="sm" onClick={refreshOpencodeModels} disabled={refreshingOpencode}>
                <RefreshCw className={`w-3.5 h-3.5 me-1.5 ${refreshingOpencode ? 'animate-spin' : ''}`} />
                Refresh
              </Button>
            </div>
          </SettingItem>
          {opencodeError && (
            <SettingItem label="Status">
              <span className="flex items-start gap-1.5 text-sm text-destructive">
                <AlertTriangle className="w-3.5 h-3.5 shrink-0 mt-0.5" /> {opencodeError}
              </span>
            </SettingItem>
          )}

          <SettingItem
            label="Providers"
            description="Add credentials for any provider OpenCode supports — a key entered here is stored by OpenCode itself, not by this app. Providers that only offer a browser sign-in (OAuth) aren't manageable here yet; use the opencode CLI for those."
          >
            <Button
              variant="outline" size="sm"
              onClick={() => {
                const next = !showProviderManager;
                setShowProviderManager(next);
                if (next && opencodeProviders.length === 0) void refreshOpencodeProviders();
              }}
            >
              {showProviderManager ? 'Hide' : 'Manage providers'}
            </Button>
          </SettingItem>

          {showProviderManager && (
            <div className="px-4 pb-4 space-y-3">
              {providerActionError && (
                <p className="flex items-start gap-1.5 text-sm text-destructive">
                  <AlertTriangle className="w-3.5 h-3.5 shrink-0 mt-0.5" /> {providerActionError}
                </p>
              )}

              <div className="flex items-center gap-2">
                <input
                  type="text"
                  value={providerSearch}
                  onChange={(e) => setProviderSearch(e.target.value)}
                  placeholder="Search providers (e.g. anthropic, openai, groq)…"
                  spellCheck={false}
                  className={inputClass}
                />
                <Button variant="outline" size="sm" onClick={refreshOpencodeProviders} disabled={loadingProviders}>
                  <RefreshCw className={`w-3.5 h-3.5 me-1.5 ${loadingProviders ? 'animate-spin' : ''}`} />
                  Refresh
                </Button>
              </div>

              {opencodeProviders.length === 0 && !loadingProviders && (
                <p className="text-xs text-muted-foreground">No providers loaded yet — click Refresh.</p>
              )}

              <div className="max-h-72 overflow-y-auto space-y-1.5">
                {opencodeProviders
                  .filter((p) => {
                    const q = providerSearch.trim().toLowerCase();
                    return !q || p.id.toLowerCase().includes(q) || p.name.toLowerCase().includes(q);
                  })
                  // Connected first (already sorted server-side), then cap what
                  // renders — 180 providers in one scroll box is noise, not choice.
                  .slice(0, providerSearch.trim() ? 40 : 20)
                  .map((p) => (
                    <div key={p.id} className="flex items-center gap-2 rounded-md border border-border px-3 py-2">
                      <div className="flex-1 min-w-0">
                        <span className="text-sm">{p.name}</span>
                        <span className="ms-1.5 text-xs text-muted-foreground">{p.id}</span>
                      </div>
                      {p.connected ? (
                        <>
                          <span className="flex items-center gap-1 text-xs text-emerald-600 dark:text-emerald-500">
                            <CheckCircle className="w-3.5 h-3.5" /> Connected
                          </span>
                          <Button
                            variant="outline" size="sm"
                            onClick={() => handleRemoveProvider(p.id)}
                            disabled={providerBusyId === p.id}
                          >
                            <Trash2 className="w-3.5 h-3.5" />
                          </Button>
                        </>
                      ) : p.supportsApiKey ? (
                        addingProviderId === p.id ? (
                          <div className="flex items-center gap-1.5">
                            <input
                              type="password"
                              value={newProviderKey}
                              onChange={(e) => setNewProviderKey(e.target.value)}
                              placeholder="API key"
                              autoFocus
                              className="px-2 py-1 text-xs rounded-md bg-muted border border-border w-36"
                            />
                            <Button size="sm" onClick={() => handleAddProvider(p.id)} disabled={providerBusyId === p.id || !newProviderKey.trim()}>
                              Save
                            </Button>
                            <Button variant="outline" size="sm" onClick={() => { setAddingProviderId(null); setNewProviderKey(''); }}>
                              <X className="w-3.5 h-3.5" />
                            </Button>
                          </div>
                        ) : (
                          <Button variant="outline" size="sm" onClick={() => { setAddingProviderId(p.id); setNewProviderKey(''); }}>
                            <Plus className="w-3.5 h-3.5 me-1" /> Add key
                          </Button>
                        )
                      ) : (
                        <span className="text-xs text-muted-foreground">Browser sign-in only</span>
                      )}
                    </div>
                  ))}
              </div>
            </div>
          )}
        </SettingsSection>
      )}

      {settings.provider === 'server' && canUseServer && (
        <SettingsSection
          title="Server (VNC-hosted)"
          description="Centrally hosted — no setup needed on your side. Licensed per seat; using this for the first time consumes one automatically if seats remain."
        >
          <SettingItem label="Model" description={serverModels.length === 0 ? 'Refresh to list available models.' : undefined}>
            <div className="flex items-center gap-2 flex-wrap">
              {serverModels.length > 0 ? (
                <Select
                  value={settings.serverModel ?? ''}
                  onChange={(v) => update('serverModel', v)}
                  options={serverModels.map((m) => ({ value: m, label: m }))}
                />
              ) : (
                <span className="text-sm text-muted-foreground">{settings.serverModel || 'None selected'}</span>
              )}
              <Button variant="outline" size="sm" onClick={refreshServerModels} disabled={refreshingServer}>
                <RefreshCw className={`w-3.5 h-3.5 me-1.5 ${refreshingServer ? 'animate-spin' : ''}`} />
                Refresh
              </Button>
            </div>
          </SettingItem>
          {serverError && (
            <SettingItem label="Status">
              <span className="flex items-center gap-1.5 text-sm text-destructive">
                <AlertTriangle className="w-3.5 h-3.5 shrink-0" /> {serverError}
              </span>
            </SettingItem>
          )}
        </SettingsSection>
      )}

      {settings.provider === 'public' && canUsePublic && (
        <SettingsSection
          title="Public providers"
          description="Save several — different models for different questions. Any OpenAI-compatible endpoint works. Keys are stored only in this browser and, for now, use of this class is not monitored or metered by VNC."
        >
          {policy.publicPresets.length > 0 && (
            <SettingItem
              label="Org-managed providers"
              description="Set up by your admin. Pick one below in “Answer with” — no key to paste, it's resolved on the server."
            >
              <div className="flex flex-col gap-2 w-full">
                {policy.publicPresets.map((p) => (
                  <div key={p.id} className="flex items-center gap-2 rounded-md border border-border px-3 py-2">
                    <div className="flex-1 min-w-0">
                      <p className="text-sm font-medium text-foreground truncate">{p.name}</p>
                      <p className="text-xs text-muted-foreground truncate">{p.model} · managed by admin</p>
                    </div>
                  </div>
                ))}
              </div>
            </SettingItem>
          )}
          {settings.publicProfiles.length > 0 && (
            <SettingItem label="Your own keys">
              <div className="flex flex-col gap-2 w-full">
                {settings.publicProfiles.map((p) => (
                  <div key={p.id} className="flex items-center gap-2 rounded-md border border-border px-3 py-2">
                    <div className="flex-1 min-w-0">
                      <p className="text-sm font-medium text-foreground truncate">{p.name}</p>
                      <p className="text-xs text-muted-foreground truncate">{p.model} · {p.baseUrl}</p>
                    </div>
                    <Button variant="ghost" size="sm" onClick={() => removeProfile(p.id)} aria-label={`Remove ${p.name}`}>
                      <Trash2 className="w-3.5 h-3.5 text-destructive" />
                    </Button>
                  </div>
                ))}
              </div>
            </SettingItem>
          )}
          <SettingItem label="Add your own key" description="Prefer to bring your own instead of an org-managed provider above.">
            <div className="flex flex-col gap-2 w-full">
              <div className="flex gap-2 flex-wrap">
                <input
                  type="text"
                  value={newProfileName}
                  onChange={(e) => setNewProfileName(e.target.value)}
                  placeholder="Name, e.g. Claude via OpenRouter"
                  spellCheck={false}
                  className={inputClass}
                />
                <input
                  type="text"
                  value={newProfileModel}
                  onChange={(e) => setNewProfileModel(e.target.value)}
                  placeholder="Model, e.g. anthropic/claude-sonnet-4.5"
                  spellCheck={false}
                  className={inputClass}
                />
              </div>
              <div className="flex gap-2 flex-wrap">
                <input
                  type="text"
                  value={newProfileBaseUrl}
                  onChange={(e) => setNewProfileBaseUrl(e.target.value)}
                  placeholder="Base URL"
                  spellCheck={false}
                  className={inputClass}
                />
                <input
                  type="password"
                  value={newProfileKey}
                  onChange={(e) => setNewProfileKey(e.target.value)}
                  placeholder="sk-..."
                  spellCheck={false}
                  className={inputClass}
                />
                <Button
                  variant="outline"
                  size="sm"
                  onClick={addProfile}
                  disabled={!newProfileName || !newProfileBaseUrl || !newProfileModel || !newProfileKey}
                >
                  <Plus className="w-3.5 h-3.5 me-1.5" />
                  Add
                </Button>
              </div>
              {profileError && <p className="text-xs text-destructive">{profileError}</p>}
            </div>
          </SettingItem>
          <SettingItem
            label="I understand this leaves the organisation"
            description="Your question and any retrieved mail excerpts are sent to the provider you pick below, outside this organisation."
          >
            <ToggleSwitch
              checked={settings.publicConsentAccepted}
              onChange={(v) => update('publicConsentAccepted', v)}
            />
          </SettingItem>
        </SettingsSection>
      )}

      {settings.provider && (
        <SettingsSection title="Try it" description="Ask a question against your synced mail.">
          <div className="flex flex-col gap-3">
            {settings.provider === 'public' && (settings.publicProfiles.length > 0 || policy.publicPresets.length > 0) && (
              <SettingItem label="Answer with">
                <Select
                  value={settings.activeProfileId ?? ''}
                  onChange={(v) => update('activeProfileId', v)}
                  options={[
                    ...policy.publicPresets.map((p) => ({ value: presetActiveId(p.id), label: `${p.name} (org)` })),
                    ...settings.publicProfiles.map((p) => ({ value: p.id, label: p.name })),
                  ]}
                />
              </SettingItem>
            )}
            <textarea
              value={question}
              onChange={(e) => setQuestion(e.target.value)}
              placeholder="What did legal say about the Meier contract deadline?"
              rows={3}
              className="px-3 py-2 text-sm rounded-md bg-muted border border-border text-foreground focus:outline-none focus:ring-2 focus:ring-ring transition-colors duration-150 resize-y"
            />
            <Button onClick={runAsk} disabled={!canAsk || asking} className="self-start">
              {asking && <Loader2 className="w-3.5 h-3.5 me-1.5 animate-spin" />}
              Ask
            </Button>

            {seatNotice && (
              <div className="flex items-start gap-2 rounded-lg border border-border bg-muted/40 p-3">
                <CheckCircle className="w-4 h-4 mt-0.5 text-green-600 dark:text-green-500 shrink-0" />
                <p className="text-sm text-muted-foreground">{seatNotice}</p>
              </div>
            )}

            {askError && (
              <div className="flex items-start gap-2 rounded-lg border border-destructive/40 bg-destructive/5 p-3">
                <AlertTriangle className="w-4 h-4 mt-0.5 text-destructive shrink-0" />
                <p className="text-sm text-destructive">{askError}</p>
              </div>
            )}

            {askResult && (
              <div className="flex flex-col gap-2 rounded-lg border border-border p-4">
                {askResult.retrievalState === 'no-index' && (
                  <p className="text-xs text-muted-foreground italic">
                    No local mail index available in this session — answered without your mail. The index is
                    desktop-only; build it under Settings → About &amp; Data.
                  </p>
                )}
                {askResult.retrievalState === 'no-match' && (
                  <p className="text-xs text-muted-foreground italic">
                    Your mail index is available, but nothing in it matched this question — answered without
                    your mail. It matches on keywords, so questions about <em>content</em> (&ldquo;what did
                    Anna say about the invoice?&rdquo;) work better than ones about recency
                    (&ldquo;the last mail&rdquo;).
                  </p>
                )}
                <p className="text-sm text-foreground whitespace-pre-wrap">{askResult.answer}</p>
                {askResult.sources.length > 0 && (
                  <div className="flex flex-col gap-0.5 border-t border-border pt-2 mt-1">
                    <span className="text-xs font-medium text-muted-foreground">Sources</span>
                    {askResult.sources.map((s, i) => (
                      <span key={s.id} className="text-xs text-muted-foreground truncate">
                        [{i + 1}] {s.subject}
                      </span>
                    ))}
                  </div>
                )}
              </div>
            )}
          </div>
        </SettingsSection>
      )}
    </div>
  );
}
