// spaceship-wire-fix.jsx
// Preschool wire-repair mini-game. Loaded before app.jsx and launched from Mission Map.

const WIRE_FIX_ASSET_VERSION = "spaceship-wire-fix-20260521";

const WIRE_FIX_ASSETS = {
  console:
    `data/generated-assets/spaceship-wire-fix/console-sheet.png?v=${WIRE_FIX_ASSET_VERSION}`,
  wires:
    `data/generated-assets/spaceship-wire-fix/wires-sheet.png?v=${WIRE_FIX_ASSET_VERSION}`,
  effects:
    `data/generated-assets/spaceship-wire-fix/effects-sheet.png?v=${WIRE_FIX_ASSET_VERSION}`,
};

const WIRE_FIX_COLORS = [
  {
    id: "red",
    label: "Red",
    role: "Power",
    cue: "Power wakes up the engine computer.",
    socket: 0,
    color: "#f24c3d",
    holeX: 29,
  },
  {
    id: "yellow",
    label: "Yellow",
    role: "Spark",
    cue: "Spark lights the fuel so it can burn.",
    socket: 1,
    color: "#ffd74b",
    holeX: 42,
  },
  {
    id: "green",
    label: "Green",
    role: "Fuel",
    cue: "Fuel has to reach the engine before launch.",
    socket: 2,
    color: "#42df5a",
    holeX: 55,
  },
  {
    id: "blue",
    label: "Blue",
    role: "Cooling",
    cue: "Cooling helps the engine stay steady.",
    socket: 3,
    color: "#3ca3ff",
    holeX: 68,
  },
];

const WIRE_FIX_START_PLACEMENTS = {
  red: 1,
  yellow: 2,
  green: 3,
  blue: 0,
};

function WireFixSprite({ sheet, frame, className = "", label = "", style = {} }) {
  const frameClass = Number.isFinite(frame) ? `frame-${frame}` : "frame-0";
  return (
    <span
      className={`wire-fix-sprite ${frameClass} ${className}`}
      style={{ ...style, backgroundImage: `url(${sheet})` }}
      role={label ? "img" : undefined}
      aria-label={label || undefined}
    />
  );
}

function placementsBySocket(placements) {
  return WIRE_FIX_COLORS.reduce((map, wire) => {
    map[placements[wire.id]] = wire.id;
    return map;
  }, {});
}

function wireFixCorrectCount(placements) {
  return WIRE_FIX_COLORS.filter((wire) => placements[wire.id] === wire.socket)
    .length;
}

function wireFixIsFixed(placements, wireId) {
  const wire = WIRE_FIX_COLORS.find((item) => item.id === wireId);
  return Boolean(wire && placements[wire.id] === wire.socket);
}

function wireFixNextLooseWireId(placements, afterWireId = null) {
  const startIndex = Math.max(
    -1,
    WIRE_FIX_COLORS.findIndex((wire) => wire.id === afterWireId),
  );
  for (let offset = 1; offset <= WIRE_FIX_COLORS.length; offset += 1) {
    const wire =
      WIRE_FIX_COLORS[
        (startIndex + offset + WIRE_FIX_COLORS.length) % WIRE_FIX_COLORS.length
      ];
    if (placements[wire.id] !== wire.socket) return wire.id;
  }
  return null;
}

function wireFixWireForId(wireId) {
  return WIRE_FIX_COLORS.find((wire) => wire.id === wireId) || null;
}

function wireFixSocketForIndex(socketIndex) {
  return WIRE_FIX_COLORS.find((wire) => wire.socket === socketIndex) || null;
}

function wireFixLooseWires(placements) {
  return WIRE_FIX_COLORS.filter((wire) => placements[wire.id] !== wire.socket);
}

function wireFixActiveInstruction(placements, activeWire, complete) {
  if (complete) return "All four engine systems are connected.";
  if (!activeWire) return "Pick a loose wire or press Guide wire.";
  const currentSocket = wireFixSocketForIndex(placements[activeWire.id]);
  const currentLabel = currentSocket
    ? `${currentSocket.label} ${currentSocket.role.toLowerCase()}`
    : "a different";
  return `${activeWire.label} ${activeWire.role.toLowerCase()} is in ${currentLabel}. Move it to ${activeWire.label} ${activeWire.role.toLowerCase()}.`;
}

function SpaceshipWireFixLevel({ onExit }) {
  const [placements, setPlacements] = useState(WIRE_FIX_START_PLACEMENTS);
  const [selectedWire, setSelectedWire] = useState("red");
  const [message, setMessage] = useState("Start with the red power wire.");
  const [bumpKey, setBumpKey] = useState(0);
  const [reward, setReward] = useState(null);
  const [lastTouchedSocket, setLastTouchedSocket] = useState(null);
  const [testPulse, setTestPulse] = useState(null);
  const rewardTimerRef = useRef(null);
  const testTimerRef = useRef(null);
  const sockets = placementsBySocket(placements);
  const correctCount = wireFixCorrectCount(placements);
  const complete = correctCount === WIRE_FIX_COLORS.length;
  const selected = WIRE_FIX_COLORS.find((wire) => wire.id === selectedWire);
  const selectedIsFixed = selected ? wireFixIsFixed(placements, selected.id) : false;
  const activeWire = selected && !selectedIsFixed ? selected : null;
  const consoleFrame = complete ? 3 : correctCount >= 3 ? 2 : correctCount === 0 ? 1 : 0;
  const activeInstruction = wireFixActiveInstruction(placements, activeWire, complete);

  const showWireReward = (wire, count, kind = "match") => {
    window.clearTimeout(rewardTimerRef.current);
    setReward({
      key: Date.now(),
      kind,
      label:
        kind === "complete"
          ? "Blastoff reward!"
          : `${wire.role} connected! ${count} engine ${count === 1 ? "star" : "stars"}.`,
    });
    rewardTimerRef.current = window.setTimeout(() => setReward(null), 1400);
  };

  const moveWireToSocket = (wireId, nextSocket) => {
    const wire = WIRE_FIX_COLORS.find((item) => item.id === wireId);
    if (!wire || complete) return;
    const safeSocket = Math.max(0, Math.min(WIRE_FIX_COLORS.length - 1, nextSocket));
    setPlacements((current) => {
      const oldSocket = current[wireId];
      if (oldSocket === safeSocket) return current;
      if (safeSocket !== wire.socket) {
        const socket = wireFixSocketForIndex(safeSocket);
        setSelectedWire(wireId);
        setMessage(
          `${socket ? socket.label : "That"} hole is not for ${wire.label}. Follow the glowing ${wire.label} ${wire.role.toLowerCase()} port.`,
        );
        setLastTouchedSocket(safeSocket);
        setBumpKey((key) => key + 1);
        playKidSound("boop");
        return current;
      }
      const otherWire = WIRE_FIX_COLORS.find(
        (item) => item.id !== wireId && current[item.id] === safeSocket,
      );
      const next = {
        ...current,
        [wireId]: safeSocket,
      };
      if (otherWire) next[otherWire.id] = oldSocket;
      const nextCorrect = wireFixCorrectCount(next);
      const nextWireId = wireFixNextLooseWireId(next, wire.id);
      const nextWire = WIRE_FIX_COLORS.find((item) => item.id === nextWireId);
      setSelectedWire(nextWireId);
      setMessage(
        nextWire
          ? `${wire.label} ${wire.role.toLowerCase()} connected. Now fix ${nextWire.label} ${nextWire.role.toLowerCase()}.`
          : "All wires fixed. The spaceship is ready.",
      );
      showWireReward(wire, nextCorrect);
      playKidSound("chime");
      setLastTouchedSocket(safeSocket);
      setBumpKey((key) => key + 1);
      return next;
    });
  };

  const stepSelectedWire = (direction) => {
    if (complete) return;
    const looseWires = wireFixLooseWires(placements);
    if (!looseWires.length) return;
    const currentIndex = Math.max(
      0,
      looseWires.findIndex((wire) => wire.id === activeWire?.id),
    );
    const nextIndex =
      (currentIndex + direction + looseWires.length) % looseWires.length;
    const nextWire = looseWires[nextIndex];
    setSelectedWire(nextWire.id);
    setLastTouchedSocket(placements[nextWire.id]);
    setMessage(
      `${nextWire.label} ${nextWire.role.toLowerCase()} picked. Tap its glowing port.`,
    );
    setBumpKey((key) => key + 1);
  };

  const guideWire = () => {
    if (complete) return;
    const wire = activeWire || wireFixWireForId(wireFixNextLooseWireId(placements));
    if (!wire) return;
    setSelectedWire(wire.id);
    moveWireToSocket(wire.id, wire.socket);
  };

  const runEngineTest = () => {
    window.clearTimeout(testTimerRef.current);
    const missingWire =
      activeWire || wireFixWireForId(wireFixNextLooseWireId(placements));
    setTestPulse({ key: Date.now(), complete, correctCount });
    testTimerRef.current = window.setTimeout(() => setTestPulse(null), 1100);
    if (complete) {
      setMessage("Engine test passed. Smooth power, spark, fuel, and cooling.");
      playKidSound("success");
    } else if (missingWire) {
      setSelectedWire(missingWire.id);
      setLastTouchedSocket(placements[missingWire.id]);
      setMessage(
        `Engine test needs ${missingWire.label} ${missingWire.role.toLowerCase()} next.`,
      );
      playKidSound("pop");
    }
    setBumpKey((key) => key + 1);
  };

  const reset = () => {
    setPlacements(WIRE_FIX_START_PLACEMENTS);
    setSelectedWire("red");
    setMessage("Start with the red power wire.");
    setReward(null);
    setTestPulse(null);
    setLastTouchedSocket(null);
    window.clearTimeout(rewardTimerRef.current);
    window.clearTimeout(testTimerRef.current);
    setBumpKey((key) => key + 1);
  };

  useEffect(
    () => () => {
      window.clearTimeout(rewardTimerRef.current);
      window.clearTimeout(testTimerRef.current);
    },
    [],
  );

  useEffect(() => {
    if (!complete) return;
    setSelectedWire(null);
    setMessage("Engine fixed. The spaceship is ready.");
    showWireReward(WIRE_FIX_COLORS[WIRE_FIX_COLORS.length - 1], correctCount, "complete");
    window.SpaceExplorerFoundation?.celebrateGameFinish?.({
      gameId: "spaceship-wire-fix",
      emoji: "🔌",
      title: "Ship powered up!",
      message: "Every colorful wire is fixed.",
    });
    playKidSound("success");
  }, [complete]);

  useWindowKeyHandler(
    (event) => {
      if (event.key === "ArrowLeft") {
        event.preventDefault();
        stepSelectedWire(-1);
      } else if (event.key === "ArrowRight") {
        event.preventDefault();
        stepSelectedWire(1);
      } else if (event.key === "Escape") {
        event.preventDefault();
        onExit();
      } else if (event.key === "Enter" || event.key === " ") {
        event.preventDefault();
        if (!activeWire) return;
        moveWireToSocket(activeWire.id, activeWire.socket);
      }
    },
    true,
  );

  return (
    <section className="wire-fix-level" aria-label="Spaceship wire repair game">
      <Starfield count={180} />
      <button className="planet-sort-back wire-fix-back" onClick={onExit}>
        Back to planets
      </button>
      <div className="wire-fix-shell">
        <div
          className={`wire-fix-console-card ${
            testPulse
              ? `testing ${testPulse.complete ? "test-ready" : "test-needs-help"}`
              : ""
          }`}
        >
          <WireFixSprite
            sheet={WIRE_FIX_ASSETS.console}
            frame={consoleFrame}
            className={`wire-fix-console ${complete ? "complete" : ""}`}
            label={complete ? "Happy spaceship engine" : "Spaceship engine console"}
          />
          <div className="wire-fix-console-leads" aria-hidden="true">
            {WIRE_FIX_COLORS.map((hole) => {
              const wireId = sockets[hole.socket];
              const wire = WIRE_FIX_COLORS.find((item) => item.id === wireId);
              const isSelected =
                activeWire && (activeWire.id === wireId || activeWire.id === hole.id);
              const isCorrect = wire && wire.id === hole.id;
              return (
                <span
                  key={hole.id}
                  className={[
                    "wire-fix-console-hole",
                    `hole-${hole.id}`,
                    wire ? `wire-${wire.id}` : "",
                    isCorrect ? "connected" : "",
                    isSelected ? "selected" : "",
                  ]
                    .filter(Boolean)
                    .join(" ")}
                  style={{
                    "--wire-fix-hole-x": `${hole.holeX}%`,
                    "--wire-fix-color": wire ? wire.color : hole.color,
                  }}
                >
                  {wire ? <i /> : null}
                </span>
              );
            })}
          </div>
          <div className="wire-fix-robot-wires" aria-hidden="true">
            {WIRE_FIX_COLORS.map((hole) => {
              const wireId = sockets[hole.socket];
              const wire = WIRE_FIX_COLORS.find((item) => item.id === wireId);
              if (!wire) return null;
              const isSelected = activeWire && activeWire.id === wire.id;
              const isCorrect = wire.id === hole.id;
              return (
                <WireFixSprite
                  key={`${hole.id}-${wire.id}`}
                  sheet={WIRE_FIX_ASSETS.wires}
                  frame={wire.socket}
                  className={[
                    "wire-fix-robot-wire",
                    `robot-wire-${wire.id}`,
                    `robot-hole-${hole.id}`,
                    isSelected ? "selected" : "",
                    isCorrect ? "connected" : "",
                  ]
                    .filter(Boolean)
                    .join(" ")}
                  style={{
                    "--wire-fix-hole-x": `${hole.holeX}%`,
                    "--wire-fix-color": wire.color,
                  }}
                />
              );
            })}
          </div>
          {testPulse ? (
            <div
              className={`wire-fix-power-wave ${testPulse.complete ? "complete" : ""}`}
              key={testPulse.key}
              aria-hidden="true"
            >
              {WIRE_FIX_COLORS.map((wire) => (
                <span
                  key={wire.id}
                  className={placements[wire.id] === wire.socket ? "on" : ""}
                  style={{ "--wire-fix-color": wire.color }}
                />
              ))}
            </div>
          ) : null}
          {complete ? (
            <>
              <WireFixSprite
                sheet={WIRE_FIX_ASSETS.effects}
                frame={2}
                className="wire-fix-reward-star"
                label="Star reward"
              />
              <WireFixSprite
                sheet={WIRE_FIX_ASSETS.effects}
                frame={3}
                className="wire-fix-flame"
                label="Rocket flame"
              />
            </>
          ) : null}
          {reward ? (
            <div className={`wire-fix-reward-pop ${reward.kind}`} key={reward.key}>
              <WireFixSprite
                sheet={WIRE_FIX_ASSETS.effects}
                frame={reward.kind === "complete" ? 3 : 2}
                className="wire-fix-reward-pop-art"
                label=""
              />
              <strong>{reward.label}</strong>
            </div>
          ) : null}
        </div>

        <div className="wire-fix-panel">
          <span className="wire-fix-kicker">Spaceship Wire Fix</span>
          <h1>{complete ? "Engine ready!" : "Wire the engine path"}</h1>
          <p>Connect power, spark, fuel, and cooling so the rocket can launch.</p>
          <div className="wire-fix-message" role="status" key={bumpKey}>
            {message}
          </div>
          <div className="wire-fix-engine-stars" aria-label={`${correctCount} of 4 engine stars`}>
            {WIRE_FIX_COLORS.map((wire) => {
              const isOn = placements[wire.id] === wire.socket;
              return (
                <span key={wire.id} className={isOn ? "on" : ""}>
                  ★
                </span>
              );
            })}
          </div>
          <div
            className="wire-fix-engine-path"
            aria-label={`${correctCount} of 4 engine systems connected`}
          >
            {WIRE_FIX_COLORS.map((wire) => {
              const isOn = placements[wire.id] === wire.socket;
              const isActive = activeWire && activeWire.id === wire.id;
              return (
                <span
                  key={wire.id}
                  className={`${isOn ? "on" : ""} ${isActive ? "active" : ""}`}
                  style={{ "--wire-fix-color": wire.color }}
                >
                  <i aria-hidden="true" />
                  <strong>{wire.role}</strong>
                  <small>{isOn ? "Ready" : isActive ? "Next" : "Waiting"}</small>
                </span>
              );
            })}
          </div>

          <div className="wire-fix-sockets" aria-label="Wire sockets">
            {WIRE_FIX_COLORS.map((socket, socketIndex) => {
              const wireId = sockets[socketIndex];
              const wire = WIRE_FIX_COLORS.find((item) => item.id === wireId);
              const isSelected = activeWire && wire && wire.id === activeWire.id;
              const isTarget = activeWire && socket.id === activeWire.id && !complete;
              const isCorrect = wire && wire.socket === socketIndex;
              return (
                <button
                  key={socket.id}
                  type="button"
                  className={`wire-fix-socket target-${socket.id} ${
                    isSelected ? "selected" : ""
                  } ${isTarget ? "target-ready" : ""} ${lastTouchedSocket === socketIndex ? "last-touched" : ""} ${isCorrect ? "correct" : ""}`}
                  style={{ "--wire-fix-color": socket.color }}
                  onClick={() => {
                    if (activeWire && socket.id === activeWire.id) {
                      moveWireToSocket(activeWire.id, socketIndex);
                      return;
                    }
                    if (activeWire && !isTarget) {
                      setSelectedWire(activeWire.id);
                      setMessage(
                        `${socket.label} is not the ${activeWire.label} port. Try the glowing ${activeWire.label} ${activeWire.role.toLowerCase()} port.`,
                      );
                      setLastTouchedSocket(socketIndex);
                      setBumpKey((key) => key + 1);
                      playKidSound("boop");
                      return;
                    }
                    if (wire) {
                      if (isCorrect) {
                        const nextWireId = wireFixNextLooseWireId(placements, wire.id);
                        const nextWire = WIRE_FIX_COLORS.find((item) => item.id === nextWireId);
                        setSelectedWire(nextWireId);
                        setMessage(
                          nextWire
                            ? `${wire.label} ${wire.role.toLowerCase()} is ready. Now fix ${nextWire.label} ${nextWire.role.toLowerCase()}.`
                            : `${wire.label} ${wire.role.toLowerCase()} is ready.`,
                        );
                      } else {
                        setSelectedWire(wire.id);
                        setMessage(
                          `${wire.label} ${wire.role.toLowerCase()} picked. Tap the matching port.`,
                        );
                      }
                      setLastTouchedSocket(socketIndex);
                      setBumpKey((key) => key + 1);
                    }
                  }}
                  aria-label={`${socket.label} ${socket.role} port${
                    wire ? ` holding ${wire.label} ${wire.role} wire` : ""
                  }`}
                >
                  <span className="wire-fix-socket-dot" />
                  <span className="wire-fix-hole-name">
                    {socket.label} {socket.role}
                  </span>
                  {wire ? (
                    <>
                      <span
                        className="wire-fix-end-to-hole"
                        style={{ "--wire-fix-color": wire.color }}
                        aria-hidden="true"
                      />
                      <WireFixSprite
                        sheet={WIRE_FIX_ASSETS.wires}
                        frame={wire.socket}
                        className={`wire-fix-wire wire-${wire.id}`}
                        label={`${wire.label} wire`}
                      />
                      <span
                        className="wire-fix-plug-snap left"
                        style={{ "--wire-fix-color": wire.color }}
                        aria-hidden="true"
                      />
                      <span
                        className="wire-fix-plug-snap right"
                        style={{ "--wire-fix-color": wire.color }}
                        aria-hidden="true"
                      />
                    </>
                  ) : null}
                  {isTarget ? <span className="wire-fix-target-label">Connect here</span> : null}
                  {isCorrect ? (
                    <>
                      <span className="wire-fix-check">OK</span>
                      <span className="wire-fix-socket-sparkles" aria-hidden="true">
                        ★
                      </span>
                    </>
                  ) : null}
                </button>
              );
            })}
          </div>

          <div className="wire-fix-direct-help" aria-live="polite">
            <span
              className={`wire-fix-selected-dot dot-${activeWire ? activeWire.id : "none"}`}
              aria-hidden="true"
            />
            <span>
              <strong>{activeInstruction}</strong>
              {activeWire ? <small>{activeWire.cue}</small> : null}
            </span>
          </div>

          <div className="wire-fix-palette" aria-label="Pick a wire">
            {WIRE_FIX_COLORS.map((wire) => (
              <button
                key={wire.id}
                type="button"
                className={`wire-fix-pick pick-${wire.id} ${
                  activeWire && activeWire.id === wire.id ? "selected" : ""
                } ${placements[wire.id] === wire.socket ? "correct" : ""}`}
                onClick={() => {
                  if (wireFixIsFixed(placements, wire.id)) {
                    const nextWireId = wireFixNextLooseWireId(placements, wire.id);
                    const nextWire = WIRE_FIX_COLORS.find((item) => item.id === nextWireId);
                    setSelectedWire(nextWireId);
                    setMessage(
                      nextWire
                        ? `${wire.label} ${wire.role.toLowerCase()} is ready. Now fix ${nextWire.label} ${nextWire.role.toLowerCase()}.`
                        : `${wire.label} ${wire.role.toLowerCase()} is ready.`,
                    );
                  } else {
                    setSelectedWire(wire.id);
                    setMessage(
                      `${wire.label} ${wire.role.toLowerCase()} picked. Tap the matching port.`,
                    );
                  }
                  setLastTouchedSocket(placements[wire.id]);
                  setBumpKey((key) => key + 1);
                }}
                aria-pressed={Boolean(activeWire && activeWire.id === wire.id)}
              >
                <span>{placements[wire.id] === wire.socket ? "Ready" : wire.label}</span>
                <small>{wire.role}</small>
              </button>
            ))}
          </div>

          <div className="wire-fix-actions">
            {!complete ? (
              <button type="button" onClick={guideWire}>
                Guide wire
              </button>
            ) : null}
            <button type="button" onClick={runEngineTest}>
              {complete ? "Test launch" : "Test engine"}
            </button>
            <button type="button" onClick={reset}>
              Mix again
            </button>
            {complete ? (
              <button type="button" onClick={onExit}>
                Done
              </button>
            ) : null}
          </div>
        </div>
      </div>
    </section>
  );
}

window.SpaceExplorerWireFix = {
  assets: WIRE_FIX_ASSETS,
  colors: WIRE_FIX_COLORS,
};
