// Rhyme Compass LP — animated editor mockup (the product, shown not told)

const prefersReduced = () =>
  window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches;

function statusOf(num, notes) {
  if (num > notes) return "over";
  if (num < notes) return "under";
  return "ok";
}
function labelOf(s) {
  return s === "over" ? "多すぎる" : s === "under" ? "足りない" : "ぴったり";
}

// note lane for one phrase
function Lane({ notes, rests, pitch, loaded }) {
  const bars = [];
  let k = 0;
  for (let i = 0; i < notes; i++) {
    if (rests && rests.includes(i)) {
      bars.push(<span key={`r${i}`} className="rc-note rc-note--rest" style={{ height: "30%" }} />);
    }
    const h = (pitch && pitch[i] != null ? pitch[i] : 0.5) * 100;
    bars.push(
      <span key={`n${i}`} className="rc-note"
        style={{ height: `${Math.max(18, h)}%`, transitionDelay: `${k * 45}ms` }} />
    );
    k++;
  }
  return <div className={`rc-lane ${loaded ? "" : "load"}`}>{bars}</div>;
}

function RcEditor({ phrases, pitch }) {
  const N = phrases.length;
  const [loaded, setLoaded] = React.useState(false);
  const [dropHidden, setDropHidden] = React.useState(false);
  const [typed, setTyped] = React.useState(() => phrases.map(() => 0)); // chars typed per row
  const [active, setActive] = React.useState(-1); // row currently typing
  const timers = React.useRef([]);

  const finalState = () => {
    timers.current.forEach(clearTimeout);
    timers.current = [];
    setLoaded(true); setDropHidden(true);
    setTyped(phrases.map((p) => p.lyric.length));
    setActive(-1);
  };

  React.useEffect(() => {
    if (prefersReduced()) { finalState(); return; }
    let alive = true;
    const push = (fn, ms) => { const id = setTimeout(() => { if (alive) fn(); }, ms); timers.current.push(id); };
    const setRow = (r, c) => setTyped((prev) => { const nx = prev.slice(); nx[r] = c; return nx; });
    const SP = 115; // type speed
    const overIdx = phrases.findIndex((p) => p.mora > p.notes);

    // intro: MIDI drops in, notes load, all rows type once
    setLoaded(false); setDropHidden(false); setTyped(phrases.map(() => 0)); setActive(-1);
    push(() => { setDropHidden(true); setLoaded(true); }, 900);
    let t = 1700;
    phrases.forEach((p, r) => {
      push(() => setActive(r), t);
      for (let c = 1; c <= p.lyric.length; c++) push(() => setRow(r, c), t + c * SP);
      t += p.lyric.length * SP + 480;
    });
    push(() => setActive(-1), t);

    // gentle loop: re-type ONLY the over-count row so the red "catch" keeps
    // demonstrating real-time validation — the editor never empties.
    const loopOnce = () => {
      if (overIdx < 0) return;
      setRow(overIdx, 0);
      const len = phrases[overIdx].lyric.length;
      push(() => setActive(overIdx), 420);
      for (let c = 1; c <= len; c++) push(() => setRow(overIdx, c), 420 + c * 130);
      push(() => setActive(-1), 420 + len * 130);
      push(loopOnce, 420 + len * 130 + 2600);
    };
    push(loopOnce, t + 2200);

    return () => { alive = false; timers.current.forEach(clearTimeout); timers.current = []; };
  }, []);

  return (
    <div className="rc" role="img" aria-label="Rhyme Compass エディタ画面：MIDIから自動分割された各フレーズの音数を、足りない・ぴったり・多すぎるで色分け表示">
      <div className="rc__bar">
        <span className="rc__lights">
          <i style={{ background: "rgba(239,68,68,.75)" }} />
          <i style={{ background: "rgba(234,179,8,.75)" }} />
          <i style={{ background: "rgba(34,197,94,.75)" }} />
        </span>
        <span className="rc__file"><i className="ph ph-music-notes-simple" />guide_melody.mid</span>
        <span className="rc__exports">
          <span className="rc__exp rc__exp--hot">.svp</span>
          <span className="rc__exp">MIDI</span>
          <span className="rc__exp">docx</span>
          <span className="rc__exp">txt</span>
        </span>
      </div>

      <div className="rc__body">
        {!dropHidden && (
          <div className="rc__dropnote">
            <i className="ph ph-file-arrow-down" />MIDI をドロップ → 自動でフレーズ分割…
          </div>
        )}
        {phrases.map((p, r) => {
          const chars = typed[r];
          const num = Math.round((p.mora * chars) / p.lyric.length) || 0;
          const st = statusOf(num, p.notes);
          const showRow = loaded;
          return (
            <div key={p.idx} className={`rc-row ${st === "over" && chars >= p.lyric.length ? "rc-row--over" : ""}`}
                 style={{ opacity: showRow ? 1 : 0.4, transition: "opacity .4s" }}>
              <div className="rc-row__idx">{p.idx}</div>
              <div className="rc-row__main">
                <Lane notes={p.notes} rests={p.rests} pitch={pitch[r]} loaded={loaded} />
                <div className="rc-lyric">
                  {p.lyric.slice(0, chars)}
                  {active === r && <span className="car" />}
                  {chars === 0 && active !== r && <span style={{ color: "var(--zinc-700)" }}>歌詞を入力…</span>}
                </div>
              </div>
              <div className={`rc-count rc-count--${st}`} title={labelOf(st)}>
                <b>{num}</b><span>/ {p.notes}</span>
              </div>
            </div>
          );
        })}
      </div>

      <div className="rc__foot">
        <span className="rc__play"><i className="ph-fill ph-play" /></span>
        <span style={{ display: "flex", alignItems: "center", gap: ".4rem" }}>
          <span className="dot" />内蔵シンセでプレビュー
        </span>
        <span className="right">4 フレーズ · 自動保存済み</span>
      </div>
    </div>
  );
}

// ---- compact validation micro-demo for Feature ② (triggers on scroll) ----
function VDemo() {
  const target = "君の名前を呼んだ"; // 10 mora vs 8 notes -> goes red
  const notes = 8, mora = 10;
  const ref = React.useRef(null);
  const [chars, setChars] = React.useState(prefersReduced() ? target.length : 0);
  const timers = React.useRef([]);

  React.useEffect(() => {
    if (prefersReduced()) return;
    const el = ref.current;
    if (!el) return;
    const io = new IntersectionObserver((ents) => {
      ents.forEach((e) => {
        if (e.isIntersecting) {
          timers.current.forEach(clearTimeout); timers.current = [];
          setChars(0);
          for (let c = 1; c <= target.length; c++) {
            timers.current.push(setTimeout(() => setChars(c), 500 + c * 140));
          }
        }
      });
    }, { threshold: 0.6 });
    io.observe(el);
    return () => { io.disconnect(); timers.current.forEach(clearTimeout); };
  }, []);

  const num = Math.round((mora * chars) / target.length) || 0;
  const st = statusOf(num, notes);
  const heights = [60, 50, 75, 100, 55, 80, 65, 90];
  return (
    <div className="vdemo" ref={ref}>
      <div className="vdemo__lane">
        {heights.map((h, i) => <i key={i} style={{ height: `${h}%` }} />)}
      </div>
      <div className="vdemo__row">
        <span className="vdemo__lyric">
          {target.slice(0, chars)}
          {chars < target.length && chars > 0 && <span className="car" />}
          {chars === 0 && <span style={{ color: "var(--zinc-700)" }}>入力中…</span>}
        </span>
        <span className={`rc-count rc-count--${st}`}><b>{num}</b><span>/ {notes}</span></span>
      </div>
    </div>
  );
}

Object.assign(window, { RcEditor, VDemo });
