// kid-rocket-pack.jsx
// Rocket Builder (RocketPackLevel): tap/keyboard/pointer/touch drag flows.

const ROCKET_TINTABLE_PART_IDS = ["nose", "body", "fins", "booster"];

function isRocketTintablePart(partId) {
  return ROCKET_TINTABLE_PART_IDS.includes(partId);
}

function rocketTintMaskStyle(partId, src) {
  if (!isRocketTintablePart(partId)) return null;
  const cssMaskSrc = src.startsWith("/") ? src : `../${src}`;
  return { "--rocket-tint-mask": `url("${cssMaskSrc}")` };
}

function RocketBuildPartImage({ partId, className, built, src }) {
  const tintable = isRocketTintablePart(partId);
  return (
    <span
      className={`rocket-part-img ${className} ${built ? "on" : ""} ${tintable ? "tintable" : "no-tint"}`}
      style={rocketTintMaskStyle(partId, src)}
    >
      <img src={src} alt="" />
    </span>
  );
}

function RocketChoicePartArt({ part }) {
  const tintable = isRocketTintablePart(part.id);
  return (
    <span
      className={`rocket-choice-art ${tintable ? "tintable" : ""}`}
      style={rocketTintMaskStyle(part.id, part.asset)}
      aria-hidden="true"
    >
      <SafeAssetImage
        src={part.asset}
        alt=""
        context="RocketPack:nextPart"
        fallbackText=""
      />
    </span>
  );
}

function RocketDragGhost({ part, point }) {
  const tintable = isRocketTintablePart(part.id);
  return (
    <span
      className={`rocket-drag-ghost ${tintable ? "tintable" : ""}`}
      style={{
        left: `${point.x}px`,
        top: `${point.y}px`,
        ...rocketTintMaskStyle(part.id, part.asset),
      }}
      aria-hidden="true"
    >
      <img src={part.asset} alt="" />
    </span>
  );
}

function RocketPackLevel({
  builtParts,
  color,
  message,
  launched,
  mode,
  onMode,
  onPart,
  onColor,
  onLaunch,
  onReset,
  onExit,
}) {
  const partSet =
    mode === "engineering"
      ? ROCKET_PARTS
      : ROCKET_PARTS.filter((part) => SIMPLE_ROCKET_PART_IDS.includes(part.id));
  const done = partSet.every((part) => builtParts.includes(part.id));
  const nextPart = partSet.find((part) => !builtParts.includes(part.id));
  const builtCount = partSet.filter((part) => builtParts.includes(part.id)).length;
  const nextPartIndex = nextPart ? partSet.findIndex((part) => part.id === nextPart.id) : -1;
  const colorInfo = resolveRocketColor(color);
  const pickerColor = rocketColorToHex(color);
  const padRef = useRef(null);
  const [draggingPart, setDraggingPart] = useState(null);
  const [dragPoint, setDragPoint] = useState(null);
  const [dragStatus, setDragStatus] = useState(null);
  const dragStartRef = useRef(null);
  const ignoreNextClickRef = useRef(false);
  const isDraggingNextPart = Boolean(draggingPart);
  const currentDragPart = draggingPart
    ? ROCKET_PARTS.find((part) => part.id === draggingPart)
    : null;
  const describeNextPart = () =>
    nextPart
      ? `Next piece ${builtCount + 1} of ${partSet.length}: ${nextPart.label}`
      : "Rocket is ready.";
  const markDragMoved = (clientX, clientY) => {
    if (!dragStartRef.current) return false;
    const dx = clientX - dragStartRef.current.x;
    const dy = clientY - dragStartRef.current.y;
    if (Math.hypot(dx, dy) > 8) dragStartRef.current.moved = true;
    return dragStartRef.current.moved;
  };
  const dropPointInBuildPad = (clientX, clientY) => {
    const rect = padRef.current && padRef.current.getBoundingClientRect();
    const dropForgiveness = 72;
    return Boolean(
      rect &&
        clientX >= rect.left - dropForgiveness &&
        clientX <= rect.right + dropForgiveness &&
        clientY >= rect.top - dropForgiveness &&
        clientY <= rect.bottom + dropForgiveness,
    );
  };
  const finishDragPart = useCallback(
    (clientX, clientY) => {
      if (!draggingPart) return;
      const inPad = dropPointInBuildPad(clientX, clientY);
      const part = ROCKET_PARTS.find((entry) => entry.id === draggingPart);
      const wasMoved = Boolean(
        dragStartRef.current && dragStartRef.current.moved,
      );
      setDraggingPart(null);
      setDragPoint(null);
      dragStartRef.current = null;
      if (wasMoved) ignoreNextClickRef.current = true;
      if (inPad && part && part.id === (nextPart && nextPart.id)) {
        ignoreNextClickRef.current = true;
        setDragStatus({ tone: "good", text: `${part.label} snapped on!` });
        onPart(part);
      } else if (wasMoved && part) {
        setDragStatus({
          tone: "try",
          text: `Move ${part.label} onto the rocket.`,
        });
      }
    },
    [draggingPart, nextPart, onPart],
  );
  const cancelDragPart = () => {
    setDraggingPart(null);
    setDragPoint(null);
    dragStartRef.current = null;
  };
  useEffect(() => {
    if (!draggingPart) return;
    const onPointerMove = (event) => {
      markDragMoved(event.clientX, event.clientY);
      setDragPoint({ x: event.clientX, y: event.clientY });
    };
    const onPointerUp = (event) => finishDragPart(event.clientX, event.clientY);
    const onTouchMove = (event) => {
      const touch = event.touches && event.touches[0];
      if (touch) {
        event.preventDefault();
        markDragMoved(touch.clientX, touch.clientY);
        setDragPoint({ x: touch.clientX, y: touch.clientY });
      }
    };
    const onTouchEnd = (event) => {
      const touch = event.changedTouches && event.changedTouches[0];
      if (touch) finishDragPart(touch.clientX, touch.clientY);
    };
    window.addEventListener("pointermove", onPointerMove);
    window.addEventListener("pointerup", onPointerUp);
    window.addEventListener("pointercancel", cancelDragPart);
    window.addEventListener("touchmove", onTouchMove, { passive: false });
    window.addEventListener("touchend", onTouchEnd);
    window.addEventListener("touchcancel", cancelDragPart);
    return () => {
      window.removeEventListener("pointermove", onPointerMove);
      window.removeEventListener("pointerup", onPointerUp);
      window.removeEventListener("pointercancel", cancelDragPart);
      window.removeEventListener("touchmove", onTouchMove);
      window.removeEventListener("touchend", onTouchEnd);
      window.removeEventListener("touchcancel", cancelDragPart);
    };
  }, [draggingPart, finishDragPart]);
  const beginDragPart = (event, part) => {
    if (part.id !== (nextPart && nextPart.id)) return;
    event.preventDefault();
    setDragStatus({ tone: "ready", text: `Drag ${part.label} to the rocket.` });
    try {
      event.currentTarget.setPointerCapture(event.pointerId);
    } catch {}
    setDraggingPart(part.id);
    setDragPoint({ x: event.clientX, y: event.clientY });
    dragStartRef.current = { x: event.clientX, y: event.clientY, moved: false };
  };
  const moveDragPart = (event) => {
    if (!draggingPart) return;
    markDragMoved(event.clientX, event.clientY);
    setDragPoint({ x: event.clientX, y: event.clientY });
  };
  const endDragPart = (event, part) => {
    if (!draggingPart) return;
    try {
      event.currentTarget.releasePointerCapture(event.pointerId);
    } catch {}
    finishDragPart(event.clientX, event.clientY);
  };
  const tapNextPart = () => {
    if (ignoreNextClickRef.current) {
      ignoreNextClickRef.current = false;
      return;
    }
    if (dragStartRef.current && dragStartRef.current.moved) return;
    if (nextPart) {
      setDragStatus({ tone: "good", text: `${nextPart.label} snapped on!` });
      onPart(nextPart);
    }
  };
  return (
    <div className="rocket-pack-level">
      <Starfield count={260} />
      <button className="rocket-pack-back" onClick={onExit}>
        ← Back to planets
      </button>
      <div
        className={`rocket-builder-stage color-${colorInfo.id} ${launched ? "launching" : ""} ${isDraggingNextPart ? "dragging" : ""}`}
        style={{
          "--rocket-color": colorInfo.value,
          "--rocket-part-filter": colorInfo.filter,
        }}
      >
        <div
          className="rocket-build-pad"
          role="button"
          tabIndex={nextPart && !launched ? 0 : -1}
          aria-label={
            nextPart && !launched
              ? `Add ${nextPart.label} to the rocket`
              : "Rocket build area"
          }
          aria-describedby="rocket-build-status"
          ref={padRef}
          onClick={() => {
            if (nextPart && !launched && !draggingPart) tapNextPart();
          }}
          onKeyDown={(event) => {
            if (!nextPart || launched || draggingPart) return;
            if (event.key === " " || event.key === "Enter") {
              event.preventDefault();
              tapNextPart();
            }
          }}
        >
          {nextPart && !launched ? (
            <div className="rocket-drop-hint">
              {isDraggingNextPart
                ? "Release to snap it on"
                : `Drop ${nextPart.label} here`}
            </div>
          ) : null}
          <div className="rocket-build-ship" aria-hidden="true">
            <RocketBuildPartImage
              partId="nose"
              className="rocket-nose-img"
              built={builtParts.includes("nose")}
              src={GENERATED_ASSETS.rocketNose}
            />
            <RocketBuildPartImage
              partId="body"
              className="rocket-body-img"
              built={builtParts.includes("body")}
              src={GENERATED_ASSETS.rocketBody}
            />
            <RocketBuildPartImage
              partId="fins"
              className="rocket-fins-img"
              built={builtParts.includes("fins")}
              src={GENERATED_ASSETS.rocketFins}
            />
            <RocketBuildPartImage
              partId="window"
              className="rocket-window-img"
              built={builtParts.includes("window")}
              src={GENERATED_ASSETS.rocketWindow}
            />
            <RocketBuildPartImage
              partId="engine"
              className="rocket-engine-img"
              built={builtParts.includes("engine")}
              src={GENERATED_ASSETS.rocketEngine}
            />
            <RocketBuildPartImage
              partId="fuelTank"
              className="rocket-fuel-tank-img"
              built={builtParts.includes("fuelTank")}
              src={GENERATED_ASSETS.rocketFuelTank}
            />
            <RocketBuildPartImage
              partId="fuelPipes"
              className="rocket-fuel-pipes-img"
              built={builtParts.includes("fuelPipes")}
              src={GENERATED_ASSETS.rocketFuelPipes}
            />
            <RocketBuildPartImage
              partId="igniter"
              className="rocket-igniter-img"
              built={builtParts.includes("igniter")}
              src={GENERATED_ASSETS.rocketIgniter}
            />
            <RocketBuildPartImage
              partId="booster"
              className="rocket-booster-img"
              built={builtParts.includes("booster")}
              src={GENERATED_ASSETS.rocketBooster}
            />
            <RocketBuildPartImage
              partId="antenna"
              className="rocket-antenna-img"
              built={builtParts.includes("antenna")}
              src={GENERATED_ASSETS.rocketAntenna}
            />
            <RocketBuildPartImage
              partId="badge"
              className="rocket-badge-img"
              built={builtParts.includes("badge")}
              src={GENERATED_ASSETS.rocketBadge}
            />
            <img
              className="rocket-flame"
              src={GENERATED_ASSETS.rocketFire}
              alt=""
            />
          </div>
        </div>
        <div className="rocket-pack-copy">
          <span>Build Rocket</span>
          <h1>Build Adam's rocket.</h1>
          <p>
            {mode === "engineering"
              ? "Engineer the engine system piece by piece."
              : "Build the big rocket first, then level up."}
          </p>
          <div className="rocket-mode-toggle" aria-label="Rocket builder mode">
            <button
              className={mode === "simple" ? "on" : ""}
              onClick={() => onMode("simple")}
              aria-pressed={mode === "simple" ? "true" : "false"}
            >
              Simple
            </button>
            <button
              className={mode === "engineering" ? "on" : ""}
              onClick={() => onMode("engineering")}
              aria-pressed={mode === "engineering" ? "true" : "false"}
            >
              Engineering
            </button>
          </div>
          <div className="rocket-pack-slots" aria-label="Packed items">
            {partSet.map((part) => {
              const isBuilt = builtParts.includes(part.id);
              const isNext = nextPart && part.id === nextPart.id;
              return (
                <span
                  key={part.id}
                  className={`${isBuilt ? "packed" : ""} ${isNext ? "next" : ""}`}
                  title={part.label}
                >
                  {isBuilt ? (
                    <SafeAssetImage
                      src={part.asset}
                      alt=""
                      context="RocketPack:builtPart"
                      fallbackText=""
                    />
                  ) : isNext ? (
                    nextPartIndex + 1
                  ) : (
                    "?"
                  )}
                </span>
              );
            })}
          </div>
          {nextPart ? (
            <div className="rocket-next-part-cue" aria-label="Next rocket part job">
              <strong>{nextPart.label} job</strong>
              <span>{nextPart.fact}</span>
            </div>
          ) : null}
          <div className="rocket-color-row" aria-label="Rocket color">
            {ROCKET_COLORS.map((entry) => (
              <button
                key={entry.id}
                className={entry.id === colorInfo.id ? "on" : ""}
                style={{ "--swatch": entry.value }}
                onClick={() => onColor(entry.id)}
                aria-label={`${entry.label} rocket`}
                aria-pressed={entry.id === colorInfo.id ? "true" : "false"}
              />
            ))}
            <label className={`rocket-custom-color ${colorInfo.source === "custom" ? "on" : ""}`}>
              <input
                type="color"
                value={pickerColor}
                onChange={(event) => onColor(normalizeRocketColor(event.target.value))}
                aria-label="Choose any rocket color"
              />
              <span>Any color</span>
            </label>
          </div>
          {nextPart ? (
            <button
              className="pack-next-choice"
              onClick={tapNextPart}
              onPointerDown={(event) => beginDragPart(event, nextPart)}
              onPointerMove={moveDragPart}
              onPointerUp={(event) => endDragPart(event, nextPart)}
              onPointerCancel={cancelDragPart}
              aria-label={`Add ${nextPart.label}`}
            >
              <RocketChoicePartArt part={nextPart} />
              <span>
                Next piece {builtCount + 1} of {partSet.length}
              </span>
              <strong>{nextPart.label}</strong>
              <em>Tap here or tap the big rocket.</em>
            </button>
          ) : null}
          {currentDragPart && dragPoint ? (
            <RocketDragGhost part={currentDragPart} point={dragPoint} />
          ) : null}
          {message ? (
            <div className="rocket-pack-fact good" role="status">
              <strong>{message.title}</strong>
              <span>{message.fact}</span>
            </div>
          ) : (
            <div className="rocket-pack-fact" role="status">
              <strong>Try one.</strong>
              <span id="rocket-build-status">
                {dragStatus ? dragStatus.text : describeNextPart()}
              </span>
            </div>
          )}
          {done ? (
            <div className="rocket-pack-done" role="status">
              <strong>{launched ? "Whoosh!" : "Ready for launch!"}</strong>
              <button onClick={launched ? onReset : onLaunch}>
                {launched ? "Build again" : "3 2 1 Launch"}
              </button>
            </div>
          ) : null}
        </div>
      </div>
    </div>
  );
}
