// frontend/components/settings/LlmTab.tsx — LLM 연결(provider/model/host/api키/timeout) 편집·테스트·적용
// 지원 provider: ollama | openai(호환 엔드포인트) | gemini. provider 별로 입력 필드가 달라진다.
"use client";
import { useEffect, useState } from "react";
import { Button } from "@/components/Button";
import { Icon } from "@/components/Icon";
import { SegmentToggle } from "@/components/SegmentToggle";
import { settingsApi } from "@/lib/settings/api";
import { useLlmConfig } from "@/lib/hooks/useSettings";
import type { LlmProvider, LlmTestResult } from "@/lib/types";
import type { ToastFn } from "./SettingsClient";

const PROVIDER_LABEL: Record<string, string> = {
  auto: "자동",
  ollama: "Ollama",
  heuristic: "규칙(오프라인)",
  openai: "OpenAI 호환",
  gemini: "Gemini",
};

type ProviderMeta = {
  hint: string;
  hostLabel: string;
  hostPlaceholder: string;
  modelPlaceholder: string;
  needsHost: boolean;
  needsKey: boolean;
  keyPlaceholder: string;
  presets: { label: string; url: string }[];
};

// provider 별 입력 메타. needsHost=호스트/베이스URL 필드 표시, needsKey=API 키 필드 표시.
const PROVIDER_META: Record<string, ProviderMeta> = {
  ollama: {
    hint: "Ollama 서버(로컬/원격)를 사용해요.",
    hostLabel: "Ollama 호스트 (API 주소)",
    hostPlaceholder: "https://ollama.yirugi.synology.me",
    modelPlaceholder: "예: gemma4:26b-a4b-it-q8_0",
    needsHost: true,
    needsKey: false,
    keyPlaceholder: "",
    presets: [
      { label: "Synology 원격", url: "https://ollama.yirugi.synology.me" },
      { label: "로컬", url: "http://localhost:11434" },
    ],
  },
  openai: {
    hint: "OpenAI 호환 엔드포인트(OpenAI·OpenRouter·Groq·로컬 vLLM/LM Studio 등).",
    hostLabel: "API 베이스 URL",
    hostPlaceholder: "https://api.openai.com/v1",
    modelPlaceholder: "예: gpt-4o-mini",
    needsHost: true,
    needsKey: true,
    keyPlaceholder: "sk-…",
    presets: [
      { label: "OpenAI", url: "https://api.openai.com/v1" },
      { label: "OpenRouter", url: "https://openrouter.ai/api/v1" },
      { label: "Gemini(호환)", url: "https://generativelanguage.googleapis.com/v1beta/openai" },
    ],
  },
  gemini: {
    hint: "Google Gemini API. 키만 입력하면 기본 엔드포인트를 사용해요.",
    hostLabel: "API 베이스 URL (선택)",
    hostPlaceholder: "https://generativelanguage.googleapis.com/v1beta",
    modelPlaceholder: "예: gemini-2.5-flash",
    needsHost: false,
    needsKey: true,
    keyPlaceholder: "AIza…",
    presets: [],
  },
};

function metaFor(p: string): ProviderMeta {
  return PROVIDER_META[p] ?? PROVIDER_META.ollama;
}

export function LlmTab({ toast }: { toast: ToastFn }) {
  const { data: cfg, isLoading, refresh } = useLlmConfig();
  const [provider, setProvider] = useState<LlmProvider>("ollama");
  const [model, setModel] = useState("");
  const [host, setHost] = useState("");
  const [apiKey, setApiKey] = useState("");
  const [timeout, setTimeoutVal] = useState("");
  const [test, setTest] = useState<LlmTestResult | null>(null);
  const [testing, setTesting] = useState(false);
  const [saving, setSaving] = useState(false);
  // 호스트/키에서 불러온 사용 가능한 모델 목록(드롭다운 선택). reloadKey 증가 시 강제 재조회.
  const [models, setModels] = useState<string[]>([]);
  const [loadingModels, setLoadingModels] = useState(false);
  const [reloadKey, setReloadKey] = useState(0);

  useEffect(() => {
    if (cfg) {
      setProvider(cfg.provider);
      setModel(cfg.model);
      setHost(cfg.host);
      setTimeoutVal(String(cfg.timeout));
      setApiKey(""); // 키는 절대 내려받지 않음 — 비워두고 'set' 여부만 표시.
    }
  }, [cfg]);

  const meta = metaFor(provider);
  const keySet = !!cfg?.api_key_set;

  // provider 전환: 잔여 host/key 혼입을 막고 새 provider 기본 호스트로 초기화.
  const changeProvider = (p: LlmProvider) => {
    setProvider(p);
    setTest(null);
    setModels([]);
    setModel(""); // provider 마다 모델명 체계가 달라 초기화(목록서 다시 선택)
    setApiKey("");
    const presets = metaFor(p).presets;
    setHost(presets[0]?.url ?? "");
  };

  // host/key 가 준비되면 모델 목록을 자동 조회 → 드롭다운. 디바운스 450ms. 새로고침=reloadKey.
  useEffect(() => {
    if (provider === "heuristic") {
      setModels([]);
      return;
    }
    const needsKey = provider === "openai" || provider === "gemini";
    const needsHost = provider === "ollama" || provider === "openai";
    if (needsHost && !host.trim()) {
      setModels([]);
      return;
    }
    if (needsKey && !apiKey.trim() && !cfg?.api_key_set) {
      setModels([]);
      return;
    }
    let cancelled = false;
    const run = async () => {
      setLoadingModels(true);
      try {
        const r = await settingsApi.testLlm({ provider, host, api_key: apiKey || undefined });
        if (!cancelled) setModels(r.reachable ? r.models : []);
      } catch {
        if (!cancelled) setModels([]);
      } finally {
        if (!cancelled) setLoadingModels(false);
      }
    };
    const t = setTimeout(run, 450);
    return () => {
      cancelled = true;
      clearTimeout(t);
    };
  }, [host, provider, apiKey, reloadKey, cfg?.api_key_set]);

  const onTest = async () => {
    setTesting(true);
    setTest(null);
    try {
      const r = await settingsApi.testLlm({ provider, host, model, api_key: apiKey || undefined });
      setTest(r);
      if (r.reachable) setModels(r.models);
      toast(
        r.reachable ? "연결에 성공했어요" : "연결할 수 없어요",
        r.reachable ? "green" : "coral",
      );
    } catch {
      toast("연결 테스트에 실패했어요", "coral");
    } finally {
      setTesting(false);
    }
  };

  const onSave = async () => {
    setSaving(true);
    try {
      const t = parseFloat(timeout);
      await settingsApi.updateLlm({
        provider,
        model,
        host,
        timeout: Number.isFinite(t) && t > 0 ? t : undefined,
        // 입력했을 때만 키 전송(빈칸=기존 키 유지). 지우려면 '기본값으로'.
        api_key: apiKey.trim() ? apiKey : undefined,
      });
      await refresh();
      setApiKey("");
      toast("LLM 설정을 저장했어요", "green");
    } catch {
      toast("저장에 실패했어요", "coral");
    } finally {
      setSaving(false);
    }
  };

  const onReset = async () => {
    setSaving(true);
    try {
      await settingsApi.updateLlm({ provider: "", model: "", host: "", timeout: 0, api_key: "" });
      const next = await refresh();
      if (next) {
        setProvider(next.provider);
        setModel(next.model);
        setHost(next.host);
        setTimeoutVal(String(next.timeout));
      }
      setApiKey("");
      setTest(null);
      toast("기본값(.env)으로 되돌렸어요", "blue");
    } catch {
      toast("초기화에 실패했어요", "coral");
    } finally {
      setSaving(false);
    }
  };

  return (
    <section className="set-sec" aria-label="AI · LLM">
      <header className="set-sechead">
        <h2>AI · LLM 연결</h2>
        <p>아리의 분류·요약을 담당하는 모델 연결이에요. 저장하면 즉시 적용돼요.</p>
        {cfg?.overridden && <span className="set-tag set-tag-on">사용자 설정 적용 중</span>}
      </header>

      <div className="set-card">
        <div className="set-field">
          <label>프로바이더</label>
          <SegmentToggle
            options={(cfg?.provider_options ?? ["ollama", "openai", "gemini"]).map((p) => ({
              id: p,
              label: PROVIDER_LABEL[p] ?? p,
            }))}
            value={provider}
            onChange={(id) => changeProvider(id as LlmProvider)}
          />
          <small className="set-hint">{meta.hint}</small>
        </div>

        {meta.needsHost && (
          <div className="set-field">
            <label htmlFor="llm-host">{meta.hostLabel}</label>
            <input
              id="llm-host"
              className="set-input set-mono"
              value={host}
              onChange={(e) => setHost(e.target.value)}
              placeholder={meta.hostPlaceholder}
            />
            {meta.presets.length > 0 && (
              <div className="set-chips" role="list" aria-label="자주 쓰는 호스트">
                {meta.presets.map((h) => (
                  <button
                    key={h.url}
                    type="button"
                    role="listitem"
                    className={"set-chip" + (h.url === host ? " on" : "")}
                    onClick={() => setHost(h.url)}
                    title={h.url}
                  >
                    {h.label}
                  </button>
                ))}
              </div>
            )}
          </div>
        )}

        {meta.needsKey && (
          <div className="set-field">
            <label htmlFor="llm-key">API 키</label>
            <input
              id="llm-key"
              type="password"
              autoComplete="off"
              className="set-input set-mono"
              value={apiKey}
              onChange={(e) => setApiKey(e.target.value)}
              placeholder={keySet ? "설정됨 · 새 키를 입력하면 교체돼요" : meta.keyPlaceholder}
            />
            <small className="set-hint">
              {keySet
                ? "키가 저장돼 있어요(암호화). 비워 두면 그대로 유지, 입력하면 교체돼요."
                : "키는 암호화해서 저장하고, 화면에는 다시 표시하지 않아요."}
            </small>
          </div>
        )}

        <div className="set-field">
          <label htmlFor="llm-model">모델</label>
          {models.length > 0 ? (
            <select
              id="llm-model"
              className="set-input set-mono"
              value={model}
              onChange={(e) => setModel(e.target.value)}
            >
              {model && !models.includes(model) && (
                <option value={model}>{model} (현재 · 목록에 없음)</option>
              )}
              {models.map((m) => (
                <option key={m} value={m}>
                  {m}
                </option>
              ))}
            </select>
          ) : (
            <input
              id="llm-model"
              className="set-input set-mono"
              value={model}
              onChange={(e) => setModel(e.target.value)}
              placeholder={meta.modelPlaceholder}
            />
          )}
          <div className="set-modelmeta">
            <small className="set-hint">
              {loadingModels
                ? "모델 목록 불러오는 중…"
                : models.length > 0
                  ? `${models.length}개 모델 사용 가능 · 드롭다운에서 선택`
                  : meta.needsKey
                    ? "키를 입력하면 모델 목록을 자동으로 불러와요"
                    : "호스트에 연결되면 모델 목록을 자동으로 불러와요"}
            </small>
            <button
              type="button"
              className="set-chip"
              onClick={() => setReloadKey((k) => k + 1)}
              disabled={loadingModels}
            >
              <Icon name="refresh" />
              새로고침
            </button>
          </div>
        </div>

        <div className="set-field set-field-narrow">
          <label htmlFor="llm-timeout">응답 제한시간(초)</label>
          <input
            id="llm-timeout"
            type="number"
            min={1}
            className="set-input set-mono"
            value={timeout}
            onChange={(e) => setTimeoutVal(e.target.value)}
          />
        </div>

        <div className="set-actions">
          <Button variant="glass" icon="zap" onClick={onTest} disabled={testing}>
            {testing ? "테스트 중…" : "연결 테스트"}
          </Button>
          <Button variant="lime" icon="tick" onClick={onSave} disabled={saving || isLoading}>
            {saving ? "저장 중…" : "저장 후 적용"}
          </Button>
          {cfg?.overridden && (
            <Button variant="glass" icon="refresh" onClick={onReset} disabled={saving}>
              기본값으로
            </Button>
          )}
        </div>

        {test && (
          <div className={"set-test " + (test.reachable ? "ok" : "bad")} role="status">
            <Icon name={test.reachable ? "tick" : "x"} />
            <span>
              {test.reachable
                ? `연결됨 · ${test.provider}${test.model ? " · " + test.model : ""}`
                : `연결 실패 · ${test.detail || "응답 없음"}`}
            </span>
          </div>
        )}
      </div>

      <div className="set-card">
        <header className="set-cardhead">
          <h3>보조 프로바이더</h3>
          <span className="set-hint">환경변수(.env)로 관리 · 읽기 전용</span>
        </header>
        <dl className="set-kv">
          <div>
            <dt>임베딩(RAG)</dt>
            <dd>
              {cfg?.embed_provider ?? "—"}
              {cfg?.embed_model ? ` · ${cfg.embed_model}` : ""}
            </dd>
          </div>
          <div>
            <dt>에이전트</dt>
            <dd>{cfg?.agent_provider ?? "—"}</dd>
          </div>
          <div>
            <dt>음성 인식(STT)</dt>
            <dd>{cfg?.stt_provider ?? "—"}</dd>
          </div>
          <div>
            <dt>이미지 인식(Vision)</dt>
            <dd>{cfg?.vision_provider ?? "—"}</dd>
          </div>
        </dl>
      </div>
    </section>
  );
}
