// ACT 3 — Pinned craft section with numbered steps
const CRAFT_FRAME_NUMBERS = [1,5,10,15,20,25,30,35,40,45,50,55,60,65,70,75,80,85,90,95,100,105,110,115,120,125,130,135,140,145,150,155,160,165,170,175,180,185,190];
const CRAFT_FRAME_SRCS = CRAFT_FRAME_NUMBERS.map(n => `images/maalem-stitching/frame-${String(n).padStart(3,'0')}.webp`);

function ActCraft({ lang }) {
  const content = window.CONTENT[lang].craft;
  const sectionRef = useRef(null);
  const canvasRef = useRef(null);
  const videoRef = useRef(null);
  const frameCacheRef = useRef(new Map());
  const lastDrawnFrameRef = useRef(null);
  const mountedRef = useRef(false);
  const rawP = useStickyProgress(sectionRef);
  const [p, setP] = useState(0);
  const [cacheTick, setCacheTick] = useState(0);
  const [videoReadyTick, setVideoReadyTick] = useState(0);
  const isRTL = lang === 'ar';
  const { isPhone, isTablet } = useResponsive();
  const isCompact = isTablet;
  const craftIntro = easeInOut(clamp(p / 0.18, 0, 1));
  const craftExit = isCompact ? easeInOut(clamp((p - 0.78) / 0.18, 0, 1)) : 0;
  const craftContentOpacity = (1 - craftExit) * (1 - craftExit);

  useEffect(() => {
    mountedRef.current = true;
    return () => {
      mountedRef.current = false;
    };
  }, []);

  useEffect(() => {
    let raf;
    const tick = () => {
      setP(current => {
        const next = current + (rawP - current) * 0.16;
        return Math.abs(next - current) < 0.0005 ? rawP : next;
      });
      raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [rawP]);

  // The first craft frame is visible immediately so the section never opens blank.
  const activeStep =
    p < 0.34 ? 1 :
    p < 0.64 ? 2 : 3;

  const frameFloat = clamp(p / 0.9, 0, 1) * (CRAFT_FRAME_SRCS.length - 1);
  const frameBaseIndex = Math.floor(frameFloat);
  const craftVideoProgress = clamp(p / 0.9, 0, 1);
  const finishingReveal = clamp((p - 0.6) / 0.22, 0, 1);
  const baseStageOpacity = lerp(0.58, 0.84, finishingReveal);
  const stageBrightness = lerp(lerp(0.62, 0.76, finishingReveal), 0.48, craftExit);
  const stageVignette = lerp(lerp(1, 0.72, finishingReveal), 1.18, craftExit);
  const stageScale = lerp(1.04, isPhone ? 1.18 : 1.1, craftExit);

  useEffect(() => {
    if (!isCompact) return;
    const video = videoRef.current;
    if (!video || !Number.isFinite(video.duration) || video.duration <= 0) return;

    video.pause();
    const target = craftVideoProgress * Math.max(0, video.duration - 0.04);
    if (Math.abs(video.currentTime - target) > 0.02) {
      video.currentTime = target;
    }
  }, [isCompact, craftVideoProgress, videoReadyTick]);

  useEffect(() => {
    if (!isCompact) return;
    videoRef.current?.load();
  }, [isCompact]);

  useEffect(() => {
    if (isCompact) return;

    let cancelled = false;
    const cache = frameCacheRef.current;

    const loadFrame = (i) => new Promise((resolve) => {
      const existing = cache.get(i);
      if (existing?.ready) {
        resolve();
        return;
      }

      const img = new Image();
      img.decoding = 'async';
      cache.set(i, { img, ready: false });
      const markReady = () => {
        const record = cache.get(i);
        if (mountedRef.current && record && !record.ready) {
          record.ready = true;
          setCacheTick(tick => tick + 1);
        }
        resolve();
      };
      img.onerror = resolve;
      img.onload = markReady;
      img.src = CRAFT_FRAME_SRCS[i];
      if (img.decode) img.decode().then(markReady).catch(() => {});
    });

    const preload = async () => {
      const priority = Array.from(
        { length: CRAFT_FRAME_SRCS.length },
        (_, i) => i
      ).sort((a, b) => Math.abs(a - frameBaseIndex) - Math.abs(b - frameBaseIndex));

      for (const i of priority) {
        if (cancelled) return;
        await loadFrame(i);
        await new Promise(requestAnimationFrame);
      }
    };

    preload();
    return () => {
      cancelled = true;
    };
  }, [isCompact, frameBaseIndex]);

  useEffect(() => {
    if (isCompact) return;

    const cache = frameCacheRef.current;
    const current = cache.get(frameBaseIndex);
    const nextIndex = Math.min(CRAFT_FRAME_SRCS.length - 1, frameBaseIndex + 1);
    const next = cache.get(nextIndex);
    const fallback = current?.ready
      ? current.img
      : lastDrawnFrameRef.current || [...cache.values()].find(record => record.ready)?.img;
    if (current?.ready) {
      lastDrawnFrameRef.current = current.img;
    }
    drawCraftFrame(
      canvasRef.current,
      fallback,
      current?.ready && next?.ready ? next.img : null,
      clamp(frameFloat - frameBaseIndex, 0, 1),
      baseStageOpacity * craftIntro
    );
  }, [isCompact, frameBaseIndex, frameFloat, baseStageOpacity, craftIntro, cacheTick]);

  return (
    <section
      ref={sectionRef}
      id="craft"
      className="act craft-act"
      style={{ height: isPhone ? '430vh' : '520vh', marginTop: isPhone ? '-38vh' : '-72vh' }}
    >
      <div style={{
        position: 'sticky', top: 0, height: isPhone ? '100svh' : '100vh', overflow: 'hidden',
        display: 'flex',
        flexDirection: isPhone ? 'column' : 'row',
        background: `rgba(7, 7, 10, ${lerp(0, 1, craftIntro)})`
      }}>
        {/* Left: pinned title column */}
        <div style={{
          width: isPhone ? '100%' : isTablet ? '38%' : '40%',
          position: isPhone ? 'absolute' : 'relative',
          inset: isPhone ? 0 : 'auto',
          zIndex: isPhone ? 4 : 'auto',
          padding: isPhone ? 'calc(72px + env(safe-area-inset-top)) 24px calc(30px + env(safe-area-inset-bottom))' : isTablet ? '11vh 4vw' : '12vh 6vw',
          display: 'flex', flexDirection: 'column', justifyContent: 'space-between',
          borderRight: isPhone ? 'none' : `1px solid rgba(250, 250, 247, ${lerp(0, 0.14, craftIntro)})`,
          opacity: craftIntro * craftContentOpacity,
          transform: `translate3d(0, ${lerp(3, 0, craftIntro) + lerp(0, -3, craftExit)}vh, 0)`
        }}>
          <div>
            <div className="mono" style={{ color: 'var(--fg-dim)', marginBottom: isPhone ? 20 : 40 }}>
              {content.eyebrow}
            </div>
            <h2 style={{
              fontFamily: isRTL ? 'var(--font-ar)' : 'var(--font-serif)',
              fontWeight: 300,
              fontSize: isPhone ? 'clamp(38px, 11.5vw, 58px)' : isTablet ? 'clamp(42px, 5vw, 68px)' : 'clamp(48px, 5.5vw, 84px)',
              lineHeight: isPhone ? 1.04 : 1.02,
              letterSpacing: '-0.02em'
            }}>
              {content.h1}<br />
              <span style={{ fontStyle: 'italic' }}>{content.h2}</span>
            </h2>
          </div>

          {/* Step indicators */}
          <div style={{
            display: 'flex',
            flexDirection: isPhone ? 'row' : 'column',
            gap: isPhone ? 12 : 20,
            alignItems: isPhone ? 'flex-end' : 'stretch'
          }}>
            {content.steps.map((s, i) => {
              const active = activeStep === i + 1 || (activeStep > 3 && i === 2);
              return (
                <div key={i} style={{
                  display: 'flex',
                  flex: isPhone ? 1 : 'initial',
                  minWidth: 0,
                  alignItems: isPhone ? 'flex-start' : 'baseline',
                  flexDirection: isPhone ? 'column' : 'row',
                  gap: isPhone ? 7 : 16,
                  opacity: active ? 1 : 0.3,
                  transition: 'opacity 0.6s'
                }}>
                  <span className="mono" style={{ fontSize: 10, color: active ? 'var(--ocre)' : 'var(--fg-dim)' }}>
                    {s.n}
                  </span>
                  <span style={{
                    fontFamily: isRTL ? 'var(--font-ar)' : 'var(--font-serif)',
                    fontSize: isPhone ? 15 : isTablet ? 18 : 22,
                    lineHeight: isPhone ? 1.12 : 'normal',
                    fontWeight: 300
                  }}>
                    {s.t}
                  </span>
                  <div style={{
                    flex: 1,
                    width: isPhone ? '100%' : 'auto',
                    height: 1,
                    background: active ? 'var(--fg)' : 'var(--fg-faint)',
                    transition: 'background 0.6s'
                  }} />
                </div>
              );
            })}
          </div>
        </div>

        {/* Right: content pane with transitioning panels */}
        <div style={{
          width: isPhone ? '100%' : isTablet ? '62%' : '60%',
          position: 'relative',
          overflow: 'hidden',
          height: isPhone ? '100%' : 'auto'
        }}>
          <div
            className="craft-stitch-stage"
            style={{
              '--craft-stitch-opacity': baseStageOpacity * craftIntro,
              '--craft-stitch-brightness': stageBrightness,
              '--craft-stitch-vignette': stageVignette,
              '--craft-stitch-scale': stageScale
            }}
          >
            {isCompact ? (
              <>
                <img
                  className="craft-stitch-video craft-stitch-fallback"
                  src="video/craft-stitching-poster.jpg"
                  alt=""
                  aria-hidden="true"
                  loading="eager"
                  decoding="async"
                />
                <video
                  ref={videoRef}
                  className="craft-stitch-video"
                  src="video/craft-stitching-mobile.mp4"
                  poster="video/craft-stitching-poster.jpg"
                  muted
                  playsInline
                  preload="auto"
                  aria-hidden="true"
                  onLoadedMetadata={(event) => {
                    event.currentTarget.pause();
                    setVideoReadyTick(tick => tick + 1);
                  }}
                  onLoadedData={(event) => {
                    event.currentTarget.pause();
                    setVideoReadyTick(tick => tick + 1);
                  }}
                />
              </>
            ) : (
              <canvas ref={canvasRef} className="craft-stitch-canvas" aria-hidden="true" />
            )}
            <div className="craft-stitch-vignette" />
          </div>

          {content.steps.map((s, i) => {
            const stepStart = [0.08, 0.3, 0.55][i];
            const stepEnd = [0.3, 0.55, 0.8][i];
            const local = clamp((p - stepStart) / (stepEnd - stepStart), 0, 1);
            const isActivePanel = activeStep === i + 1;
            const fade = isPhone ? 0.055 : 0.08;
            const enter = clamp((p - (stepStart - fade)) / fade, 0, 1);
            const leave = clamp((p - stepEnd) / fade, 0, 1);
            const panelOpacity = isPhone
              ? (isActivePanel ? craftIntro * craftContentOpacity : 0)
              : craftIntro * enter * (1 - leave) * craftContentOpacity;

            return (
              <div key={i} style={{
                position: 'absolute', inset: 0,
                opacity: panelOpacity,
                visibility: isPhone && !isActivePanel ? 'hidden' : 'visible',
                transform: `translateY(${isPhone ? lerp(10, -6, local) : lerp(22, -18, local)}px)`,
                display: 'flex', flexDirection: 'column',
                justifyContent: isPhone ? 'flex-end' : 'flex-start',
                padding: isPhone ? '0 24px calc(108px + env(safe-area-inset-bottom))' : isTablet ? '11vh 4vw' : '12vh 6vw',
                zIndex: isActivePanel || panelOpacity > 0.02 ? 2 : 1,
                pointerEvents: 'none',
                willChange: 'opacity, transform'
              }}>
                {/* Big step number */}
                <div style={{
                  fontFamily: 'var(--font-serif)',
                  fontSize: isPhone ? 'clamp(96px, 30vw, 138px)' : 'clamp(120px, 20vw, 320px)',
                  lineHeight: 0.9,
                  fontWeight: 300,
                  color: 'var(--fg-faint)',
                  position: 'absolute',
                  top: isPhone ? 'calc(78px + env(safe-area-inset-top))' : '8vh',
                  right: isPhone ? 24 : '6vw',
                  fontStyle: 'italic',
                  transform: `translateY(${(1 - local) * 60}px)`,
                  opacity: 0.5 * panelOpacity
                }}>
                  {s.n}
                </div>

                {/* Image placeholder / product */}
                <div style={{
                  flex: 1,
                  position: 'relative',
                  display: 'flex', alignItems: 'center', justifyContent: 'center',
                  marginBottom: isPhone ? 0 : 40
                }}>
                </div>

                {/* Body */}
                <div style={{
                  maxWidth: isPhone ? 300 : 460,
                  marginLeft: isPhone ? 'auto' : 0,
                  padding: isPhone ? '18px 0 0' : 0
                }}>
                  <p style={{
                    fontFamily: isRTL ? 'var(--font-ar)' : 'var(--font-serif)',
                    fontSize: isPhone ? 'clamp(20px, 6vw, 25px)' : 'clamp(20px, 1.8vw, 26px)',
                    lineHeight: isPhone ? 1.36 : 1.45,
                    fontWeight: 300,
                    color: 'var(--fg)',
                    textWrap: 'pretty'
                  }}>
                    {s.b}
                  </p>
                </div>
              </div>
            );
          })}
        </div>
      </div>
    </section>
  );
}

function drawCraftFrame(canvas, currentImg, nextImg, blend, opacity) {
  if (!canvas || !currentImg || !currentImg.complete || !currentImg.naturalWidth) return;

  const rect = (canvas.parentElement || canvas).getBoundingClientRect();
  if (!rect.width || !rect.height) return;

  const dpr = Math.min(window.devicePixelRatio || 1, 2);
  const width = Math.round(rect.width * dpr);
  const height = Math.round(rect.height * dpr);

  if (canvas.width !== width || canvas.height !== height) {
    canvas.width = width;
    canvas.height = height;
  }

  const ctx = canvas.getContext('2d');
  ctx.clearRect(0, 0, width, height);
  ctx.imageSmoothingEnabled = true;
  ctx.imageSmoothingQuality = 'high';
  ctx.globalAlpha = nextImg ? opacity * (1 - blend) : opacity;
  drawCoverImage(ctx, currentImg, width, height);

  if (nextImg && nextImg.complete && nextImg.naturalWidth) {
    ctx.globalAlpha = opacity * blend;
    drawCoverImage(ctx, nextImg, width, height);
  }

  ctx.globalAlpha = 1;
}

function drawCoverImage(ctx, img, width, height) {
  const scale = Math.max(width / img.naturalWidth, height / img.naturalHeight);
  const drawWidth = img.naturalWidth * scale;
  const drawHeight = img.naturalHeight * scale;
  const x = (width - drawWidth) / 2;
  const y = (height - drawHeight) / 2;
  ctx.drawImage(img, x, y, drawWidth, drawHeight);
}

window.ActCraft = ActCraft;
