// ———— Library Page ————
function PlatformBadge({ platform }) {
  if (!platform) return null;
  return <span className={`badge platform ${platform.cls || "neutral"}`}>{platform.label || "?"}</span>;
}

function MechBadge({ name }) {
  const cls = MECH_COLORS[name] || "neutral";
  return <span className={`badge ${cls}`}>{name}</span>;
}

// Map API video to display format
function _mapVideo(v) {
  const platMap = { youtube: PLATFORMS[0], tiktok: PLATFORMS[1], bilibili: PLATFORMS[2] };
  const hue = (v.id || "").split("").reduce((a, c) => a + c.charCodeAt(0), 200) % 360;
  return {
    ...v,
    type: v.video_type,
    structure: v.story_structure,
    mechanisms: Array.isArray(v.viral_mechanisms) ? v.viral_mechanisms : [],
    score: v.score_total || 0,
    publishedAt: v.published_at || "",
    duration: v.duration_sec || 0,
    channel: "",   // not in ChromaDB
    views: 0,      // not in ChromaDB
    thumbHue: hue,
    platform: platMap[v.platform] || { id: v.platform, label: v.platform?.slice(0,2).toUpperCase() || "?", cls: "neutral", name: v.platform },
    emotionPath: (v.emotion_curve || "").split(/[→\-]/).map(s => s.trim()).filter(Boolean),
    transferable: Array.isArray(v.viral_mechanisms) ? v.viral_mechanisms : [],
    structure_labels_arr: Array.isArray(v.structure_labels) ? v.structure_labels : [],
    radar: {
      "综合评分": v.score_total || 0,
    },
    metricsHistory: [],
  };
}

function VideoCard({ video, onClick }) {
  return (
    <div className="video-card" onClick={onClick}>
      <div className="video-thumb" style={{ background: `linear-gradient(135deg, oklch(0.78 0.05 ${video.thumbHue}), oklch(0.5 0.09 ${(video.thumbHue + 40) % 360}))` }}>
        <div className="video-thumb-overlay" />
        <div className="plat-corner"><PlatformBadge platform={video.platform} /></div>
        <div className="video-thumb-label">{video.id}</div>
        <div className="video-duration">{formatDuration(video.duration)}</div>
      </div>
      <div className="video-body">
        <div className="video-title">{video.title}</div>
        <div className="video-channel">
          <span>{video.type}</span>
          <span>· {formatDate(video.publishedAt)}</span>
        </div>
        <div className="video-mechs">
          {video.mechanisms.slice(0, 3).map(m => <MechBadge key={m} name={m} />)}
          {video.mechanisms.length > 3 && <span className="badge neutral">+{video.mechanisms.length - 3}</span>}
        </div>
        <div className="video-stats">
          <span className="stat"><Icon.Star /> <span className="val">{video.score}</span></span>
          <span className="stat" style={{ color: "var(--accent-ink)" }}>{video.reproducibility}</span>
        </div>
      </div>
    </div>
  );
}

function RadarChart({ data, weights }) {
  const keys = Object.keys(data);
  const N = keys.length;
  const cx = 120, cy = 110, R = 80;
  const pt = (i, v) => {
    const a = -Math.PI / 2 + (i / N) * Math.PI * 2;
    const r = (v / 100) * R;
    return [cx + r * Math.cos(a), cy + r * Math.sin(a)];
  };
  const axisPt = (i, r=R) => {
    const a = -Math.PI / 2 + (i / N) * Math.PI * 2;
    return [cx + r * Math.cos(a), cy + r * Math.sin(a)];
  };
  const points = keys.map((k, i) => pt(i, data[k]).join(",")).join(" ");
  return (
    <div>
      <svg viewBox="0 0 240 220" width="100%" height={200}>
        {[0.25, 0.5, 0.75, 1].map(r => (
          <polygon key={r}
            points={keys.map((_, i) => axisPt(i, R * r).join(",")).join(" ")}
            fill="none" stroke="oklch(0.88 0.008 255)" strokeWidth="1" />
        ))}
        {keys.map((k, i) => {
          const [x, y] = axisPt(i);
          return <line key={k} x1={cx} y1={cy} x2={x} y2={y} stroke="oklch(0.9 0.008 255)" strokeWidth="1" />;
        })}
        <polygon points={points} fill="oklch(0.52 0.16 258 / 0.18)" stroke="oklch(0.52 0.16 258)" strokeWidth="1.5" />
        {keys.map((k, i) => {
          const [x, y] = pt(i, data[k]);
          return <circle key={k} cx={x} cy={y} r="3" fill="oklch(0.52 0.16 258)" />;
        })}
        {keys.map((k, i) => {
          const [x, y] = axisPt(i, R + 18);
          return <text key={k} x={x} y={y} fontSize="10.5" textAnchor="middle" dominantBaseline="middle" fill="var(--ink-2)" fontFamily="var(--font-sans)">{k}</text>;
        })}
      </svg>
    </div>
  );
}

function LineChart({ data }) {
  if (!data || data.length < 2) return <div style={{ color: "var(--ink-3)", fontSize: 12, padding: "12px 0" }}>暂无播放量历史数据</div>;
  const W = 500, H = 160, P = 28;
  const max = Math.max(...data.map(d => d.v));
  const min = Math.min(...data.map(d => d.v));
  const x = (i) => P + (i / (data.length - 1)) * (W - P * 2);
  const y = (v) => H - P - ((v - min) / Math.max(1, max - min)) * (H - P * 2);
  const pts = data.map((d, i) => `${x(i)},${y(d.v)}`).join(" ");
  const area = `M ${x(0)},${H - P} L ${data.map((d, i) => `${x(i)},${y(d.v)}`).join(" L ")} L ${x(data.length-1)},${H-P} Z`;
  return (
    <div>
      <svg viewBox={`0 0 ${W} ${H}`} width="100%" height={H}>
        {[0.25, 0.5, 0.75].map(f => (
          <line key={f} x1={P} x2={W-P} y1={P + (H-P*2)*f} y2={P + (H-P*2)*f} stroke="oklch(0.93 0.008 255)" strokeDasharray="2 3" />
        ))}
        <path d={area} fill="oklch(0.52 0.16 258 / 0.12)" />
        <polyline points={pts} fill="none" stroke="oklch(0.52 0.16 258)" strokeWidth="1.8" />
        {data.map((d, i) => i % 4 === 0 && (
          <circle key={i} cx={x(i)} cy={y(d.v)} r="2.5" fill="#fff" stroke="oklch(0.52 0.16 258)" strokeWidth="1.4" />
        ))}
        <text x={P} y={P-6} fontSize="9.5" fill="var(--ink-3)" fontFamily="var(--font-mono)">{formatViews(max)}</text>
        <text x={P} y={H-P+14} fontSize="9.5" fill="var(--ink-3)" fontFamily="var(--font-mono)">{formatViews(min)}</text>
      </svg>
    </div>
  );
}

function Drawer({ video, onClose }) {
  if (!video) return null;
  return (
    <>
      <div className="drawer-backdrop" onClick={onClose} />
      <div className="drawer" onClick={e=>e.stopPropagation()}>
        <div className="drawer-header">
          <h3>视频详情 · {video.id}</h3>
          <button className="drawer-close" onClick={onClose}><Icon.Close/></button>
        </div>
        <div className="drawer-body">
          <div className="drawer-hero-thumb" style={{ background: `linear-gradient(135deg, oklch(0.78 0.05 ${video.thumbHue}), oklch(0.5 0.09 ${(video.thumbHue + 40) % 360}))` }}>
            <div className="video-thumb-overlay" />
            <div className="plat-corner" style={{position:"absolute", top:12, right:12}}><PlatformBadge platform={video.platform}/></div>
            <div style={{ position:"absolute", bottom: 12, left: 12, color: "#fff", fontSize: 11, fontFamily:"var(--font-mono)", background:"rgba(0,0,0,0.5)", padding:"3px 8px", borderRadius:4 }}>
              {formatDuration(video.duration)}
            </div>
          </div>
          <h2 style={{ margin: "8px 0 10px", fontSize: 16, fontWeight: 600, lineHeight: 1.45 }}>{video.title}</h2>
          <dl className="kv-grid">
            <dt>发布时间</dt><dd>{formatDate(video.publishedAt)}</dd>
            <dt>视频类型</dt><dd>{video.type}</dd>
            <dt>剧情结构</dt><dd>{video.structure}</dd>
            <dt>钩子类型</dt><dd>{video.hook_type || "—"}</dd>
            <dt>综合评分</dt><dd>{video.score} / 100</dd>
            <dt>可复刻</dt><dd>{video.reproducibility}</dd>
          </dl>

          {video.url && (
            <div style={{ marginTop: 14 }}>
              <a href={video.url} target="_blank" rel="noopener" className="btn" style={{ width: "100%" }}>
                查看原视频 <Icon.External size={12}/>
              </a>
            </div>
          )}

          {video.emotionPath && video.emotionPath.length > 0 && (
            <div className="drawer-subsection">
              <h4>情绪路径</h4>
              <div className="emotion-path">
                {video.emotionPath.map((e, i) => (
                  <div className="emotion-step" key={i}>
                    <span className="step-num">0{i+1}</span>
                    <span className="step-ink">{e}</span>
                  </div>
                ))}
              </div>
            </div>
          )}

          {video.structure_labels_arr && video.structure_labels_arr.length > 0 && (
            <div className="drawer-subsection">
              <h4>结构标签</h4>
              <div style={{ display: "flex", flexWrap: "wrap", gap: 6 }}>
                {video.structure_labels_arr.map(l => <span key={l} className="badge neutral">{l}</span>)}
              </div>
            </div>
          )}

          {video.transferable && video.transferable.length > 0 && (
            <div className="drawer-subsection">
              <h4>可迁移机制</h4>
              <div className="blob-paragraph">
                {video.transferable.map((t, i) => (
                  <div key={i} style={{ marginBottom: 4 }}>· {t}</div>
                ))}
              </div>
            </div>
          )}

          <div className="drawer-subsection">
            <h4>评分</h4>
            <RadarChart data={video.radar} weights={{ "综合评分": 100 }} />
          </div>
        </div>
      </div>
    </>
  );
}

function MultiPick({ label, options, value, onChange, single }) {
  const [open, setOpen] = useState(false);
  const text = value.length === 0 ? "全部" : single ? value[0] : `已选`;
  return (
    <div className="filter-item" style={{ position: "relative" }}>
      <span className="filter-label">{label}</span>
      <div className="multi-dropdown" onClick={()=>setOpen(o=>!o)}>
        <span>{text}</span>
        {value.length > 0 && !single && <span className="count">{value.length}</span>}
      </div>
      {open && (
        <>
          <div style={{ position: "fixed", inset: 0, zIndex: 10 }} onClick={()=>setOpen(false)} />
          <div style={{ position: "absolute", top: "100%", left: 0, right: 0, marginTop: 4, background: "#fff", border: "1px solid var(--line-strong)", borderRadius: 6, boxShadow: "var(--shadow-md)", zIndex: 11, padding: 4, maxHeight: 260, overflow: "auto" }}>
            {options.map(opt => {
              const active = value.includes(opt);
              return (
                <div key={opt}
                  onClick={() => {
                    if (single) { onChange(active ? [] : [opt]); setOpen(false); }
                    else onChange(active ? value.filter(x=>x!==opt) : [...value, opt]);
                  }}
                  style={{ padding: "6px 10px", fontSize: 12.5, borderRadius: 4, cursor: "pointer", background: active ? "var(--accent-weak)" : "transparent", color: active ? "var(--accent-ink)" : "var(--ink-1)", display: "flex", alignItems: "center", gap: 6 }}>
                  {!single && <span style={{ width: 12, height: 12, border: "1.2px solid var(--line-strong)", borderRadius: 2, background: active ? "var(--accent)" : "#fff", display: "flex", alignItems: "center", justifyContent: "center", color: "#fff" }}>{active && <Icon.Check size={8}/>}</span>}
                  {opt}
                </div>
              );
            })}
          </div>
        </>
      )}
    </div>
  );
}

function LibraryPage({ openDetail }) {
  const [allVideos, setAllVideos] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState("");

  const [types, setTypes] = useState([]);
  const [mechs, setMechs] = useState([]);
  const [structure, setStructure] = useState([]);
  const [scoreMin, setScoreMin] = useState(0);
  const [plats, setPlats] = useState([]);
  const [page, setPage] = useState(1);

  const loadVideos = async () => {
    setLoading(true);
    setError("");
    try {
      const data = await window.API.listVideos({ page_size: 200 });
      setAllVideos(data.map(_mapVideo));
    } catch (e) {
      setError(e.message);
    } finally {
      setLoading(false);
    }
  };

  useEffect(() => { loadVideos(); }, []);

  const filtered = useMemo(() => {
    return allVideos.filter(v =>
      (types.length === 0 || types.includes(v.type)) &&
      (mechs.length === 0 || v.mechanisms.some(m => mechs.includes(m))) &&
      (structure.length === 0 || structure.includes(v.structure)) &&
      (v.score >= scoreMin) &&
      (plats.length === 0 || plats.some(p => v.platform.id === p || v.platform === p))
    );
  }, [allVideos, types, mechs, structure, scoreMin, plats]);

  const pageSize = 18;
  const totalPages = Math.max(1, Math.ceil(filtered.length / pageSize));
  const shown = filtered.slice((page - 1) * pageSize, page * pageSize);

  const reset = () => { setTypes([]); setMechs([]); setStructure([]); setScoreMin(0); setPlats([]); setPage(1); };

  return (
    <>
      <div className="page-header">
        <div>
          <h1 className="page-title">知识库</h1>
          <div className="page-subtitle">
            共 <strong>{allVideos.length}</strong> 条 · 筛选 <strong style={{color:"var(--accent-ink)"}}>{filtered.length}</strong> 条 · 每页 18 条
          </div>
        </div>
        <div className="btn-row">
          <button className="btn" onClick={loadVideos} disabled={loading}>{loading ? <Icon.Spinner /> : "刷新"}</button>
        </div>
      </div>

      {error && (
        <div style={{ margin: "0 0 16px", padding: "10px 14px", background: "#fff0f0", border: "1px solid var(--danger)", borderRadius: 6, fontSize: 12, color: "var(--danger)" }}>
          {error}
        </div>
      )}

      <div className="filter-bar">
        <MultiPick label="视频类型" options={TYPES} value={types} onChange={(v)=>{setTypes(v); setPage(1);}} />
        <MultiPick label="爆款机制" options={MECHS} value={mechs} onChange={(v)=>{setMechs(v); setPage(1);}} />
        <MultiPick label="剧情结构" options={STRUCTURES} value={structure} onChange={(v)=>{setStructure(v); setPage(1);}} single />
        <div className="filter-item">
          <span className="filter-label">评分 ≥ {scoreMin}</span>
          <div className="range-slider">
            <input type="range" min={0} max={100} step={5} value={scoreMin} onChange={e=>{setScoreMin(+e.target.value); setPage(1);}} />
            <div className="range-values"><span>0</span><span>100</span></div>
          </div>
        </div>
        <div className="filter-item">
          <span className="filter-label">平台</span>
          <div className="platform-chips">
            {PLATFORMS.map(p => (
              <button key={p.id}
                className={`platform-chip ${plats.includes(p.id)?"active":""}`}
                onClick={()=>{ setPlats(plats.includes(p.id) ? plats.filter(x=>x!==p.id) : [...plats, p.id]); setPage(1); }}>
                {p.label}
              </button>
            ))}
          </div>
        </div>
        {(types.length || mechs.length || structure.length || scoreMin > 0 || plats.length) ? (
          <div className="filter-item">
            <button className="btn ghost sm" onClick={reset}>清除筛选</button>
          </div>
        ) : null}
      </div>

      {loading ? (
        <div style={{ padding: "60px 0", textAlign: "center", color: "var(--ink-3)" }}>
          <Icon.Spinner size={20} /> 加载中...
        </div>
      ) : shown.length === 0 ? (
        <div style={{ padding: "60px 0", textAlign: "center", color: "var(--ink-3)" }}>
          {allVideos.length === 0 ? "知识库为空，请先在采集页提交视频链接" : "当前筛选条件下无结果"}
        </div>
      ) : (
        <>
          <div className="video-grid">
            {shown.map(v => (
              <VideoCard key={v.id} video={v} onClick={() => openDetail(v)} />
            ))}
          </div>

          {totalPages > 1 && (
            <div className="pagination">
              <button className="btn ghost sm" disabled={page === 1} onClick={()=>setPage(p=>p-1)}>← 上一页</button>
              <span style={{ fontSize: 12, color: "var(--ink-3)", fontFamily: "var(--font-mono)" }}>
                {page} / {totalPages}
              </span>
              <button className="btn ghost sm" disabled={page === totalPages} onClick={()=>setPage(p=>p+1)}>下一页 →</button>
            </div>
          )}
        </>
      )}
    </>
  );
}
window.LibraryPage = LibraryPage;
window.Drawer = Drawer;
