// intro.jsx
// Cinematic boot intro — Alien-style slow luminance reveal on pure black.
// Parent-controlled: App owns visibility (no reliance on location.reload()).
//   props:
//     show       — boolean, whether to render
//     playKey    — number; change it to restart the animation
//     onDone     — called when the sequence finishes or is skipped
//
// Visit logic lives in App via shouldShowIntro(); replay just bumps playKey.

(function () {
  const { useEffect, useState, useRef } = React;

  const VISIT_KEY = 'dys_visit_count';
  const INTRO_DURATION = 6800; // ms — slower, more cinematic

  // Called once by App to decide initial visibility. Side effect: increments count.
  function shouldShowIntro() {
    try {
      const raw = localStorage.getItem(VISIT_KEY);
      const n = parseInt(raw || '0', 10);
      localStorage.setItem(VISIT_KEY, String(n + 1));
      return n % 3 === 0; // visits 1, 4, 7, …
    } catch {
      return true;
    }
  }

  function Intro({ show, playKey, onDone }) {
    const { t } = useLang();
    const [leaving, setLeaving] = useState(false);
    const [ready, setReady] = useState(false);
    const doneRef = useRef(false);
    const logoRef = useRef(null);

    function finish() {
      if (doneRef.current) return;
      doneRef.current = true;
      setLeaving(true);
      setTimeout(() => { onDone && onDone(); }, 700);
    }

    // ----- Alien-style assembly: trace each letter's outline, then fill -----
    function runReveal() {
      const host = logoRef.current;
      if (!host) return;
      const svg = host.querySelector('svg');
      if (!svg) return;
      svg.setAttribute('preserveAspectRatio', 'xMidYMid meet');

      // every drawable glyph except the transparent background rect
      const shapes = Array.from(svg.querySelectorAll('path, rect'))
        .filter((el) => el.id !== 'logotyp');

      // order by horizontal distance from centre → builds from the middle out
      const svgRect = svg.getBoundingClientRect();
      const cxMid = svgRect.left + svgRect.width / 2;
      const measured = shapes.map((el) => {
        const r = el.getBoundingClientRect();
        return { el, dist: Math.abs(r.left + r.width / 2 - cxMid) };
      });
      measured.sort((a, b) => a.dist - b.dist);

      const STEP = 150;   // ms between glyphs
      const DRAW = 720;   // ms to trace a glyph
      measured.forEach(({ el }, i) => {
        let len;
        try {
          len = el.getTotalLength ? el.getTotalLength() : 0;
        } catch { len = 0; }
        if (!len && el.tagName.toLowerCase() === 'rect') {
          const w = parseFloat(el.getAttribute('width')) || 0;
          const h = parseFloat(el.getAttribute('height')) || 0;
          len = (w + h) * 2;
        }
        if (!len) len = 600;

        el.style.fill = '#ffffff';
        el.style.stroke = '#ffffff';
        el.style.strokeWidth = '1.4px';
        el.style.vectorEffect = 'non-scaling-stroke';
        el.style.strokeDasharray = len;
        el.style.strokeDashoffset = len;
        el.style.fillOpacity = '0';

        const delay = 1700 + i * STEP;
        // trace the outline
        el.animate(
          [{ strokeDashoffset: len }, { strokeDashoffset: 0 }],
          { duration: DRAW, delay, easing: 'cubic-bezier(0.4,0,0.2,1)', fill: 'forwards' }
        );
        // fill resolves shortly after the trace completes
        el.animate(
          [{ fillOpacity: 0 }, { fillOpacity: 1 }],
          { duration: 520, delay: delay + DRAW * 0.55, easing: 'ease-out', fill: 'forwards' }
        );
        // let the stroke melt back into the fill at the very end
        el.animate(
          [{ strokeOpacity: 1 }, { strokeOpacity: 0 }],
          { duration: 600, delay: delay + DRAW + 300, easing: 'ease-out', fill: 'forwards' }
        );
      });
    }

    // load + inject the wordmark SVG, then run the reveal
    useEffect(() => {
      if (!show) return;
      doneRef.current = false;
      setLeaving(false);
      setReady(false);

      let cancelled = false;
      const host = logoRef.current;

      function inject(svgText) {
        if (cancelled || !host) return;
        host.innerHTML = svgText;
        // defer so layout settles before measuring
        requestAnimationFrame(() => requestAnimationFrame(runReveal));
      }

      if (window.__wordmarkSvg) {
        inject(window.__wordmarkSvg);
      } else {
        fetch(window.__res('wordmark', 'assets/logo/wordmark-solid.svg'))
          .then((r) => r.text())
          .then((txt) => { window.__wordmarkSvg = txt; inject(txt); })
          .catch(() => {
            if (host) host.innerHTML =
              '<img src="' + window.__res('wordmark', 'assets/logo/wordmark-solid.svg') + '" alt="dysbalans" class="intro__logo-fallback">';
          });
      }

      const t1 = setTimeout(() => { window.Audio && window.Audio.transformer(); }, 1500);
      // After the wordmark finishes assembling, reveal the ENTER gate instead of
      // auto-advancing. The click on it is the user's first gesture, so the base
      // SFX (ambient) starts the instant they enter — no autoplay block.
      const t2 = setTimeout(() => setReady(true), INTRO_DURATION);

      function onKey(e) {
        if (e.key === 'Escape' || e.key === 'Enter' || e.key === ' ') {
          e.preventDefault();
          finish();
        }
      }
      window.addEventListener('keydown', onKey);
      return () => {
        cancelled = true;
        clearTimeout(t1);
        clearTimeout(t2);
        window.removeEventListener('keydown', onKey);
      };
    }, [show, playKey]);

    if (!show) return null;

    return (
      <div
        className={`intro ${leaving ? 'is-leaving' : ''}`}
        key={playKey}
        role="dialog"
        aria-label="Wprowadzenie"
      >
        <button className="intro__skip" onClick={finish}>
          <kbd>esc</kbd>skip
        </button>

        {/* Pure-black cinematic stage — no grid, no particles */}
        <div className="intro__stage">
          <div className="intro__strap">brand &amp; narrative studio</div>
          <div className="intro__logo" ref={logoRef}></div>
          {ready && (
            <button
              className="intro__enter"
              data-clickable
              onClick={() => { window.Audio && window.Audio.enable(); finish(); }}
            >
              {t('intro_enter')}
            </button>
          )}
        </div>

        <div className="intro__meta">
          <div>STUDIO.&#208;YSBALANS.MAINFRAME <b>v0.1</b></div>
          <div>{t('intro_meta')}</div>
        </div>
      </div>
    );
  }

  Object.assign(window, { Intro, shouldShowIntro });
})();
