'use client';

import { useCallback, useEffect, useMemo, useState } from 'react';
import { RefreshCw, CheckCircle, AlertTriangle, Loader2 } 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 { DEFAULT_AI_POLICY, type AiPolicy } from '@/lib/ai/types';
import { supportsLocalLlm, localLlmNeedsCorsSetup } from '@/lib/platform-capabilities';
import { getAiApiKey, setAiApiKey } from '@/lib/ai/key-store';
import { loadAiSettings, saveAiSettings, type AiLocalSettings } from '@/lib/ai/local-settings';
import { askMail, listLocalModels, 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]';

/**
 * Prototype scope (docs/AI-ASSISTANT-CONCEPT.md, decisions recorded
 * 2026-08-05 evening — see lib/ai/types.ts): `local` (loopback
 * Ollama-compatible runtime) ships free with no entitlement check; `public`
 * (BYOK, OpenAI-compatible) is available but explicitly unmonitored for now
 * — no seats, no metering, no server-recorded consent yet. `server`
 * (VNC-hosted) isn't wired up here; that infra is landing on the dev k8s
 * cluster separately.
 */
export function AiAssistantSettings() {
  const [policy, setPolicy] = useState<AiPolicy>(DEFAULT_AI_POLICY);
  const [policyLoading, setPolicyLoading] = useState(true);
  const [settings, setSettings] = useState<AiLocalSettings>(() => loadAiSettings());

  useEffect(() => {
    let cancelled = false;
    (async () => {
      try {
        const res = await apiFetch('/api/ai/policy');
        if (res.ok && !cancelled) setPolicy(await res.json());
      } 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 canUsePublic = policy.entitlement.classes.includes('public');

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

  const refreshModels = useCallback(async () => {
    setRefreshing(true);
    try {
      const models = await listLocalModels(settings.localBaseUrl);
      setLocalModels(models);
      if (!settings.localModel && models[0]) update('localModel', models[0]);
    } catch {
      setLocalModels([]);
    } finally {
      setRefreshing(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]);

  // ── Public provider ──
  const [hasSavedKey, setHasSavedKey] = useState(false);
  const [apiKeyInput, setApiKeyInput] = useState('');

  useEffect(() => {
    setHasSavedKey(!!getAiApiKey('public'));
  }, []);

  const saveKey = useCallback(() => {
    if (!apiKeyInput) return;
    setAiApiKey('public', apiKeyInput);
    setHasSavedKey(true);
    setApiKeyInput('');
  }, [apiKeyInput]);

  // ── 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 canAsk =
    question.trim().length > 0 &&
    (settings.provider === 'local'
      ? canUseLocal && !!settings.localModel
      : settings.provider === 'public'
        ? canUsePublic && !!settings.publicModel && settings.publicConsentAccepted && hasSavedKey
        : false);

  const runAsk = useCallback(async () => {
    setAsking(true);
    setAskError(null);
    setAskResult(null);
    try {
      const result = await askMail(question.trim(), {
        provider: settings.provider as 'local' | 'public',
        localBaseUrl: settings.localBaseUrl,
        localModel: settings.localModel,
        publicBaseUrl: settings.publicBaseUrl,
        publicModel: settings.publicModel,
        publicApiKey: getAiApiKey('public'),
      });
      setAskResult(result);
    } catch (err) {
      setAskError(err instanceof Error ? err.message : String(err));
    } finally {
      setAsking(false);
    }
  }, [question, settings]);

  const providerOptions = useMemo(
    () => [
      ...(canUseLocal ? [{ value: 'local', label: 'Local (Ollama)' }] : []),
      ...(canUsePublic ? [{ value: 'public', label: 'Public (your API key)' }] : []),
    ],
    [canUseLocal, canUsePublic],
  );

  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>
    );
  }

  return (
    <div className="space-y-6">
      <SettingsSection
        title="AI Assistant"
        description="Ask questions about your synced mail. Local runs entirely on this machine's own model runtime; public sends your question (and any retrieved excerpts) 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' | 'public')}
              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={refreshModels} disabled={refreshing}>
                <RefreshCw className={`w-3.5 h-3.5 me-1.5 ${refreshing ? '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 === 'public' && canUsePublic && (
        <SettingsSection
          title="Public provider"
          description="Any OpenAI-compatible endpoint. Defaults to OpenRouter. Your key is stored only in this browser, never sent anywhere but the provider below — and, for now, use of this class is not monitored or metered by VNC."
        >
          <SettingItem label="Base URL">
            <input
              type="text"
              value={settings.publicBaseUrl}
              onChange={(e) => update('publicBaseUrl', e.target.value)}
              spellCheck={false}
              className={inputClass}
            />
          </SettingItem>
          <SettingItem label="Model">
            <input
              type="text"
              value={settings.publicModel}
              onChange={(e) => update('publicModel', e.target.value)}
              placeholder="e.g. anthropic/claude-sonnet-4.5"
              spellCheck={false}
              className={inputClass}
            />
          </SettingItem>
          <SettingItem
            label="API key"
            description={hasSavedKey ? 'A key is saved in this browser. Enter a new one to replace it.' : 'Stored in this browser only.'}
          >
            <div className="flex items-center gap-2 flex-wrap">
              <input
                type="password"
                value={apiKeyInput}
                onChange={(e) => setApiKeyInput(e.target.value)}
                placeholder={hasSavedKey ? '•••• saved' : 'sk-...'}
                spellCheck={false}
                className={inputClass}
              />
              <Button variant="outline" size="sm" onClick={saveKey} disabled={!apiKeyInput}>
                Save
              </Button>
            </div>
          </SettingItem>
          <SettingItem
            label="I understand this leaves the organisation"
            description="Your question and any retrieved mail excerpts are sent to the provider above, 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">
            <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>

            {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.unaugmented && (
                  <p className="text-xs text-muted-foreground italic">
                    No local mail index available in this session — answered without retrieval context.
                  </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>
  );
}
