const { useState, useEffect, useMemo, useCallback, useRef } = React;

const PHRASES = window.PHRASES;
const { useBackgroundMusic, MusicToggle } = window.Music;
const { layoutHeart, FONT_STACK, ASPECT, ROW_HEIGHT } = window.HeartLayout;
const PHRASE_TEXTS = PHRASES.map((p) => p.phrase);
const PHRASE_DELAY_MS = 450; // fixed 2x pace (was 900ms per phrase)
const MAX_HEART_PX = 760;
const HEART_VIEWPORT_SHARE = 0.64;
const FLOATER_COUNT = 18;
const ZOOM_HEART_PX = 820; // big heart for the tap-to-read view
const SMALL_TEXT_PX = 11; // below this, offer the zoom view
const SIDE_GUTTER_PX = 16;

function useHeartSize() {
  const compute = () => {
    const w = window.innerWidth - SIDE_GUTTER_PX;
    const h = window.innerHeight * HEART_VIEWPORT_SHARE;
    return Math.max(260, Math.min(w, h / ASPECT, MAX_HEART_PX));
  };
  const [size, setSize] = useState(compute);
  useEffect(() => {
    const onResize = () => setSize(compute());
    window.addEventListener("resize", onResize);
    return () => window.removeEventListener("resize", onResize);
  }, []);
  return size;
}

function getRecipientName() {
  try {
    const raw = new URLSearchParams(window.location.search).get("to") || "";
    return raw.trim().slice(0, 40) || window.LETTER.recipient;
  } catch {
    return window.LETTER.recipient;
  }
}

function Floaters() {
  const items = useMemo(
    () =>
      Array.from({ length: FLOATER_COUNT }, (_, i) => ({
        left: `${(i * 53) % 100}%`,
        size: 10 + ((i * 7) % 18),
        duration: 12 + ((i * 5) % 14),
        delay: -((i * 3.1) % 20),
      })),
    []
  );
  return (
    <div className="floaters" aria-hidden="true">
      {items.map((f, i) => (
        <span
          key={i}
          className="floater"
          style={{
            left: f.left,
            fontSize: f.size,
            animationDuration: `${f.duration}s`,
            animationDelay: `${f.delay}s`,
          }}
        >
          ♥
        </span>
      ))}
    </div>
  );
}

function Intro({ name, onStart }) {
  return (
    <section className="intro" aria-labelledby="intro-title">
      {name && <div className="for-name">for {name}</div>}
      <h1 id="intro-title">I love you</h1>
      <p>in {PHRASES.length} ways, one at a time</p>
      <button className="btn" onClick={onStart} autoFocus>
        Begin ♥
      </button>
    </section>
  );
}

function HeartStage({ layout, shown, done }) {
  const { width, height, fontSize, words, fillers } = layout;
  const current = shown - 1;
  return (
    <div
      className={`stage${done ? " beating done" : ""}`}
      style={{ width, height, fontFamily: FONT_STACK, fontSize }}
      aria-hidden="true"
    >
      {PHRASES.slice(0, shown).map((p, i) => {
        const spot = words[i];
        if (!spot) return null;
        const isCurrent = i === current && !done;
        // The finale stacks its words, one per row, at the tip of the heart.
        const style = spot.lines
          ? { left: spot.x, top: spot.y, fontSize: `${spot.scale}em`, lineHeight: ROW_HEIGHT / spot.scale }
          : { left: spot.x, top: spot.y, fontSize: `${spot.scale}em` };
        return (
          <span
            key={`${i}-${p.language}`}
            className={`word${spot.lines ? " finale-word" : ""}${isCurrent ? " current" : ""}`}
            style={style}
          >
            {spot.lines ? p.phrase.split(/\s+/).map((w) => <span key={w}>{w}</span>) : p.phrase}
          </span>
        );
      })}
      {done &&
        fillers.map((f, i) => (
          <span
            key={`f${i}`}
            className="filler"
            style={{ left: f.x, top: f.y, animationDelay: `${i * 25}ms` }}
          >
            ♥
          </span>
        ))}
    </div>
  );
}

// Full-screen, scrollable big version of the finished heart so every word is
// readable on small screens. Swipe to move around; × or Escape closes.
function HeartZoom({ onClose }) {
  const scrollRef = useRef(null);
  const closeRef = useRef(null);
  const zoomLayout = useMemo(() => layoutHeart(PHRASE_TEXTS, ZOOM_HEART_PX), []);

  useEffect(() => {
    const el = scrollRef.current;
    if (el) el.scrollLeft = (el.scrollWidth - el.clientWidth) / 2;
    closeRef.current?.focus();
    const onKey = (e) => {
      if (e.key === "Escape") onClose();
    };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [onClose]);

  return (
    <div className="zoom-overlay" role="dialog" aria-modal="true" aria-label="The whole heart, up close">
      <button ref={closeRef} className="letter-close" onClick={onClose} aria-label="Close">
        ×
      </button>
      <p className="zoom-hint">swipe to read every word</p>
      <div className="zoom-scroll" ref={scrollRef}>
        <div className="zoom-inner">
          <HeartStage layout={zoomLayout} shown={PHRASES.length} done />
        </div>
      </div>
    </div>
  );
}

function Caption({ index, done, name }) {
  if (done) {
    return (
      <div className="caption" aria-live="polite">
        <div className="finale">
          …and in every language,
          <br />I love you{name ? `, ${name}` : ""}.
        </div>
      </div>
    );
  }
  const p = PHRASES[index];
  if (!p) return <div className="caption" />;
  return (
    <div className="caption" aria-live="polite">
      <div key={index} className="phrase-anim">
        <div className="lang">{p.language}</div>
        <div className="phrase">{p.phrase}</div>
        <div className="roman">{p.note}</div>
      </div>
    </div>
  );
}

function Controls({ shown, playing, done, onToggle, onSkip, onReplay, onOpenLetter }) {
  if (done) {
    return (
      <div className="controls controls-finale">
        <button className="btn btn-letter" onClick={onOpenLetter}>
          ✉ A letter for you
        </button>
        <button className="btn-ghost" onClick={onReplay}>
          Again ♥
        </button>
      </div>
    );
  }
  return (
    <div className="controls">
      <button className="btn-ghost" onClick={onToggle}>
        {playing ? "Pause" : "Play"}
      </button>
      <span className="counter">
        {shown} / {PHRASES.length}
      </span>
      <button className="btn-ghost" onClick={onSkip}>
        Skip
      </button>
    </div>
  );
}

function App() {
  const size = useHeartSize();
  const layout = useMemo(() => layoutHeart(PHRASE_TEXTS, size), [size]);
  const name = useMemo(getRecipientName, []);
  const music = useBackgroundMusic();

  const [started, setStarted] = useState(false);
  const [shown, setShown] = useState(0);
  const [playing, setPlaying] = useState(false);
  const [isLetterOpen, setIsLetterOpen] = useState(false);
  const closeLetter = useCallback(() => setIsLetterOpen(false), []);
  const [isZoomOpen, setIsZoomOpen] = useState(false);
  const closeZoom = useCallback(() => setIsZoomOpen(false), []);

  const total = PHRASES.length;
  const done = shown >= total;

  useEffect(() => {
    if (!playing || done) return undefined;
    const id = setTimeout(() => setShown((n) => Math.min(n + 1, total)), PHRASE_DELAY_MS);
    return () => clearTimeout(id);
  }, [playing, shown, done, total]);

  const { play: playMusic } = music;
  const start = useCallback(() => {
    playMusic();
    setStarted(true);
    setShown(1);
    setPlaying(true);
  }, [playMusic]);

  const replay = useCallback(() => {
    setShown(0);
    setPlaying(false);
    // Let the heart clear for a beat before redrawing.
    setTimeout(() => {
      setShown(1);
      setPlaying(true);
    }, 400);
  }, []);

  return (
    <main className="app">
      <Floaters />
      {started && !music.hasFailed && <MusicToggle isMuted={music.isMuted} onToggle={music.toggleMute} />}
      {started && <div className="progress" style={{ width: `${(shown / total) * 100}%` }} />}
      {!started ? (
        <Intro name={name} onStart={start} />
      ) : (
        <>
          {/* Same wrapper throughout so words don't re-animate when it becomes tappable. */}
          <button
            className="stage-button"
            disabled={!done}
            onClick={() => setIsZoomOpen(true)}
            aria-label="Open the heart up close to read every word"
          >
            <HeartStage layout={layout} shown={shown} done={done} />
          </button>
          {done && layout.fontSize < SMALL_TEXT_PX && (
            <button className="zoom-cta" onClick={() => setIsZoomOpen(true)}>
              tap the heart to read every word
            </button>
          )}
          <Caption index={shown - 1} done={done} name={name} />
          <Controls
            shown={shown}
            playing={playing}
            done={done}
            onToggle={() => setPlaying((p) => !p)}
            onSkip={() => setShown(total)}
            onReplay={replay}
            onOpenLetter={() => setIsLetterOpen(true)}
          />
        </>
      )}
      {isZoomOpen && <HeartZoom onClose={closeZoom} />}
      {isLetterOpen && <window.LetterModal name={name} onClose={closeLetter} />}
    </main>
  );
}

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