/* ========== AI Hooks + 決策儲存（自我迭代基礎） ==========
   - useAI(promptFn, deps, cacheKey)：呼叫 claude.complete，自動快取
   - decisionStore：localStorage 包裝，記憶接受/跳過/決策歷史
   - trajectory：軌跡計算
   - detectDrift：漂移偵測
*/

/* ========== Decision Store ========== */
const DS_KEY = 'kaios.v4';

const decisionStore = {
  get() {
    try { return JSON.parse(localStorage.getItem(DS_KEY)) || { decisions: [], accepted: [], skipped: [], aiCache: {} }; }
    catch { return { decisions: [], accepted: [], skipped: [], aiCache: {} }; }
  },
  save(data) {
    try { localStorage.setItem(DS_KEY, JSON.stringify(data)); } catch {}
  },
  addDecision(d) {
    const data = this.get();
    data.decisions = [{ ...d, time: Date.now() }, ...data.decisions].slice(0, 200);
    if (d.action === 'accept' && !data.accepted.includes(d.id)) data.accepted.push(d.id);
    if (d.action === 'skip' && !data.skipped.includes(d.id)) data.skipped.push(d.id);
    this.save(data);
    return data;
  },
  reset() {
    this.save({ decisions: [], accepted: [], skipped: [], aiCache: {} });
  },
  getCache(key) {
    return this.get().aiCache?.[key];
  },
  setCache(key, value) {
    const data = this.get();
    data.aiCache = data.aiCache || {};
    data.aiCache[key] = { value, time: Date.now() };
    // 只保留最近 50 筆
    const keys = Object.keys(data.aiCache);
    if (keys.length > 50) {
      const sorted = keys.sort((a, b) => data.aiCache[b].time - data.aiCache[a].time);
      const keep = sorted.slice(0, 50);
      const newCache = {};
      keep.forEach(k => newCache[k] = data.aiCache[k]);
      data.aiCache = newCache;
    }
    this.save(data);
  },
};

/* ========== useDecisions hook ========== */
function useDecisions() {
  const [state, setState] = useState(() => decisionStore.get());

  const reload = useCallback(() => setState(decisionStore.get()), []);

  const record = useCallback((kind, id, action) => {
    const next = decisionStore.addDecision({ kind, id, action });
    setState(next);
  }, []);

  const stats = useMemo(() => {
    const today = new Date().toDateString();
    const todayD = state.decisions.filter(d => new Date(d.time).toDateString() === today);
    const weekAgo = Date.now() - 7 * 86400000;
    const weekD = state.decisions.filter(d => d.time >= weekAgo);
    return {
      today: todayD.length,
      todayAccepted: todayD.filter(d => d.action === 'accept').length,
      todaySkipped: todayD.filter(d => d.action === 'skip').length,
      week: weekD.length,
      weekAccepted: weekD.filter(d => d.action === 'accept').length,
      total: state.decisions.length,
    };
  }, [state.decisions]);

  return { state, stats, record, reload, reset: () => { decisionStore.reset(); reload(); } };
}

/* ========== useAI: 快取 + 載入狀態 ========== */
function useAI(prompt, cacheKey, opts = {}) {
  const { skip = false, ttl = 7 * 86400000 /* 7 天 */ } = opts;
  const [text, setText] = useState(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);

  useEffect(() => {
    if (skip || !prompt || !cacheKey) return;

    // 檢查快取
    const cached = decisionStore.getCache(cacheKey);
    if (cached && (Date.now() - cached.time) < ttl) {
      setText(cached.value);
      return;
    }

    let cancel = false;
    setLoading(true);
    setError(null);

    (async () => {
      try {
        const result = await window.claude.complete(prompt);
        if (cancel) return;
        const t = (result || '').trim();
        setText(t);
        decisionStore.setCache(cacheKey, t);
      } catch (e) {
        if (!cancel) setError(e);
      } finally {
        if (!cancel) setLoading(false);
      }
    })();

    return () => { cancel = true; };
  }, [prompt, cacheKey, skip]);

  return { text, loading, error };
}

/* ========== Trajectory：軌跡計算 ========== */
function buildTrajectory(items) {
  const live = items
    .filter(i => i.status === '已上線' && i.b && i.publish)
    .sort((a, b) => (a.publish || '').localeCompare(b.publish || ''));

  if (live.length < 4) return null;

  // 取最近 10 支
  const recent10 = live.slice(-10);
  const last5 = live.slice(-5);
  const prev5 = live.slice(-10, -5);

  const avg = (arr, f) => arr.length ? arr.reduce((s, x) => s + f(x), 0) / arr.length : 0;

  const last5R = avg(last5, x => x.b.r);
  const prev5R = avg(prev5, x => x.b.r);
  const last5C = avg(last5, x => x.b.c);
  const prev5C = avg(prev5, x => x.b.c);
  const last5V = avg(last5, x => x.b.v);
  const prev5V = avg(prev5, x => x.b.v);

  return {
    recent10,
    last5R, prev5R, deltaR: last5R - prev5R,
    last5C, prev5C, deltaC: last5C - prev5C,
    last5V, prev5V, deltaV: last5V - prev5V,
    highRate: recent10.filter(x => x.b.p === 'high').length / recent10.length,
  };
}

/* ========== Drift 偵測 ========== */
function detectDrift(items) {
  const live = items
    .filter(i => i.status === '已上線' && i.b && i.publish)
    .sort((a, b) => (b.publish || '').localeCompare(a.publish || ''));

  const last5 = live.slice(0, 5);
  if (last5.length < 3) return null;

  // 統計最近 5 支的 hook_type 分布
  const hookCount = {};
  last5.forEach(it => {
    if (it.hook) hookCount[it.hook] = (hookCount[it.hook] || 0) + 1;
  });

  // 找出被過度使用的（>= 3 次）
  const overused = Object.entries(hookCount).filter(([_, n]) => n >= 3);
  if (overused.length === 0) return null;

  const [overCode, overN] = overused[0];

  // 找出該 hook 的勝率
  const overPattern = PATTERNS.proven_openings.find(p => p.code === overCode);
  if (!overPattern) return null;

  // 找有沒有更好的選擇
  const better = PATTERNS.proven_openings.filter(p => p.win_rate > overPattern.win_rate + 0.2);
  if (better.length === 0) return null;

  const bestAlt = better[0];

  return {
    overCode, overName: overPattern.name, overRate: overPattern.win_rate, overN,
    altCode: bestAlt.code, altName: bestAlt.name, altRate: bestAlt.win_rate,
    lastVids: last5.map(x => x.vid),
  };
}

/* ========== Sparkline 元件 ========== */
function Sparkline({ data, threshold, max, color = 'var(--kai)', height = 32, width = 120 }) {
  if (!data || data.length === 0) return null;
  const m = max || Math.max(...data, 1);
  const points = data.map((v, i) => {
    const x = (i / Math.max(1, data.length - 1)) * width;
    const y = height - (v / m) * height;
    return [x, y];
  });
  const path = points.map((p, i) => (i === 0 ? 'M' : 'L') + p[0].toFixed(1) + ',' + p[1].toFixed(1)).join(' ');
  const area = path + ` L ${width},${height} L 0,${height} Z`;
  const last = points[points.length - 1];

  return (
    <svg width={width} height={height} style={{ display: 'block', overflow: 'visible' }}>
      {threshold && (
        <line x1="0" x2={width} y1={height - (threshold / m) * height} y2={height - (threshold / m) * height}
          stroke="var(--warn)" strokeDasharray="2 3" strokeOpacity="0.5" />
      )}
      <path d={area} fill={color} fillOpacity="0.12" />
      <path d={path} stroke={color} strokeWidth="1.5" fill="none" />
      {points.map((p, i) => (
        <circle key={i} cx={p[0]} cy={p[1]} r={i === points.length - 1 ? 2.5 : 1.5} fill={color} />
      ))}
      <circle cx={last[0]} cy={last[1]} r="4" fill={color} fillOpacity="0.3" />
    </svg>
  );
}

/* ========== 軌跡卡 ========== */
function TrajectoryCard({ traj, onSelect }) {
  if (!traj) return null;
  const items = [
    { label: '3秒留存', last: traj.last5R, prev: traj.prev5R, delta: traj.deltaR, threshold: 55, format: v => v.toFixed(1) + '%', vals: traj.recent10.map(x => x.b.r) },
    { label: '完播率', last: traj.last5C, prev: traj.prev5C, delta: traj.deltaC, threshold: 40, format: v => v.toFixed(1) + '%', vals: traj.recent10.map(x => x.b.c) },
    { label: '觀看數', last: traj.last5V, prev: traj.prev5V, delta: traj.deltaV, format: v => fmtNum(Math.round(v)), vals: traj.recent10.map(x => x.b.v) },
  ];
  return (
    <div style={{
      padding: 18,
      background: 'var(--bg-1)',
      border: '1px solid var(--line)',
      borderRadius: 12,
      marginBottom: 14,
    }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 9, marginBottom: 14 }}>
        <Icon name="trending_up" size={14} color="var(--kai-bright)" />
        <span style={{ fontSize: 12, fontWeight: 600, color: 'var(--ink-1)' }}>近期軌跡</span>
        <span style={{ fontSize: 11, color: 'var(--ink-3)' }}>最近 5 支 vs 前 5 支</span>
        <div style={{ flex: 1 }} />
        <Pill color="var(--high)" bg="var(--high-tint)" border="var(--high-glow)" size="sm">
          高表現率 {Math.round(traj.highRate * 100)}%
        </Pill>
      </div>
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(220px, 1fr))', gap: 14 }}>
        {items.map(m => {
          const up = m.delta > (m.threshold ? 1 : 1000);
          const down = m.delta < (m.threshold ? -1 : -1000);
          const trendC = up ? 'var(--high)' : down ? 'var(--low)' : 'var(--ink-2)';
          return (
            <div key={m.label} style={{ display: 'flex', alignItems: 'center', gap: 14 }}>
              <Sparkline data={m.vals} threshold={m.threshold} color={trendC} height={36} width={90} />
              <div style={{ flex: 1, minWidth: 0 }}>
                <div style={{ fontSize: 10.5, color: 'var(--ink-3)', textTransform: 'uppercase', fontFamily: 'var(--mono)' }}>{m.label}</div>
                <div className="mono" style={{ fontSize: 16, fontWeight: 700, color: 'var(--ink-0)', lineHeight: 1.2 }}>
                  {m.format(m.last)}
                </div>
                <div className="mono" style={{ fontSize: 10.5, color: trendC, marginTop: 2 }}>
                  {up ? '↗' : down ? '↘' : '→'} {m.format(Math.abs(m.delta))} vs 前 5 支
                </div>
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
}

/* ========== 漂移警示卡 ========== */
function DriftCard({ drift, onSelect, items }) {
  if (!drift) return null;
  return (
    <div style={{
      padding: 16,
      background: 'linear-gradient(135deg, var(--warn-glow), transparent)',
      border: '1px solid var(--warn-glow)',
      borderRadius: 10,
      marginBottom: 14,
      display: 'flex', gap: 14, alignItems: 'flex-start',
      animation: 'slide-up 0.4s var(--ease-out)',
    }}>
      <Icon name="warn" size={18} color="var(--warn)" style={{ flexShrink: 0, marginTop: 2 }} />
      <div style={{ flex: 1 }}>
        <div style={{ fontSize: 12.5, fontWeight: 600, color: 'var(--warn)', marginBottom: 6 }}>
          ⚠ 公式漂移：最近 5 支有 {drift.overN} 支用 {drift.overCode}（{drift.overName}）
        </div>
        <div style={{ fontSize: 12, color: 'var(--ink-1)', lineHeight: 1.6, marginBottom: 8 }}>
          {drift.overCode} 勝率僅 <strong className="mono" style={{ color: 'var(--warn)' }}>{Math.round(drift.overRate * 100)}%</strong>。
          試試 <strong className="mono" style={{ color: 'var(--high)' }}>{drift.altCode}</strong>（{drift.altName}）— 勝率 <strong className="mono" style={{ color: 'var(--high)' }}>{Math.round(drift.altRate * 100)}%</strong>。
        </div>
        <div style={{ display: 'flex', gap: 5 }}>
          {drift.lastVids.map(v => {
            const item = items.find(i => i.vid === v);
            return (
              <button key={v} onClick={() => item && onSelect(item.id)} className="mono" style={{
                fontSize: 10, padding: '2px 6px', borderRadius: 3,
                background: 'var(--bg-2)', color: 'var(--ink-2)',
                border: '1px solid var(--line)',
              }}>{v}</button>
            );
          })}
        </div>
      </div>
    </div>
  );
}

/* ========== AI 標題候選 ========== */
function AITitleCandidates({ idea, opening, cta }) {
  const prompt = useMemo(() => `你是 KaiOS 的標題生成器，幫紅茶巴士／阿檸的老闆 Kai 寫短影音封面標題。

主題：${idea.topic}
${idea.tags?.length > 0 ? `標籤：${idea.tags.join('、')}` : ''}
公式：${opening.code}（${opening.name}）開場 + ${cta.code}（${cta.name}）CTA
成功實證：${[...opening.vid_evidence.slice(0, 2), ...cta.vid_evidence.slice(0, 2)].join('、')}

請生成 3 個吸引人的封面標題，要求：
- 繁體中文，每個 10-18 字
- 口語、直接、有衝突感或數字或懸念
- 符合品牌調性（老闆視角、不裝、有溫度）
- 每行一個，不要編號、不要解釋

直接輸出 3 行：`, [idea.id, opening.code, cta.code]);

  const cacheKey = `title:${idea.id}:${opening.code}:${cta.code}`;
  const { text, loading } = useAI(prompt, cacheKey);

  const titles = useMemo(() => {
    if (!text) return [];
    return text.split('\n').map(s => s.trim().replace(/^[-•*0-9.\s）)]+/, '').trim()).filter(Boolean).slice(0, 3);
  }, [text]);

  return (
    <div style={{ marginTop: 14 }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 7, marginBottom: 8 }}>
        <AIBadge />
        <span style={{ fontSize: 11, color: 'var(--ink-2)', fontWeight: 600 }}>標題候選</span>
        {loading && <span className="mono" style={{ fontSize: 10, color: 'var(--ink-3)' }}>生成中…</span>}
      </div>
      {loading ? (
        <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
          {[1, 2, 3].map(i => (
            <div key={i} className="skeleton" style={{ height: 32, borderRadius: 6 }} />
          ))}
        </div>
      ) : titles.length > 0 ? (
        <div style={{ display: 'flex', flexDirection: 'column', gap: 5 }}>
          {titles.map((t, i) => (
            <div key={i} style={{
              padding: '8px 12px',
              background: 'var(--bg-2)',
              border: '1px solid var(--line)',
              borderRadius: 6,
              display: 'flex', alignItems: 'center', gap: 10,
              animation: `slide-up 0.3s ${i * 0.08}s var(--ease-out) both`,
            }}>
              <span className="mono" style={{ fontSize: 10, color: 'var(--ink-3)', width: 12 }}>{i + 1}</span>
              <span style={{ flex: 1, fontSize: 13, color: 'var(--ink-0)', lineHeight: 1.4 }}>{t}</span>
              <button
                data-tip="複製"
                onClick={() => navigator.clipboard?.writeText(t)}
                style={{ color: 'var(--ink-3)', fontSize: 11, padding: '2px 6px' }}
              >複製</button>
            </div>
          ))}
        </div>
      ) : null}
    </div>
  );
}

/* ========== AI 影片解讀 ========== */
function AIReading({ item }) {
  const b = item.b;
  if (!b) return null;

  const prompt = useMemo(() => `你是 KaiOS 的表現分析助理。讀以下這支短影音的數據，給老闆 Kai 一段解讀。

標題：${item.title || item.topic}
${item.tags?.length > 0 ? `標籤：${item.tags.join('、')}` : ''}
${item.hook ? `開場類型：${item.hook}` : ''}
觀看：${b.v}
3 秒留存：${b.r}%（門檻 55%，高表現 70%）
完播率：${b.c}%（門檻 40%）
互動率：${b.e}%（門檻 1.5%）
表現分級：${b.p === 'high' ? '高表現' : b.p === 'low' ? '啞彈' : '一般'}
${b.str?.length > 0 ? `強項：${b.str.join('、')}` : ''}
${b.wk?.length > 0 ? `弱項：${b.wk.join('、')}` : ''}

寫一段 2-3 句的解讀：
- 第一句：這支贏（或輸）在哪
- 第二句：為什麼
- 第三句：下次可以怎麼做（如果適用）

繁體中文，直白不客套，像同事在 Slack 上發訊息。輸出純文字，不要分段或標題。`, [item.id]);

  const { text, loading } = useAI(prompt, `reading:${item.id}`);

  return (
    <div style={{
      marginTop: 14, padding: 14,
      background: 'linear-gradient(135deg, var(--kai-tint), transparent)',
      border: '1px solid var(--kai-glow)',
      borderRadius: 9,
    }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 7, marginBottom: 9 }}>
        <AIBadge />
        <span style={{ fontSize: 11, color: 'var(--ink-2)', fontWeight: 600 }}>AI 解讀</span>
        {loading && <span className="mono" style={{ fontSize: 10, color: 'var(--ink-3)' }}>分析中…</span>}
      </div>
      {loading ? (
        <div className="skeleton" style={{ height: 60, borderRadius: 6 }} />
      ) : text ? (
        <div style={{ fontSize: 13, color: 'var(--ink-1)', lineHeight: 1.7 }}>{text}</div>
      ) : null}
    </div>
  );
}

/* ========== AI Twins 雙胞胎對比 ========== */
function findTwins(items) {
  const live = items.filter(i => i.status === '已上線' && i.b);
  // 找標籤有重疊但表現差距大的兩支
  const pairs = [];
  for (let i = 0; i < live.length; i++) {
    for (let j = i + 1; j < live.length; j++) {
      const a = live[i], b = live[j];
      if (!a.tags?.length || !b.tags?.length) continue;
      const common = a.tags.filter(t => b.tags.includes(t));
      if (common.length === 0) continue;
      const diff = Math.abs(a.b.v - b.b.v);
      if (diff < 50000) continue;
      // 必須一支高一支非高
      const highA = a.b.p === 'high';
      const highB = b.b.p === 'high';
      if (highA === highB) continue;
      pairs.push({ a, b, common, diff, score: diff });
    }
  }
  return pairs.sort((x, y) => y.score - x.score).slice(0, 1)[0];
}

function TwinsCard({ items, onSelect }) {
  const pair = useMemo(() => findTwins(items), [items]);
  if (!pair) return null;
  const winner = pair.a.b.p === 'high' ? pair.a : pair.b;
  const loser = winner === pair.a ? pair.b : pair.a;

  const prompt = useMemo(() => `你是 KaiOS 的對比分析師。比較兩支主題相近但表現差距大的影片，告訴 Kai 差別在哪。

【贏】${winner.title || winner.topic}（${winner.vid}）
  觀看 ${winner.b.v} · 留存 ${winner.b.r}% · 完播 ${winner.b.c}%
  ${winner.hook ? `開場：${winner.hook}` : ''}
  ${winner.b.str?.length ? `強項：${winner.b.str.join('、')}` : ''}

【輸】${loser.title || loser.topic}（${loser.vid}）
  觀看 ${loser.b.v} · 留存 ${loser.b.r}% · 完播 ${loser.b.c}%
  ${loser.hook ? `開場：${loser.hook}` : ''}
  ${loser.b.wk?.length ? `弱項：${loser.b.wk.join('、')}` : ''}

共同標籤：${pair.common.join('、')}

請用 2-3 句話告訴 Kai 為什麼差距這麼大，能學到什麼。繁體中文，直白。`, [winner.id, loser.id]);

  const { text, loading } = useAI(prompt, `twins:${winner.id}:${loser.id}`);

  return (
    <div style={{
      padding: 18, background: 'var(--bg-1)',
      border: '1px solid var(--line)', borderRadius: 12,
      marginBottom: 14,
    }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 9, marginBottom: 12 }}>
        <AIBadge />
        <span style={{ fontSize: 12, fontWeight: 600 }}>雙胞胎對比</span>
        <span style={{ fontSize: 11, color: 'var(--ink-3)' }}>主題相近，表現差距 {fmtNum(pair.diff)}</span>
      </div>

      <div style={{ display: 'grid', gridTemplateColumns: '1fr 28px 1fr', gap: 10, alignItems: 'stretch', marginBottom: 12 }}>
        <TwinSide item={winner} kind="win" onClick={() => onSelect(winner.id)} />
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 18, color: 'var(--ink-3)' }}>vs</div>
        <TwinSide item={loser} kind="lose" onClick={() => onSelect(loser.id)} />
      </div>

      <div style={{ display: 'flex', gap: 4, marginBottom: 10, flexWrap: 'wrap' }}>
        <span style={{ fontSize: 10.5, color: 'var(--ink-3)', fontFamily: 'var(--mono)' }}>共同：</span>
        {pair.common.map(t => <Pill key={t} color="var(--kai-bright)" bg="var(--kai-tint)" size="xs">#{t}</Pill>)}
      </div>

      {loading ? (
        <div className="skeleton" style={{ height: 50, borderRadius: 6 }} />
      ) : text ? (
        <div style={{ padding: 10, background: 'var(--bg-2)', borderRadius: 6, fontSize: 12.5, color: 'var(--ink-1)', lineHeight: 1.6 }}>{text}</div>
      ) : null}
    </div>
  );
}

function TwinSide({ item, kind, onClick }) {
  const c = kind === 'win' ? 'var(--high)' : 'var(--low)';
  return (
    <button onClick={onClick} style={{
      padding: 12, background: kind === 'win' ? 'var(--high-tint)' : 'var(--low-tint)',
      border: `1px solid ${kind === 'win' ? 'var(--high-glow)' : 'var(--low-glow)'}`,
      borderRadius: 8, textAlign: 'left',
      display: 'flex', flexDirection: 'column', gap: 4,
    }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 5 }}>
        <span className="mono" style={{ fontSize: 9.5, color: c, fontWeight: 700 }}>{kind === 'win' ? '🏆 贏' : '❌ 輸'}</span>
        <span className="mono" style={{ fontSize: 9.5, color: 'var(--ink-3)' }}>{item.vid}</span>
      </div>
      <div style={{ fontSize: 12, color: 'var(--ink-0)', lineHeight: 1.35, fontWeight: 500, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
        {item.title || item.topic}
      </div>
      <div className="mono" style={{ fontSize: 10.5, color: c, fontWeight: 600 }}>{fmtNum(item.b.v)} · 留 {item.b.r.toFixed(0)}%</div>
    </button>
  );
}

/* ========== AI Badge（共用） ========== */
function AIBadge({ size = 'sm' }) {
  const sizes = { sm: { fs: 9.5, pad: '3px 7px' }, md: { fs: 11, pad: '4px 9px' } };
  const s = sizes[size];
  return (
    <div style={{
      display: 'inline-flex', alignItems: 'center', gap: 5,
      padding: s.pad,
      background: 'linear-gradient(135deg, var(--kai-bright), var(--kai-deep))',
      borderRadius: 5, fontSize: s.fs, fontWeight: 700,
      color: '#fff', letterSpacing: '0.04em',
      boxShadow: '0 1px 4px var(--kai-glow)',
    }}>
      <Icon name="sparkle" size={s.fs} />
      <span>AI</span>
    </div>
  );
}

Object.assign(window, {
  decisionStore, useDecisions, useAI,
  buildTrajectory, detectDrift,
  Sparkline, TrajectoryCard, DriftCard,
  AITitleCandidates, AIReading, TwinsCard, AIBadge,
  findTwins,
});
