/* ============================================================
   昆虫整理人格 · 问卷系统 — 主程序
   ============================================================ */
const { useState, useEffect, useRef } = React;
const STORE_KEY = "insect-organizing-style-v1";

/* 默认旋钮 */
const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "accent": "#5BA8A0",
  "font": "serif",
  "mode": "day",
  "anim": 6
}/*EDITMODE-END*/;

const ACCENTS = [
  { c: "#5BA8A0", n: "蓝绿" },
  { c: "#E0795F", n: "蝴蝶橘" },
  { c: "#F5B547", n: "蜂蜜黄" },
  { c: "#163A32", n: "深松绿" },
  { c: "#B8A8D6", n: "柔软紫" },
  { c: "#1F2A2E", n: "墨黑" },
];
const FONTS = {
  serif: '"Noto Serif SC", serif',
  sans: '"Noto Sans SC", system-ui, sans-serif',
  hand: '"LXGW WenKai", "Noto Serif SC", serif',
};

function inkFor(hex) {
  const h = hex.replace("#", "");
  const r = parseInt(h.substr(0, 2), 16), g = parseInt(h.substr(2, 2), 16), b = parseInt(h.substr(4, 2), 16);
  const lum = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
  return lum > 0.62 ? "#1F2A2E" : "#FAF6EE";
}

function readShared() {
  const m = location.hash.match(/^#r=(.+)/);
  if (!m) return null;
  try { return JSON.parse(decodeURIComponent(escape(atob(m[1])))); } catch (e) { return null; }
}

function App() {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const survey = window.SURVEY;
  const source = survey.source || {};
  const questions = survey.questions;

  const shared = useRef(readShared());
  const saved = useRef((() => { try { return JSON.parse(localStorage.getItem(STORE_KEY)); } catch (e) { return null; } })());

  const [phase, setPhase] = useState(shared.current ? "result" : "intro");
  const [index, setIndex] = useState(0);
  const [answers, setAnswers] = useState({});
  const [dir, setDir] = useState("fwd");
  const [scenePhase, setScenePhase] = useState("in");
  const [justPicked, setJustPicked] = useState(null);
  const [refocus, setRefocus] = useState(false);
  const [toastMsg, setToastMsg] = useState("");
  const [parting, setParting] = useState(false);

  const transitioning = useRef(false);
  const advTimer = useRef(null);
  const toastTimer = useRef(null);
  const anim = Math.max(0.35, (t.anim ?? 6) / 6);

  useEffect(() => {
    const root = document.documentElement;
    root.style.setProperty("--accent", t.accent);
    root.style.setProperty("--accent-ink", inkFor(t.accent));
    root.style.setProperty("--font-display", FONTS[t.font] || FONTS.serif);
    root.style.setProperty("--anim", String(anim));
    root.setAttribute("data-mode", t.mode === "night" ? "night" : "day");
  }, [t.accent, t.font, t.mode, anim]);

  const sharedData = shared.current;

  useEffect(() => {
    if (phase === "intro" || sharedData) return;
    try { localStorage.setItem(STORE_KEY, JSON.stringify({ index, answers, phase })); } catch (e) {}
  }, [index, answers, phase]);

  function toast(msg) {
    setToastMsg(msg);
    clearTimeout(toastTimer.current);
    toastTimer.current = setTimeout(() => setToastMsg(""), 2200);
  }

  function go(target, d) {
    if (transitioning.current || target === index) return;
    transitioning.current = true;
    setDir(d); setScenePhase("out");
    setTimeout(() => {
      setIndex(target);
      setScenePhase("in");
      setRefocus(true);
      setTimeout(() => setRefocus(false), 700 / anim);
      transitioning.current = false;
    }, 300 / anim);
  }
  function finish() {
    if (transitioning.current) return;
    transitioning.current = true;
    setDir("fwd"); setScenePhase("out");
    setTimeout(() => {
      setPhase("result");
      transitioning.current = false;
      try { document.querySelector(".stage")?.scrollTo(0, 0); } catch (e) {}
    }, 320 / anim);
  }
  function next() {
    const q = questions[index];
    if (!answered(q, answers[q.id])) return;
    if (index < questions.length - 1) go(index + 1, "fwd");
    else finish();
  }
  function back() {
    if (index > 0) go(index - 1, "back");
  }

  function answered(q, v) {
    if (!q) return false;
    if (q.type === "multi") return Array.isArray(v) && v.length > 0;
    if (q.type === "rank") return true;
    if (q.type === "text") return true;
    return v != null && v !== "";
  }

  function setAns(qid, val) { setAnswers((a) => ({ ...a, [qid]: val })); }

  function handleChange(q, val) {
    setAns(q.id, val);
    if (q.type === "single" || q.type === "image") {
      setJustPicked(val);
      clearTimeout(advTimer.current);
      advTimer.current = setTimeout(() => {
        setJustPicked(null);
        if (index < questions.length - 1) go(index + 1, "fwd");
        else finish();
      }, 470 / anim);
    }
  }

  function start(resume) {
    if (resume && saved.current) {
      setAnswers(saved.current.answers || {});
      if (saved.current.phase === "result") { setPhase("result"); return; }
      setIndex(saved.current.index || 0);
    } else {
      setAnswers({}); setIndex(0);
      try { localStorage.removeItem(STORE_KEY); } catch (e) {}
    }
    setParting(true);
    setScenePhase("in"); setDir("fwd");
    setRefocus(true);
    setTimeout(() => setRefocus(false), 800 / anim);
    setTimeout(() => { setPhase("survey"); }, 720 / anim);
  }

  function restart() {
    try { localStorage.removeItem(STORE_KEY); } catch (e) {}
    if (location.hash) history.replaceState(null, "", location.pathname);
    shared.current = null;
    setAnswers({}); setIndex(0); setParting(false);
    setPhase("intro");
  }

  useEffect(() => {
    function onKey(e) {
      if (phase !== "survey") return;
      const q = questions[index];
      if (e.key === "Enter") { if (answered(q, answers[q.id])) next(); }
      else if (e.key === "Backspace" && q.type !== "text") { e.preventDefault(); back(); }
      else if (/^[1-9]$/.test(e.key) && q.type !== "text") {
        const i = +e.key - 1;
        if ((q.type === "single" || q.type === "image") && q.options[i]) handleChange(q, q.options[i].id);
        else if (q.type === "multi" && q.options[i]) {
          const arr = Array.isArray(answers[q.id]) ? answers[q.id] : [];
          const id = q.options[i].id;
          if (arr.includes(id)) setAns(q.id, arr.filter((x) => x !== id));
          else if (arr.length < (q.max || q.options.length)) setAns(q.id, [...arr, id]);
        } else if (q.type === "scale") { const v = q.min + i; if (v <= q.max) setAns(q.id, v); }
      }
    }
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  });

  const scores = sharedData ? sharedData.s : scoreSurvey(survey, answers);
  const pKey = sharedData ? sharedData.p : topPersona(survey, scores).key;
  const persona = survey.personas[pKey];

  const q = questions[index];
  const v = answers[q?.id];
  const QBody = q && {
    single: SingleChoice, multi: MultiChoice, scale: ScaleQ,
    image: ImageQ, rank: RankQ, text: TextQ,
  }[q.type];

  const total = questions.length;
  const progressN = phase === "result" ? total : index;
  const pct = (progressN / total) * 100;
  const curAct = q?.act || (index < 4 ? "开场" : index < 10 ? "进行中" : "收束");

  return (
    <div className={"app" + (phase !== "intro" ? " lit" : "") + (refocus ? " refocus" : "") + (phase === "result" ? " showing-result" : "")}>
      <div className="spotlight"></div>

      <div className="topbar">
        <div className="brand">
          <span className="seal">{Icon.ticket()}</span>
          <span>
            <b>昆虫整理人格</b>
            <small>问卷 · QUESTIONNAIRE</small>
          </span>
        </div>
        <span className="grow"></span>
        <button className="ctrl icon" title={t.mode === "night" ? "切到白天" : "切到夜场"}
          onClick={() => setTweak("mode", t.mode === "night" ? "day" : "night")}>
          {t.mode === "night" ? Icon.sun() : Icon.moon()}
        </button>
      </div>

      {phase !== "intro" && (
        <div className="progress">
          <div className="meta">
            <span className="act">{phase === "result" ? "结果 · 你的整理人格" : curAct}</span>
            <span className="count">{phase === "result" ? "全部完成" : <React.Fragment><b>{index + 1}</b> / {total}</React.Fragment>}</span>
          </div>
          <div className="track">
            <div className="fill" style={{ width: pct + "%" }}></div>
            <div className="head" style={{ left: pct + "%" }}></div>
            <div className="ticks">{questions.map((_, i) => <i key={i}></i>)}</div>
          </div>
        </div>
      )}

      <div className="stage">
        {phase === "survey" && q && (
          <div className="scene" data-phase={scenePhase} data-dir={dir} key={q.id}>
            <div className="q-eyebrow">
              <span className="q-num">第 {index + 1} 题</span>
              <span className="dash"></span>
              <span>{{ single: "单选", multi: "多选", scale: "程度", image: "选一幕", rank: "排序", text: "随手写" }[q.type]}</span>
            </div>
            <h2 className="q-title">{q.title}</h2>
            {q.sub && <div className="q-sub">{q.sub}</div>}
            <div className="q-body">
              <QBody q={q} value={v} justPicked={justPicked}
                onChange={(val) => handleChange(q, val)} />
            </div>
            <div className="q-foot">
              {index > 0 && <button className="btn ghost" onClick={back}>{Icon.arrowL()}上一题</button>}
              <span className="grow"></span>
              <span className="hint">
                {q.type === "single" || q.type === "image"
                  ? <React.Fragment>选好自动进入下一题 · 按 <kbd>1</kbd>–<kbd>{Math.min(9, q.options.length)}</kbd> 快速选</React.Fragment>
                  : <React.Fragment>按 <kbd>Enter</kbd> 继续</React.Fragment>}
              </span>
              {!(q.type === "single" || q.type === "image") && (
                <button className="btn primary" disabled={!answered(q, v)} onClick={next}>
                  {index === total - 1 ? "看结果" : "下一题"}{Icon.arrowR()}
                </button>
              )}
            </div>
          </div>
        )}

        {phase === "result" && (
          <ResultView survey={survey} scores={scores} answers={answers}
            persona={persona} pKey={pKey} accent={t.accent}
            onRestart={restart} toast={toast} />
        )}
      </div>

      {phase === "intro" && (
        <div className={"veil" + (parting ? " parting" : "")}>
          <div className="curtain-l"></div>
          <div className="curtain-r"></div>
          <div className="ticket">
            <div className="badge">{Icon.star()}</div>
            <div className="kick">整 理 风 格 测 试</div>
            <h1>{survey.title}</h1>
            <p>{survey.subtitle}</p>
            <p className="source-note">题目摘自《{source.bookTitle}》。完整整理方法、各人格章节与共同生活建议，请阅读原书。</p>
            <button className="start" onClick={() => start(false)}>
              开始测试 {Icon.arrowR()}
            </button>
            {saved.current && (saved.current.answers && Object.keys(saved.current.answers).length > 0) && (
              <div className="resume">
                <button onClick={() => start(true)}>· 继续上次未完成的 ·</button>
              </div>
            )}
          </div>
        </div>
      )}

      <div className={"toast" + (toastMsg ? " on" : "")}>{Icon.check()}{toastMsg}</div>

      <TweaksPanel>
        <TweakSection label="强调色" />
        <TweakColor label="主题强调色" value={t.accent}
          options={ACCENTS.map((a) => a.c)}
          onChange={(val) => setTweak("accent", val)} />
        <TweakSection label="文字" />
        <TweakRadio label="字体" value={t.font}
          options={[{ value: "serif", label: "衬线" }, { value: "sans", label: "黑体" }, { value: "hand", label: "手写" }]}
          onChange={(val) => setTweak("font", val)} />
        <TweakSection label="演出" />
        <TweakRadio label="场次" value={t.mode}
          options={[{ value: "day", label: "白天" }, { value: "night", label: "夜场" }]}
          onChange={(val) => setTweak("mode", val)} />
        <TweakSlider label="动效强度" value={t.anim} min={0} max={10} step={1}
          onChange={(val) => setTweak("anim", val)} />
      </TweaksPanel>
    </div>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<App />);
