import { useEffect, useMemo, useState } from "react";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { api, ApiError } from "../lib/api";
import { AppIcon } from "../components/PixelIcons";
import { Card, ErrorState, Loading, Tag } from "../components/ui";
import SkillCardPanel from "../components/SkillCardPanel";
import SkillMistakeBook from "../components/SkillMistakeBook";
import { hasCardNotesInIndex, type SkillNotesIndex } from "../lib/skillCardNotes";
import {
  fetchSkillState,
  markPrincipleReadApi,
  migrateLocalSkillDataOnce,
} from "../lib/skillProgress";
import { syncPlayerProgress } from "../lib/learningSync";
import {
  buildSkillTree,
  countProgress,
  filterSkillTree,
  isPrincipleMastered,
  masteryLabel,
  nextAchievement,
  RARITY_LABEL,
  RARITY_LEGEND,
  type DrillMistakeBook,
  type SkillProgressMap,
  type SkillTreeNode,
} from "../lib/skillTree";

export default function Skills() {
  const qc = useQueryClient();
  const kbQ = useQuery({ queryKey: ["knowledge"], queryFn: api.knowledge, staleTime: 3600_000 });
  const skillStateQ = useQuery({
    queryKey: ["skill-state"],
    queryFn: async () => {
      const migrated = await migrateLocalSkillDataOnce();
      if (migrated) return migrated;
      return fetchSkillState();
    },
    staleTime: 30_000,
  });
  const [progress, setProgress] = useState<SkillProgressMap>({});
  const [mistakeBook, setMistakeBook] = useState<DrillMistakeBook>([]);
  const [notesIndex, setNotesIndex] = useState<SkillNotesIndex>({});
  const [historyRevision, setHistoryRevision] = useState(0);

  useEffect(() => {
    if (!skillStateQ.data) return;
    setProgress(skillStateQ.data.progressMap);
    setMistakeBook(skillStateQ.data.openMistakes);
    setNotesIndex(skillStateQ.data.notesIndex || {});
  }, [skillStateQ.data]);
  const [selected, setSelected] = useState<SkillTreeNode | null>(null);
  const [openTab, setOpenTab] = useState<"detail" | "drill" | "records" | "coach" | "notes">("detail");
  const [notesRevision, setNotesRevision] = useState(0);
  const [query, setQuery] = useState("");
  const [collapsed, setCollapsed] = useState<Record<string, boolean>>({});

  const principles = kbQ.data?.principles || [];
  const groups = useMemo(
    () => buildSkillTree(principles, kbQ.data?.principleMap?.body),
    [principles, kbQ.data?.principleMap?.body],
  );
  const filtered = useMemo(() => filterSkillTree(groups, query), [groups, query]);
  const stats = useMemo(() => countProgress(principles, progress), [principles, progress]);
  const achievement = nextAchievement(stats.read, stats.mastered, stats.total);
  const nodeById = useMemo(() => {
    const map = new Map<string, SkillTreeNode>();
    for (const g of groups) for (const n of g.nodes) map.set(n.principle.id, n);
    return map;
  }, [groups]);
  const openMistakeByPrinciple = useMemo(() => {
    const map = new Map<string, (typeof mistakeBook)[number]>();
    for (const m of mistakeBook) {
      if (!m.resolved) map.set(m.principleId, m);
    }
    return map;
  }, [mistakeBook]);

  const openNode = (
    node: SkillTreeNode,
    tab: "detail" | "drill" | "records" | "coach" | "notes" = "detail",
  ) => {
    setOpenTab(tab);
    setSelected(node);
    void markPrincipleReadApi(node.principle.id).then((p) => {
      setProgress(p);
      syncPlayerProgress(qc);
    });
  };

  if (kbQ.isLoading || skillStateQ.isLoading) return <Loading label="載入心法技能樹…" />;
  if (kbQ.error instanceof ApiError && kbQ.error.code === "knowledge_not_built") {
    return (
      <>
        <div className="page-head">
          <div className="eyebrow">修煉 · 心法</div>
          <h1 className="page-title-row">
            <AppIcon name="scroll" size={38} framed glow variant="hero" />
            心法技能樹
          </h1>
        </div>
        <Card className="pad-lg">
          <p className="muted">
            知識庫尚未建立。請在 <code>app/</code> 目錄執行 <code>npm run build:knowledge</code> 後重新整理。
          </p>
        </Card>
      </>
    );
  }
  if (kbQ.error || !kbQ.data) return <ErrorState detail={(kbQ.error as Error)?.message} />;

  return (
    <div className="skill-page">
      <div className="page-head">
        <div className="eyebrow">修煉 · 心法</div>
        <h1 className="page-title-row">
          <AppIcon name="scroll" size={38} framed glow variant="hero" />
          心法技能樹
        </h1>
        <p>
          {stats.total} 條投資心法分群成技能樹。讀懂解鎖卡牌，通過修煉試煉（含 K 線圖判）由教練評分——95 分以上才算精通。
        </p>
      </div>

      <Card className="skill-progress-card skill-quest-board">
        <div className="skill-progress-row">
          <div className="kpi skill-kpi">
            <div className="k-v">
              {stats.read} / {stats.total}
            </div>
            <div className="k-l">已解鎖心法</div>
          </div>
          <div className="skill-progress-bar-wrap">
            <div className="xp-label">
              <span>收集進度</span>
              <span>{stats.pct}%</span>
            </div>
            <div className="bar">
              <span style={{ width: `${stats.pct}%` }} />
            </div>
          </div>
          <div className="skill-progress-side">
            <Tag tone={stats.mastered >= stats.total ? "gold" : undefined}>
              <span className="tag-icon-inline">
                <AppIcon name={achievement.icon} size={14} framed={false} />
              </span>
              {achievement.kind === "master"
                ? `下一個成就：精通 ${stats.mastered}/${achievement.at} → ${achievement.label}`
                : `下一個成就：${achievement.at} 條 → ${achievement.label}`}
            </Tag>
            <span className="small muted">
              精通 {stats.mastered} 條
              {stats.mastered >= stats.total ? " · 全精通達成" : ""}
            </span>
          </div>
        </div>
      </Card>

      <Card className="skill-rarity-legend pad-sm">
        <div className="skill-rarity-legend-head">
          <AppIcon name="sword" size={20} framed variant="hero" />
          <strong>精通怎麼解鎖？</strong>
        </div>
        <p className="small muted skill-drill-rule">
          修煉試煉三關（第 3 關附歷史 K 線）→ 教練評分 1–100 → <b>95 分以上</b>才算精通（可重考，取歷史最高分）。
        </p>
        <div className="skill-rarity-legend-head mt-s">
          <AppIcon name="cards" size={20} framed variant="hero" />
          <strong>稀有度怎麼評？</strong>
        </div>
        <div className="skill-rarity-legend-grid">
          {RARITY_LEGEND.map((row) => (
            <div className="skill-rarity-legend-item" key={row.rarity}>
              <span className={`pill-rar rar-${row.rarity}`}>{row.label}</span>
              <span className="small muted">{row.rule}</span>
            </div>
          ))}
        </div>
      </Card>

      <div className="skill-toolbar">
        <input
          type="search"
          className="skill-search backpack-filter"
          placeholder="搜尋心法卷軸…"
          value={query}
          onChange={(e) => setQuery(e.target.value)}
          spellCheck={false}
        />
        {query ? (
          <button type="button" className="btn-ghost sm" onClick={() => setQuery("")}>
            清除
          </button>
        ) : null}
      </div>

      {!filtered.length ? (
        <p className="muted">找不到符合「{query}」的心法。</p>
      ) : (
        filtered.map((g) => {
          const isCollapsed = collapsed[g.id];
          return (
            <section className="skill-group" key={g.id}>
              <button
                type="button"
                className="skill-group-head"
                onClick={() => setCollapsed((c) => ({ ...c, [g.id]: !c[g.id] }))}
                aria-expanded={!isCollapsed}
              >
                <h3>
                  <AppIcon name={g.icon} size={22} framed variant="hero" />
                  {g.name}
                  <Tag>{g.nodes.length}</Tag>
                </h3>
                <span className="skill-group-when">{g.whenToUse}</span>
                <span className="skill-group-toggle">{isCollapsed ? "▼ 展開" : "▲ 收合"}</span>
              </button>
              {!isCollapsed ? (
                <div className="skill-row card-wall">
                  {g.nodes.map((n) => {
                    const entry = progress[n.principle.id];
                    const mastery = masteryLabel(entry);
                    const mastered = isPrincipleMastered(entry);
                    const hasOpenMistake = openMistakeByPrinciple.has(n.principle.id);
                    const cls = [
                      "skill-node",
                      `rar-${n.rarity}`,
                      selected?.principle.id === n.principle.id ? "selected" : "",
                      entry?.reads ? "read" : "",
                      mastered ? "mastered" : "",
                      n.core ? "core" : "",
                    ]
                      .filter(Boolean)
                      .join(" ");
                    const rarLabel = RARITY_LABEL[n.rarity];
                    void notesRevision;
                    const hasCardNotes = hasCardNotesInIndex(notesIndex, n.principle.id);
                    return (
                      <button
                        key={n.principle.id}
                        type="button"
                        className={cls}
                        onClick={() => openNode(n)}
                      >
                        <div className="sn-art" aria-hidden>
                          <AppIcon
                            name={n.artIcon}
                            size={36}
                            framed
                            glow={mastered}
                            variant="hero"
                          />
                          {mastered ? (
                            <span className="sn-art-master">
                              <AppIcon name="coin" size={16} framed variant="nav" />
                            </span>
                          ) : null}
                        </div>
                        <div className="sn-body">
                          <div className="sn-nm">{n.cleanTitle}</div>
                          <div className="sn-foot">
                            <span className="sn-mastery">{mastery}</span>
                            <span className="sn-foot-tags">
                              {hasOpenMistake ? (
                                <span className="sn-mistake-dot" title="有待補錯題" aria-label="有待補錯題" />
                              ) : null}
                              {hasCardNotes ? (
                                <span className="sn-note-dot" title="有修煉筆記">
                                  <AppIcon name="scroll" size={12} framed={false} />
                                </span>
                              ) : null}
                              <span className={`pill-rar rar-${n.rarity}`}>{rarLabel}</span>
                            </span>
                          </div>
                        </div>
                      </button>
                    );
                  })}
                </div>
              ) : null}
            </section>
          );
        })
      )}

      <SkillMistakeBook
        book={mistakeBook}
        nodeById={nodeById}
        onBookChange={(book) => {
          setMistakeBook(book);
          setHistoryRevision((n) => n + 1);
        }}
        onRetry={(node) => openNode(node, "drill")}
        onViewRecords={(node) => openNode(node, "records")}
      />

      <div className="editorial mt">
        技能樹的設計，就是把 110 條心法從「綁在某一集」變成「可跨情境重用的招式」——情境細節收進每張卡的歷史實例，不再洗版掉原則本身。
      </div>

      <SkillCardPanel
        node={selected}
        progress={progress}
        initialTab={openTab}
        historyRevision={historyRevision}
        hasOpenMistake={selected ? openMistakeByPrinciple.has(selected.principle.id) : false}
        onClose={() => setSelected(null)}
        onProgressChange={setProgress}
        onMistakeBookChange={(book) => {
          setMistakeBook(book);
          setHistoryRevision((n) => n + 1);
          syncPlayerProgress(qc);
        }}
        notesIndex={notesIndex}
        onNotesIndexChange={(index) => {
          setNotesIndex(index);
          syncPlayerProgress(qc);
        }}
        onNotesChange={() => {
          setNotesRevision((n) => n + 1);
          syncPlayerProgress(qc);
        }}
        onAttemptRecorded={() => {
          setHistoryRevision((n) => n + 1);
          syncPlayerProgress(qc);
        }}
      />

      <div className="disclaimer">心法內容來自知識庫整理，僅供學習，不構成投資建議</div>
    </div>
  );
}
