import { useEffect, useMemo, useRef, useState } from "react";
import { api } from "../../data/fakeBackend";
import { loadGalaxyData } from "../r3f/shared/galaxyData";
import { getSystemPoiPositionAtTime } from "../r3f/shared/poiOrbit";
import type { GalaxyConnection, GalaxySystem, SystemPointOfInterest, Vec3 } from "../r3f/shared/types";
import { createTravelSession, getTravelSessionIdFromUrl, loadTravelSession, saveTravelSession, setTravelMode, withTravelSession, type TravelSession } from "../r3f/navigation/travelSession";
import { MOVEMENT_SYSTEM_SCALE, MovementScene } from "../r3f/movement/MovementScene";
import type { LocalEntity, LocalWaypoint } from "../r3f/movement/movementState";
import { getGameSliceSessionIdFromUrl, loadGameSliceSession, saveGameSliceSession, withGameSliceSession } from "../game-slice/gameSliceState";
import { useGameSliceSession } from "../game-slice/useGameSliceSession";

const POI_ARRIVAL_RADIUS = 1.4;
const POI_HOLD_OFFSET: Vec3 = [1.45, 0.3, 0.85];

const addVec3 = (a: Vec3, b: Vec3): Vec3 => [a[0] + b[0], a[1] + b[1], a[2] + b[2]];

export function ShipMovementDemo() {
  const [systems, setSystems] = useState<GalaxySystem[]>([]);
  const [connections, setConnections] = useState<GalaxyConnection[]>([]);
  const [entities, setEntities] = useState<LocalEntity[]>([]);
  const [currentSystemId, setCurrentSystemId] = useState("sol");
  const [shipPosition, setShipPosition] = useState<Vec3>([0, 0, 0]);
  const [localWaypoints, setLocalWaypoints] = useState<LocalWaypoint[]>([]);
  const [arrival, setArrival] = useState<string | null>(null);
  const [travelSession, setTravelSession] = useState<TravelSession | null>(null);
  const { session: sliceSession, emit } = useGameSliceSession();
  const initialUrlTargetHandled = useRef(false);

  useEffect(() => {
    loadGalaxyData().then(({ systems, connections }) => {
      setSystems(systems);
      setConnections(connections);
    });
  }, []);

  useEffect(() => {
    api.getNearbyEntities().then((items) => setEntities(items));
  }, [currentSystemId]);

  const currentSystem = systems.find((system) => system.id === currentSystemId);
  const activeWaypoint = localWaypoints[0] ?? null;
  const holdingPoi = activeWaypoint?.type === "poi" && activeWaypoint.arrived;
  const speed = holdingPoi ? 0 : localWaypoints.length ? 120 : 0;
  const localNavigationStatus = activeWaypoint
    ? activeWaypoint.arrived && activeWaypoint.type === "poi"
      ? `HOLDING ORBIT AT ${activeWaypoint.name}`
      : `APPROACHING ${activeWaypoint.name}`
    : "IDLE";

  const createPoiWaypoint = (system: GalaxySystem, poi: SystemPointOfInterest, elapsedTime = 0): LocalWaypoint => ({
    id: poi.id,
    name: poi.name,
    type: "poi",
    systemId: system.id,
    poiId: poi.id,
    position: getSystemPoiPositionAtTime({
      systemId: system.id,
      planets: system.planets,
      pointsOfInterest: system.pointsOfInterest,
      poiId: poi.id,
      elapsedTime,
      scale: MOVEMENT_SYSTEM_SCALE,
      expanded: true,
    }),
    followOrbit: true,
    arrived: false,
  });

  useEffect(() => {
    if (initialUrlTargetHandled.current || systems.length === 0) return;
    initialUrlTargetHandled.current = true;

    const params = new URLSearchParams(window.location.search);
    const navSessionId = getTravelSessionIdFromUrl();
    const sliceSessionId = getGameSliceSessionIdFromUrl();
    const loadedSlice = sliceSessionId ? loadGameSliceSession(sliceSessionId) : null;
    const loadedSession = navSessionId ? loadTravelSession(navSessionId) : null;
    const systemId = params.get("systemId");
    const poiId = params.get("poiId");

    if (navSessionId && loadedSession) {
      setTravelSession(loadedSession);
      setCurrentSystemId(loadedSession.currentSystemId);
      if (loadedSession.status === "arrived" && loadedSession.arrivalMessage) setArrival(loadedSession.arrivalMessage);
    } else if (navSessionId) {
      setArrival("Navigation session unavailable");
    } else if (loadedSlice) {
      setCurrentSystemId(loadedSlice.currentSystemId);
    }

    if (!systemId && !poiId) return;

    const targetSystem = systems.find((system) => system.id === systemId);
    if (targetSystem) setCurrentSystemId(targetSystem.id);
    if (!poiId) return;

    const targetPoi = targetSystem?.pointsOfInterest.find((poi) => poi.id === poiId);
    if (!targetSystem || !targetPoi) {
      setArrival("Navigation target unavailable");
      return;
    }

    setShipPosition([0, 0, 0]);
    setLocalWaypoints([createPoiWaypoint(targetSystem, targetPoi)]);
    setArrival(`Approaching ${targetPoi.name}`);
  }, [systems]);

  const onFrame = (dt: number, elapsedTime: number) => {
    if (localWaypoints.length === 0) return;

    const target = localWaypoints[0];
    let targetPos = target.position;
    if (target.followOrbit && target.systemId && target.poiId) {
      const waypointSystem = systems.find((system) => system.id === target.systemId);
      if (waypointSystem) {
        targetPos = getSystemPoiPositionAtTime({
          systemId: waypointSystem.id,
          planets: waypointSystem.planets,
          pointsOfInterest: waypointSystem.pointsOfInterest,
          poiId: target.poiId,
          elapsedTime,
          scale: MOVEMENT_SYSTEM_SCALE,
          expanded: true,
        });
        setLocalWaypoints((prev) => prev[0]?.id === target.id ? [{ ...prev[0], position: targetPos }, ...prev.slice(1)] : prev);
      }
    }

    const desiredPos = target.followOrbit && target.arrived ? addVec3(targetPos, POI_HOLD_OFFSET) : targetPos;
    setShipPosition((pos) => {
      const dx = desiredPos[0] - pos[0], dz = desiredPos[2] - pos[2];
      const dist = Math.hypot(dx, dz);
      if (target.followOrbit && !target.arrived && dist < POI_ARRIVAL_RADIUS) {
        setArrival(`Holding orbit at ${target.name}`);
        if (target.systemId && target.poiId && getGameSliceSessionIdFromUrl()) {
          emit({ type: "navigation.arrived", systemId: target.systemId, poiId: target.poiId, poiName: target.name });
        }
        setLocalWaypoints((prev) => prev[0]?.id === target.id ? [{ ...prev[0], position: targetPos, arrived: true }, ...prev.slice(1)] : prev);
        return addVec3(targetPos, POI_HOLD_OFFSET);
      }
      if (!target.followOrbit && dist < 0.2) {
        setLocalWaypoints((prev) => prev.slice(1));
        return desiredPos;
      }
      const step = Math.min(dist, dt * 12);
      return dist < 0.001 ? desiredPos : [pos[0] + (dx / dist) * step, desiredPos[1], pos[2] + (dz / dist) * step];
    });
  };

  const routeToEntity = (entity: LocalEntity) => {
    setArrival(null);
    setLocalWaypoints([{
      id: entity.id,
      name: entity.name,
      type: "entity",
      position: [(entity.x - 400) * 0.15, 0, (entity.y - 300) * 0.15],
    }]);
  };

  const routeToPoi = (poi: SystemPointOfInterest, position?: Vec3) => {
    const waypoint = currentSystem
      ? createPoiWaypoint(currentSystem, poi)
      : { id: poi.id, name: poi.name, type: "poi" as const, position: position ?? [0, 0, 0] };
    setLocalWaypoints([waypoint]);
    setArrival(`Approaching ${poi.name}`);
  };

  const openStarMap = () => {
    const session = travelSession
      ? setTravelMode({ ...travelSession, currentSystemId }, travelSession.status === "in_transit" ? "star_map_tracking" : "star_map_planning")
      : createTravelSession({ currentSystemId, mode: "star_map_planning" });
    saveTravelSession(session);
    setTravelSession(session);
    if (sliceSession) saveGameSliceSession({ ...sliceSession, activeTravelSessionId: session.id });
    const url = withTravelSession("/docs/demos/starmap", session.id);
    window.location.href = sliceSession ? withGameSliceSession(url, sliceSession.id) : url;
  };

  const returnToLoop = () => {
    if (!sliceSession) return;
    window.location.href = withGameSliceSession("/docs/demos/game-loop", sliceSession.id);
  };

  const dockAtCurrentPoi = () => {
    if (!sliceSession || !holdingPoi || activeWaypoint?.type !== "poi" || !activeWaypoint.systemId || !activeWaypoint.poiId) return;
    emit({ type: "station.docked", systemId: activeWaypoint.systemId, stationPoiId: activeWaypoint.poiId, stationName: activeWaypoint.name });
    window.location.href = withGameSliceSession("/docs/demos/game-loop", sliceSession.id);
  };

  const sortedEntities = useMemo(() => [...entities].sort((a, b) => (a.distance ?? 0) - (b.distance ?? 0)), [entities]);
  const activePoi = holdingPoi && activeWaypoint?.type === "poi" && currentSystem
    ? currentSystem.pointsOfInterest.find((poi) => poi.id === activeWaypoint.poiId) ?? null
    : null;

  return (
    <div style={{ position: "relative", width: "100%", height: "100vh", background: "#040810", overflow: "hidden", color: "#d4dce8" }}>
      <MovementScene
        currentSystemId={currentSystemId}
        currentSystem={currentSystem ?? null}
        localEntities={entities}
        localWaypoints={localWaypoints}
        shipPosition={shipPosition}
        onFrame={onFrame}
        onWaypointPick={(waypoint) => {
          setArrival(null);
          setLocalWaypoints([waypoint]);
        }}
        onPoiPick={routeToPoi}
      />
      <div style={{ position: "absolute", inset: 0, zIndex: 1, display: "grid", gridTemplateColumns: "260px 1fr 300px", gridTemplateRows: "44px 1fr 132px", gap: 8, padding: 8, pointerEvents: "none" }}>
        <div style={{ gridColumn: "1 / -1", display: "flex", gap: 14, alignItems: "center", padding: "0 12px", background: "rgba(8,12,20,0.86)", border: "1px solid var(--border)", borderRadius: 8, pointerEvents: "auto" }}>
          <button onClick={() => { window.location.href = "/docs/overview"; }}>DOCS</button>
          <strong>{currentSystem?.name ?? "Sol"}</strong>
          <span>SPD {speed.toFixed(0)} m/s</span>
          <span>{localNavigationStatus}</span>
          {arrival && <span style={{ color: "#22c55e" }}>{arrival}</span>}
          {sliceSession && <button onClick={returnToLoop} style={{ marginLeft: "auto" }}>Return to Loop</button>}
          <button onClick={openStarMap} style={{ marginLeft: "auto" }}>Open Star Map</button>
        </div>
        <div style={{ background: "rgba(15,22,35,0.86)", border: "1px solid var(--border)", borderRadius: 8, padding: 12, pointerEvents: "auto", alignSelf: "start" }}>
          <h3 style={{ marginTop: 0 }}>Local Navigation</h3>
          <p style={{ color: "var(--fg-dim)", marginBottom: 8 }}>System: {currentSystem?.name ?? currentSystemId}</p>
          <p style={{ color: "var(--fg-dim)", marginBottom: 8 }}>Ship: {shipPosition.map((value) => value.toFixed(1)).join(", ")}</p>
          <p style={{ color: "var(--fg-dim)", marginBottom: 0 }}>Waypoints: {localWaypoints.length}</p>
          {sliceSession && holdingPoi && activePoi && (
            <div style={{ marginTop: 12, display: "flex", gap: 8, flexWrap: "wrap" }}>
              <button onClick={returnToLoop}>Return to Loop</button>
              {activePoi.type === "station" && <button onClick={dockAtCurrentPoi}>Dock</button>}
              {activePoi.type === "asteroid_belt" && <button onClick={returnToLoop}>Mine</button>}
            </div>
          )}
        </div>
        <div style={{ gridColumn: 3, background: "rgba(15,22,35,0.86)", border: "1px solid var(--border)", borderRadius: 8, padding: 12, pointerEvents: "auto", overflow: "auto" }}>
          <h3 style={{ marginTop: 0 }}>Overview</h3>
          {sortedEntities.map((entity) => (
            <div key={entity.id} style={{ display: "grid", gridTemplateColumns: "1fr auto", gap: 8, padding: "7px 0", borderBottom: "1px solid rgba(255,255,255,0.06)" }}>
              <span>{entity.name}<br /><small>{entity.type} · {entity.distance ?? 0} km</small></span>
              <button onClick={() => routeToEntity(entity)}>Approach</button>
            </div>
          ))}
          {currentSystem?.pointsOfInterest.map((poi) => (
            <div key={poi.id} style={{ display: "grid", gridTemplateColumns: "1fr auto", gap: 8, padding: "7px 0", borderBottom: "1px solid rgba(255,255,255,0.06)" }}>
              <span>{poi.name}<br /><small>{poi.type.replace(/_/g, " ")}</small></span>
              <button onClick={() => routeToPoi(poi)}>Approach</button>
            </div>
          ))}
        </div>
        <div style={{ gridColumn: "1 / -1", background: "rgba(8,12,20,0.78)", border: "1px solid var(--border)", borderRadius: 8, pointerEvents: "auto", padding: 12 }}>
          <div style={{ display: "flex", justifyContent: "space-between", marginBottom: 8 }}>
            <span>{localNavigationStatus}</span>
            <span>{activeWaypoint ? activeWaypoint.name : "No active local target"}</span>
          </div>
          <div style={{ height: 8, background: "#111827", borderRadius: 999, overflow: "hidden" }}>
            <div style={{ width: activeWaypoint ? "68%" : "0%", height: "100%", background: activeWaypoint?.arrived ? "#22c55e" : "#f0a030" }} />
          </div>
        </div>
      </div>
    </div>
  );
}
