// kid-helmet-lab.jsx
// Astronaut Helmet Lab (HelmetLab): sticker placement, decorations, visor toy.
// Extracted from kid-levels.jsx on 2026-05-14.

function HelmetLab({
  helmetSticker,
  onHelmetChange,
  stickerPlacement,
  onPlacementChange,
  helmetSide,
  onHelmetSideChange,
  stickerSide,
  onStickerSideChange,
  stickerPos,
  onStickerPosChange,
  suitColor,
  onSuitChange,
  onClose,
}) {
  const sticker =
    HELMET_STICKERS.find((s) => s.id === helmetSticker) || HELMET_STICKERS[0];
  const suit = currentSuit(suitColor);
  const helmetSurfaceRef = useRef(null);
  const backpackRef = useRef(null);
  const flyTimer = useRef(null);
  const choiceRefs = useRef({});
  const helmetRotateDragRef = useRef(null);
  const dragAbortRef = useRef(null);
  useEffect(
    () => () => {
      if (dragAbortRef.current) {
        dragAbortRef.current.abort();
        dragAbortRef.current = null;
      }
    },
    [],
  );
  const [flyingSticker, setFlyingSticker] = useState(null);
  const [placedStickers, setPlacedStickers] = useHelmetDecorations();
  const [selectedPlacedId, setSelectedPlacedId] = useState(null);
  const [draggingPlacedId, setDraggingPlacedId] = useState(null);
  const [helmetComplete, setHelmetComplete] = useState(false);
  const sideIndex = Math.max(
    0,
    HELMET_SIDES.findIndex((s) => s.id === helmetSide),
  );
  const side = HELMET_SIDES[sideIndex] || HELMET_SIDES[0];
  const visibleStickers = placedStickers.filter(
    (item) => item.side === side.id,
  );
  const selectedPlacedSticker = placedStickers.find(
    (item) => item.uid === selectedPlacedId,
  );
  const hasHelmetDecoration =
    placedStickers.length > 0 || (stickerPlacement === "backpack" && sticker.id !== "none");

  const pointOnHelmetSurface = (event, opts = {}) => {
    const rect =
      helmetSurfaceRef.current &&
      helmetSurfaceRef.current.getBoundingClientRect();
    if (!rect) return null;
    const pad = opts.edgeClamp ? 160 : 24;
    if (
      event.clientX < rect.left - pad ||
      event.clientX > rect.right + pad ||
      event.clientY < rect.top - pad ||
      event.clientY > rect.bottom + pad
    ) {
      return null;
    }
    return {
      x: Math.max(
        12,
        Math.min(88, ((event.clientX - rect.left) / rect.width) * 100),
      ),
      y: Math.max(
        10,
        Math.min(88, ((event.clientY - rect.top) / rect.height) * 100),
      ),
    };
  };

  const updatePlacedStickerPosition = (uid, point) => {
    if (!uid || !point) return;
    setPlacedStickers((current) =>
      current.map((item) =>
        item.uid === uid
          ? { ...item, side: side.id, x: point.x, y: point.y }
          : item,
      ),
    );
  };

  const moveStickerOnSurface = (event) => {
    if (stickerPlacement !== "helmet") return;
    const point = pointOnHelmetSurface(event, { edgeClamp: true });
    const targetUid =
      selectedPlacedSticker && selectedPlacedSticker.side === side.id
        ? selectedPlacedSticker.uid
        : visibleStickers[visibleStickers.length - 1] &&
          visibleStickers[visibleStickers.length - 1].uid;
    if (!targetUid || !point) return;
    updatePlacedStickerPosition(targetUid, point);
    setSelectedPlacedId(targetUid);
    playKidSound("boop");
  };

  const startHelmetSurfaceDrag = (event) => {
    if (stickerPlacement !== "helmet") return;
    event.preventDefault();
    try {
      event.currentTarget.setPointerCapture(event.pointerId);
    } catch {}
    helmetRotateDragRef.current = {
      id: event.pointerId,
      x: event.clientX,
      y: event.clientY,
      sideIndex,
      lastIndex: sideIndex,
      lastStep: 0,
      lastDx: 0,
      moved: false,
    };
  };

  const continueSurfaceDrag = (event) => {
    if (stickerPlacement !== "helmet") return;
    const drag = helmetRotateDragRef.current;
    if (!drag || drag.id !== event.pointerId) return;
    if (event.buttons !== 1 && event.pointerType !== "touch") return;
    event.preventDefault();
    const dx = event.clientX - drag.x;
    const dy = event.clientY - drag.y;
    drag.lastDx = dx;
    if (Math.abs(dx) < 24 || Math.abs(dx) < Math.abs(dy) * 1.15) return;
    drag.moved = true;
    const step = Math.trunc(dx / 68);
    const nextIndex =
      (drag.sideIndex + step + HELMET_SIDES.length * 8) % HELMET_SIDES.length;
    if (step && nextIndex !== drag.lastIndex) {
      const next = HELMET_SIDES[nextIndex];
      onHelmetSideChange(next.id);
      const nextSticker = placedStickers.find((item) => item.side === next.id);
      setSelectedPlacedId(nextSticker ? nextSticker.uid : null);
      drag.lastIndex = nextIndex;
      drag.lastStep = step;
      playKidSound("boop");
    }
  };

  const stopSurfaceDrag = (event) => {
    const drag = helmetRotateDragRef.current;
    if (drag && drag.id === event.pointerId) {
      window.setTimeout(
        () => {
          helmetRotateDragRef.current = null;
        },
        drag.moved ? 140 : 0,
      );
    }
  };

  const maybeMoveStickerOnSurface = (event) => {
    if (helmetRotateDragRef.current?.moved) {
      event.preventDefault();
      return;
    }
    moveStickerOnSurface(event);
  };

  const startPlacedStickerDrag = (event, placed, placedSticker) => {
    event.preventDefault();
    event.stopPropagation();
    setSelectedPlacedId(placed.uid);
    onHelmetChange(placedSticker.id);
    setDraggingPlacedId(placed.uid);
    updatePlacedStickerPosition(placed.uid, pointOnHelmetSurface(event));
    playKidSound("boop");

    const move = (moveEvent) => {
      moveEvent.preventDefault();
      updatePlacedStickerPosition(
        placed.uid,
        pointOnHelmetSurface(moveEvent, { edgeClamp: true }),
      );
    };
    if (dragAbortRef.current) dragAbortRef.current.abort();
    const ac = new AbortController();
    dragAbortRef.current = ac;
    const stop = (upEvent) => {
      updatePlacedStickerPosition(
        placed.uid,
        pointOnHelmetSurface(upEvent, { edgeClamp: true }),
      );
      setDraggingPlacedId(null);
      ac.abort();
      if (dragAbortRef.current === ac) dragAbortRef.current = null;
    };
    window.addEventListener("pointermove", move, { signal: ac.signal });
    window.addEventListener("pointerup", stop, { signal: ac.signal });
    window.addEventListener("pointercancel", stop, { signal: ac.signal });
  };

  const startChoiceStickerDrag = (event, s) => {
    if (s.id === "none") {
      event.preventDefault();
      onHelmetChange(s.id);
      playKidSound("boop");
      return;
    }
    event.preventDefault();
    event.stopPropagation();
    clearTimeout(flyTimer.current);
    onHelmetChange(s.id);
    const start = { x: event.clientX, y: event.clientY };
    let uid = null;
    let didDrag = false;

    const move = (moveEvent) => {
      moveEvent.preventDefault();
      const distance = Math.hypot(
        moveEvent.clientX - start.x,
        moveEvent.clientY - start.y,
      );
      if (!didDrag && distance < 9) return;
      if (stickerPlacement === "backpack") return;
      if (!didDrag) {
        didDrag = true;
        uid = `${s.id}-${side.id}-${Date.now()}`;
        const fallback = {
          x: 50 + ((visibleStickers.length % 3) - 1) * 14,
          y: 48 + (Math.floor(visibleStickers.length / 3) % 2) * 16,
        };
        const startPoint = pointOnHelmetSurface(moveEvent) || fallback;
        setPlacedStickers((current) => [
          ...current,
          {
            uid,
            stickerId: s.id,
            side: side.id,
            x: startPoint.x,
            y: startPoint.y,
          },
        ]);
        setSelectedPlacedId(uid);
        setDraggingPlacedId(uid);
        onStickerSideChange(side.id);
        playKidSound("chime");
      }
      updatePlacedStickerPosition(
        uid,
        pointOnHelmetSurface(moveEvent, { edgeClamp: true }),
      );
    };
    if (dragAbortRef.current) dragAbortRef.current.abort();
    const ac = new AbortController();
    dragAbortRef.current = ac;
    const stop = (upEvent) => {
      if (didDrag && uid) {
        updatePlacedStickerPosition(
          uid,
          pointOnHelmetSurface(upEvent, { edgeClamp: true }),
        );
      } else {
        pickSticker(s);
      }
      setDraggingPlacedId(null);
      ac.abort();
      if (dragAbortRef.current === ac) dragAbortRef.current = null;
    };
    window.addEventListener("pointermove", move, { signal: ac.signal });
    window.addEventListener("pointerup", stop, { signal: ac.signal });
    window.addEventListener("pointercancel", stop, { signal: ac.signal });
  };

  const rotateHelmet = (step) => {
    const next =
      HELMET_SIDES[
        (sideIndex + step + HELMET_SIDES.length) % HELMET_SIDES.length
      ];
    onHelmetSideChange(next.id);
    const nextSticker = placedStickers.find((item) => item.side === next.id);
    setSelectedPlacedId(nextSticker ? nextSticker.uid : null);
    playKidSound("boop");
    if (window.__narration) window.__narration.play("game_helmet_rotate.mp3");
  };

  useEffect(() => {
    return () => clearTimeout(flyTimer.current);
  }, []);

  const pickSticker = (s) => {
    clearTimeout(flyTimer.current);
    const target =
      stickerPlacement === "backpack"
        ? backpackRef.current
        : helmetSurfaceRef.current;
    if (s.id === "none" || !target || !choiceRefs.current[s.id]) {
      onHelmetChange(s.id);
      playKidSound("boop");
      return;
    }
    const from = choiceRefs.current[s.id].getBoundingClientRect();
    const to = target.getBoundingClientRect();
    const fromX = from.left + from.width / 2;
    const fromY = from.top + from.height / 2;
    const toX = to.left + to.width / 2;
    const toY = to.top + to.height / 2;
    setFlyingSticker({
      id: s.id + "-" + Date.now(),
      glyph: s.glyph,
      asset: s.asset,
      fromX,
      fromY,
      dx: toX - fromX,
      dy: toY - fromY,
    });
    playKidSound("chime");
    flyTimer.current = setTimeout(() => {
      onHelmetChange(s.id);
      if (stickerPlacement === "helmet") {
        const uid = `${s.id}-${side.id}-${Date.now()}`;
        setPlacedStickers([
          ...placedStickers,
          {
            uid,
            stickerId: s.id,
            side: side.id,
            x: 50 + ((visibleStickers.length % 3) - 1) * 14,
            y: 48 + (Math.floor(visibleStickers.length / 3) % 2) * 16,
          },
        ]);
        setSelectedPlacedId(uid);
        onStickerSideChange(side.id);
      }
      setFlyingSticker(null);
      playKidSound("boop");
      if (s.id !== "none" && window.__narration) {
        window.__narration.play("game_helmet_sticker_placed.mp3");
      }
    }, 720);
  };

  const removeSelectedSticker = () => {
    if (!selectedPlacedId) return;
    setPlacedStickers(
      placedStickers.filter((item) => item.uid !== selectedPlacedId),
    );
    setSelectedPlacedId(null);
    playKidSound("boop");
  };

  const clearCurrentSide = () => {
    if (!visibleStickers.length) return;
    setPlacedStickers(placedStickers.filter((item) => item.side !== side.id));
    setSelectedPlacedId(null);
    playKidSound("boop");
  };

  const activateStickerChoice = (s) => {
    if (s.id === "none") {
      onHelmetChange(s.id);
      playKidSound("boop");
      setHelmetComplete(false);
      return;
    }
    onHelmetChange(s.id);
    pickSticker(s);
    setHelmetComplete(false);
  };

  const finishHelmetLab = () => {
    setHelmetComplete(true);
    playKidSound("chime");
    if (window.__narration) window.__narration.play("game_helmet_sticker_placed.mp3");
  };

  const replayHelmetLab = () => {
    clearTimeout(flyTimer.current);
    setFlyingSticker(null);
    setPlacedStickers([]);
    setSelectedPlacedId(null);
    setDraggingPlacedId(null);
    setHelmetComplete(false);
    onHelmetChange("star");
    onPlacementChange("helmet");
    onHelmetSideChange("front");
    onStickerSideChange("front");
    playKidSound("boop");
  };

  return (
    <div className="detail-view helmet-lab-view">
      <div className="detail-bg helmet-lab-bg" />
      <div
        className="helmet-lab-preview"
        aria-label="Decorated astronaut helmet"
      >
        <button
          className="helmet-rotate-btn prev"
          onClick={() => rotateHelmet(-1)}
          aria-label="Rotate helmet left"
        >
          ‹
        </button>
        <button
          className="helmet-rotate-btn next"
          onClick={() => rotateHelmet(1)}
          aria-label="Rotate helmet right"
        >
          ›
        </button>
        <div className="helmet-side-label">{side.label}</div>
        <div
          className={`helmet-lab-suit side-${side.id} placement-${stickerPlacement}`}
          style={{ "--suit-color": suit.color, "--suit-accent": suit.accent }}
        >
          <SafeAssetImage
            className="helmet-lab-base"
            src={side.asset}
            alt=""
            context="HelmetLab:suitBase"
            fallbackText=""
          />
          <div className="helmet-lab-neck" />
          <div
            className="helmet-lab-shell"
            ref={helmetSurfaceRef}
            onClick={maybeMoveStickerOnSurface}
            onPointerDown={startHelmetSurfaceDrag}
            onPointerMove={continueSurfaceDrag}
            onPointerUp={stopSurfaceDrag}
            onPointerCancel={stopSurfaceDrag}
            title="Drag the helmet to rotate. Drag stickers to place them."
          >
            <div className="helmet-lab-visor" aria-hidden="true">
              {visibleStickers.map((placed) => {
                const placedSticker =
                  HELMET_STICKERS.find((s) => s.id === placed.stickerId) ||
                  HELMET_STICKERS[0];
                const selected = selectedPlacedId === placed.uid;
                const dragging = draggingPlacedId === placed.uid;
                return (
                  <button
                    key={placed.uid}
                    type="button"
                    className={`helmet-lab-sticker placed ${selected ? "selected" : ""} ${dragging ? "dragging" : ""} ${placedSticker.asset ? "asset" : ""}`}
                    style={{ left: `${placed.x}%`, top: `${placed.y}%` }}
                    onPointerDown={(event) =>
                      startPlacedStickerDrag(event, placed, placedSticker)
                    }
                    onClick={(event) => {
                      event.stopPropagation();
                      setSelectedPlacedId(placed.uid);
                      onHelmetChange(placedSticker.id);
                      playKidSound("boop");
                    }}
                    aria-label={`${placedSticker.label} sticker on ${side.label}`}
                    title="Drag this sticker or tap the helmet to move it"
                  >
                    <StickerArt sticker={placedSticker} />
                  </button>
                );
              })}
            </div>
            <div className="helmet-lab-ear left" />
            <div className="helmet-lab-ear right" />
          </div>
          <div className="helmet-lab-backpack" ref={backpackRef}>
            <span className="helmet-lab-backpack-strap" />
            {stickerPlacement === "backpack" && sticker.id !== "none" ? (
              <StickerArt
                sticker={sticker}
                className={`helmet-lab-backpack-sticker ${sticker.asset ? "" : "glyph"}`}
              />
            ) : null}
          </div>
        </div>
      </div>
      <div className="detail-info helmet-lab-info">
        <div className="detail-eyebrow">ASTRONAUT CLOSET</div>
        <h1 className="detail-name">Helmet Lab</h1>
        <div className="kid-i-spy">Pick a sticker and a suit color.</div>
        <div className="helmet-lab-section">
          <div className="helmet-lab-label">Put it on</div>
          <div className="placement-row">
            <button
              className={`placement-choice ${stickerPlacement === "helmet" ? "on" : ""}`}
              onClick={() => {
                onPlacementChange("helmet");
                onStickerSideChange(side.id);
                playKidSound("boop");
                if (window.__narration)
                  window.__narration.play("game_helmet_helmet.mp3");
              }}
              aria-pressed={stickerPlacement === "helmet" ? "true" : "false"}
            >
              Helmet
            </button>
            <button
              className={`placement-choice ${stickerPlacement === "backpack" ? "on" : ""}`}
              onClick={() => {
                onPlacementChange("backpack");
                playKidSound("boop");
                if (window.__narration)
                  window.__narration.play("game_helmet_backpack.mp3");
              }}
              aria-pressed={stickerPlacement === "backpack" ? "true" : "false"}
            >
              Backpack
            </button>
          </div>
        </div>
        <div className="helmet-lab-section">
          <div className="helmet-lab-label">Helmet sticker</div>
          <div className="helmet-lab-note">
            Pick. Drag a sticker to place it. Drag the helmet to spin.
          </div>
          <div className="helmet-side-status" aria-live="polite">
            <span>{side.label}</span>
            <strong>
              {visibleStickers.length}{" "}
              {visibleStickers.length === 1 ? "sticker" : "stickers"}
            </strong>
          </div>
          <div className="helmet-side-tools">
            <button
              className="placement-choice"
              onClick={removeSelectedSticker}
              disabled={!selectedPlacedId}
            >
              Remove selected
            </button>
            <button
              className="placement-choice"
              onClick={clearCurrentSide}
              disabled={!visibleStickers.length}
            >
              Clear {side.label}
            </button>
          </div>
          <div className="helmet-lab-grid">
            {HELMET_STICKERS.map((s) => (
              <button
                key={s.id}
                ref={(el) => {
                  if (el) choiceRefs.current[s.id] = el;
                }}
                className={`helmet-lab-choice ${helmetSticker === s.id ? "on" : ""}`}
                onPointerDown={(event) => startChoiceStickerDrag(event, s)}
                onClick={(event) => {
                  event.preventDefault();
                  if (event.detail === 0) activateStickerChoice(s);
                }}
                onKeyDown={(event) => {
                  if (event.key === "Enter" || event.key === " ") {
                    event.preventDefault();
                    activateStickerChoice(s);
                  }
                }}
                aria-pressed={helmetSticker === s.id ? "true" : "false"}
                title={s.label}
              >
                <span>
                  <StickerArt sticker={s} />
                </span>
                <small>{s.label}</small>
              </button>
            ))}
          </div>
        </div>
        <div className="helmet-lab-section">
          <div className="helmet-lab-label">Suit color</div>
          <div className="suit-color-row">
            {SUIT_COLORS.map((s) => (
              <button
                key={s.id}
                className={`suit-color-choice ${suitColor === s.id ? "on" : ""}`}
                onClick={() => {
                  onSuitChange(s.id);
                  playKidSound("boop");
                }}
                aria-pressed={suitColor === s.id ? "true" : "false"}
                title={s.label}
                style={{ "--suit-color": s.color, "--suit-accent": s.accent }}
              />
            ))}
          </div>
        </div>
        <div className="helmet-lab-section helmet-lab-finish" role="status">
          {helmetComplete ? (
            <>
              <strong>Helmet badge earned!</strong>
              <span>Your astronaut is ready for a space walk.</span>
              <div className="helmet-lab-actions">
                <button className="placement-choice" onClick={replayHelmetLab}>
                  Play again
                </button>
                <button className="placement-choice" onClick={onClose}>
                  Done
                </button>
              </div>
            </>
          ) : (
            <>
              <strong>Ready to finish?</strong>
              <span>Pick one sticker, then earn your helmet badge.</span>
              <button
                className="placement-choice"
                onClick={finishHelmetLab}
                disabled={!hasHelmetDecoration}
              >
                Finish helmet
              </button>
            </>
          )}
        </div>
      </div>
      {flyingSticker ? (
        <div
          key={flyingSticker.id}
          className="helmet-lab-flying-sticker"
          style={{
            left: flyingSticker.fromX,
            top: flyingSticker.fromY,
            "--fly-x": `${flyingSticker.dx}px`,
            "--fly-y": `${flyingSticker.dy}px`,
          }}
          aria-hidden="true"
        >
          <StickerArt sticker={flyingSticker} />
        </div>
      ) : null}
      <button className="detail-close" onClick={onClose}>
        <svg width="14" height="14" viewBox="0 0 14 14" fill="none">
          <path
            d="M11 3L3 11M3 3l8 8"
            stroke="currentColor"
            strokeWidth="1.6"
            strokeLinecap="round"
          />
        </svg>
        <span>Back to system</span>
      </button>
    </div>
  );
}
