'use client';

export const runtime = 'edge';

// Mira 搜尋頁 — Karpathy index pattern 為 primary（leo 2026-05-23）
// SDD: polaris/mira/.agents/specs/mira-app/design.md §3.5.12.4「Karpathy index pattern（不用 vector embedding）」
// 三層（C 混合）：
//   1. Index 即時文字比對：掃 index-entry（entity 名 + 摘要）子字串命中 → 列 entity（零 token）
//   2. LLM 路由（選用）：整個 index 餵 Claude，問「leo 想找哪些 entity」→ 最貼 Karpathy 本意
//   3. 向量兜底（折疊）：KBDB /search semantic，SDD 明文「不是 primary，當保險」

import { Suspense, useCallback, useEffect, useMemo, useState } from 'react';
import { useRouter, useSearchParams } from 'next/navigation';
import Link from 'next/link';
import { MarkdownView } from '../_shared/markdown';
import '../mira.css';

const KBDB_BASE = 'https://kbdb.finally.click';
const API_BASE = process.env.NEXT_PUBLIC_API_BASE ?? 'https://cypher.arcrun.dev';
const CLAUDE_API = 'https://claude-api.arcrun.dev';

// 繁體異體字正規化（臺→台），讓 query 對得上多用「台」的 KB 內容
function normalizeQuery(q: string): string {
  return q.replace(/臺/g, '台');
}

type IndexEntry = {
  entity: string;       // H1 / page_name 去 index- 前綴
  pageName: string;     // index-entry 自己的 page_name（index-{entity}）
  oneLiner: string;     // 「一句話定義」
  outline: string;      // facet outline 全文（拿來比對 + 餵 LLM）
  raw: string;          // 完整 content（餵 LLM 用，截斷）
};

// 解析 index-entry markdown → 結構
function parseIndexEntry(content: string, pageName: string): IndexEntry {
  const entity = (content.match(/^#\s+(.+)$/m)?.[1] ?? pageName.replace(/^index-/, '')).trim();
  const oneLiner = (content.match(/##\s*一句話定義\s*\n+([^\n#]+)/)?.[1] ?? '').trim();
  const outlineMatch = content.match(/##\s*段落 outline[^\n]*\n([\s\S]*?)(?=\n##|$)/);
  const outline = (outlineMatch?.[1] ?? '').trim();
  return { entity, pageName, oneLiner, outline, raw: content.slice(0, 700) };
}

// entity 名 → wiki page 路由（wiki-{entity}）
function wikiHref(entity: string): string {
  return `/mira/wiki/${encodeURIComponent('wiki-' + entity)}`;
}

// ── 向量兜底型別 ──
type SearchMatch = {
  score: number;
  type: 'block' | 'triplet';
  metadata?: { entity?: string;[k: string]: unknown };
  block: { id: string; page_name: string | null; content: string | null; type: string; source: string | null } | null;
  triplet: { id: string; subject?: string; predicate?: string; object?: string } | null;
};

function SearchInner() {
  const router = useRouter();
  const params = useSearchParams();
  const initialQ = params.get('q') ?? '';

  const [apiKey, setApiKey] = useState<string | null>(null);
  const [input, setInput] = useState(initialQ);
  const [query, setQuery] = useState(initialQ.trim());
  const [index, setIndex] = useState<IndexEntry[] | null>(null);
  const [error, setError] = useState<string | null>(null);

  // LLM 路由結果
  const [llmEntities, setLlmEntities] = useState<{ entity: string; reason: string }[] | null>(null);
  const [llmLoading, setLlmLoading] = useState(false);

  // 向量兜底
  const [vecMatches, setVecMatches] = useState<SearchMatch[] | null>(null);
  const [vecLoading, setVecLoading] = useState(false);
  const [vecOpen, setVecOpen] = useState(false);

  // 載入：me + 全部 index-entry
  useEffect(() => {
    (async () => {
      try {
        const meRes = await fetch(`${API_BASE}/me`, { credentials: 'include' });
        if (!meRes.ok) { window.location.href = '/login?redirect=/mira/search'; return; }
        const me = (await meRes.json()) as { api_key: string };
        setApiKey(me.api_key);
        const r = await fetch(`${KBDB_BASE}/blocks?type=index-entry&limit=300`, {
          headers: { Authorization: `Bearer ${me.api_key}` },
        });
        if (!r.ok) { setError(`index 讀取失敗：${r.status}`); return; }
        const data = (await r.json()) as { blocks?: { content: string; page_name: string }[] };
        setIndex((data.blocks ?? []).map(b => parseIndexEntry(b.content || '', b.page_name || '')));
      } catch (e) {
        setError(e instanceof Error ? e.message : String(e));
      }
    })();
  }, []);

  // 即時 index 文字比對（純 client，零 token）
  const indexHits = useMemo(() => {
    if (!index || !query) return [];
    const q = normalizeQuery(query).toLowerCase();
    const terms = q.split(/\s+/).filter(Boolean);
    const scored = index
      .map(e => {
        const hay = normalizeQuery(`${e.entity}\n${e.oneLiner}\n${e.outline}`).toLowerCase();
        let score = 0;
        for (const t of terms) {
          if (e.entity.toLowerCase().includes(t)) score += 10; // entity 名命中權重高
          else if (hay.includes(t)) score += 3;
        }
        return { e, score };
      })
      .filter(x => x.score > 0)
      .sort((a, b) => b.score - a.score)
      .slice(0, 12);
    return scored.map(x => x.e);
  }, [index, query]);

  const submit = (e: React.FormEvent) => {
    e.preventDefault();
    const q = input.trim();
    setQuery(q);
    setLlmEntities(null);
    setVecMatches(null);
    setVecOpen(false);
    router.replace(`/mira/search?q=${encodeURIComponent(q)}`);
  };

  // LLM 路由：整個 index 餵 Claude
  const runLlmRoute = useCallback(async () => {
    if (!index || !query || llmLoading) return;
    setLlmLoading(true);
    setLlmEntities(null);
    try {
      const indexDigest = index
        .map(e => `- ${e.entity}：${e.oneLiner || '(無摘要)'}`)
        .join('\n');
      const prompt =
        `你是 leo 知識庫的索引導航員。以下是所有 wiki entity 的索引（entity：一句話定義）：\n\n` +
        `${indexDigest}\n\n---\n\n` +
        `leo 想找：「${query}」\n\n` +
        `請從上面索引挑出最相關的 entity（最多 6 個，可能 0 個）。` +
        `只輸出 JSON 陣列，格式 [{"entity":"<完全照抄索引裡的名稱>","reason":"<為何相關，20字內>"}]，不要其他文字。`;
      const res = await fetch(CLAUDE_API, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ prompt, timeout_ms: 45000 }),
      });
      const data = (await res.json()) as { success?: boolean; data?: { text?: string } };
      const text = data.data?.text ?? '';
      const jsonMatch = text.match(/\[[\s\S]*\]/);
      const parsed = jsonMatch ? JSON.parse(jsonMatch[0]) as { entity: string; reason: string }[] : [];
      // 只留實際存在於 index 的 entity
      const known = new Set(index.map(e => e.entity));
      setLlmEntities(parsed.filter(p => known.has(p.entity)));
    } catch {
      setLlmEntities([]);
    } finally {
      setLlmLoading(false);
    }
  }, [index, query, llmLoading]);

  // 向量兜底
  const runVecSearch = useCallback(async () => {
    if (!apiKey || !query || vecLoading) return;
    setVecOpen(true);
    setVecLoading(true);
    try {
      const res = await fetch(`${KBDB_BASE}/search`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${apiKey}` },
        body: JSON.stringify({ query: normalizeQuery(query), type: 'semantic', topK: 12 }),
      });
      const data = (await res.json()) as { matches?: SearchMatch[] };
      setVecMatches(data.matches ?? []);
    } catch {
      setVecMatches([]);
    } finally {
      setVecLoading(false);
    }
  }, [apiKey, query, vecLoading]);

  return (
    <main className="mira-page">
      <div className="mira-content">
        <header style={{ padding: '24px 0 8px' }}>
          <Link href="/mira/feed" style={{ color: '#888', fontSize: 14, textDecoration: 'none' }}>← 河道</Link>
          <h1 style={{ fontSize: 26, fontWeight: 700, color: '#fff', margin: '8px 0 0' }}>🔍 Wiki 搜尋</h1>
          <p style={{ color: '#888', fontSize: 12, marginTop: 4 }}>
            從 {index?.length ?? '…'} 個 wiki 主題的索引找（Karpathy index）
          </p>
        </header>

        <form className="mira-search-form" onSubmit={submit}>
          <input
            className="mira-search-input"
            value={input}
            onChange={e => setInput(e.target.value)}
            placeholder="找主題（例：特化、台北大學、本地模型）"
            autoFocus
          />
          <button type="submit" className="mira-btn-primary" disabled={!index}>搜尋</button>
        </form>

        {error && <div className="mira-error" style={{ marginBottom: 10 }}>{error}</div>}
        {!index && !error && <div className="empty-state">載入索引中…</div>}

        {index && query && (
          <>
            {/* 第 1 層：index 即時命中 */}
            <section style={{ marginTop: 8 }}>
              <div className="mira-search-section-head">📇 索引命中（{indexHits.length}）</div>
              {indexHits.length === 0 ? (
                <div className="empty-state" style={{ padding: '16px 0' }}>索引裡沒有直接命中的主題。</div>
              ) : (
                indexHits.map(e => (
                  <Link key={e.pageName} href={wikiHref(e.entity)} className="mira-search-result">
                    <div className="mira-search-result-meta"><span>📚 {e.entity}</span></div>
                    {e.oneLiner && <div className="mira-search-snippet">{e.oneLiner}</div>}
                  </Link>
                ))
              )}
            </section>

            {/* 第 2 層：LLM 路由（選用） */}
            <section style={{ marginTop: 18 }}>
              {!llmEntities && (
                <button type="button" className="mira-search-llm-btn" onClick={runLlmRoute} disabled={llmLoading}>
                  {llmLoading ? '🧠 Mira 翻索引中…' : '🧠 找不到？讓 Mira 讀整個索引幫你找'}
                </button>
              )}
              {llmEntities && (
                <>
                  <div className="mira-search-section-head">🧠 Mira 從索引挑的（{llmEntities.length}）</div>
                  {llmEntities.length === 0 ? (
                    <div className="empty-state" style={{ padding: '12px 0' }}>Mira 也覺得索引裡沒有相關主題。</div>
                  ) : (
                    llmEntities.map(p => (
                      <Link key={p.entity} href={wikiHref(p.entity)} className="mira-search-result">
                        <div className="mira-search-result-meta"><span>📚 {p.entity}</span></div>
                        <div className="mira-search-snippet" style={{ color: 'var(--mira-text-3)' }}>{p.reason}</div>
                      </Link>
                    ))
                  )}
                </>
              )}
            </section>

            {/* 第 3 層：向量兜底（折疊） */}
            <section style={{ marginTop: 18 }}>
              {!vecOpen ? (
                <button type="button" className="mira-search-vec-toggle" onClick={runVecSearch}>
                  ▸ 也試試全文 / 語義搜尋（兜底）
                </button>
              ) : (
                <>
                  <div className="mira-search-section-head">🧬 語義兜底</div>
                  {vecLoading && <div className="empty-state" style={{ padding: '12px 0' }}>搜尋中…</div>}
                  {vecMatches && vecMatches.length === 0 && !vecLoading && (
                    <div className="empty-state" style={{ padding: '12px 0' }}>沒有更多結果。</div>
                  )}
                  {vecMatches && vecMatches.map((m, i) => <VecResult key={i} match={m} />)}
                </>
              )}
            </section>
          </>
        )}
      </div>
    </main>
  );
}

function VecResult({ match }: { match: SearchMatch }) {
  const pct = Math.round((match.score ?? 0) * 100);
  if (match.triplet) {
    const { subject, predicate, object } = match.triplet;
    return (
      <div className="mira-search-result">
        <div className="mira-search-result-meta"><span>關係</span>{pct > 0 && <span className="mira-search-score">{pct}%</span>}</div>
        <div className="mira-search-snippet" style={{ fontFamily: 'monospace' }}>{subject} ﹥﹥ {predicate} ﹥﹥ {object}</div>
      </div>
    );
  }
  const b = match.block;
  if (!b) return null;
  const snippet = (b.content ?? '').replace(/\n+/g, ' ').slice(0, 200);
  const href = b.type === 'wiki-page' && b.page_name
    ? `/mira/wiki/${encodeURIComponent(b.page_name)}`
    : b.page_name ? `/mira/feed#page=${encodeURIComponent(b.page_name)}` : `/mira/feed#raw=${encodeURIComponent(b.id)}`;
  return (
    <Link href={href} className="mira-search-result">
      <div className="mira-search-result-meta">
        <span>{b.type === 'wiki-page' ? '📚 Wiki' : '🌊 河道'}</span>
        {pct > 0 && <span className="mira-search-score">{pct}%</span>}
      </div>
      <div className="mira-search-snippet"><MarkdownView text={snippet + ((b.content ?? '').length > 200 ? '…' : '')} /></div>
    </Link>
  );
}

export default function MiraSearchPage() {
  return (
    <Suspense fallback={<div className="empty-state">載入中…</div>}>
      <SearchInner />
    </Suspense>
  );
}
