import { useEffect, useMemo, useRef, useState } from "react";
import type { ThreeEvent } from "@react-three/fiber";
import { loadGalaxyData } from "../r3f/shared/galaxyData";
import type { GalaxySystem, PoiNavigationTarget, SystemPointOfInterest, Vec3 } from "../r3f/shared/types";
import { computeWaypointRoute, createStarMapState, type StarMapState } from "../r3f/starmap/starMapState";
import { StarMapScene, starMapToWorld } from "../r3f/starmap/StarMapScene";
import { findShortestRoute } from "../r3f/shared/routing";
import {
  advanceTravelSession,
  createTravelSession,
  getTravelSessionIdFromUrl,
  loadTravelSession,
  planRouteForSession,
  saveTravelSession,
  setTravelMode,
  startJump,
  withTravelSession,
  type TravelSession,
} from "../r3f/navigation/travelSession";
import { getGameSliceSessionIdFromUrl, loadGameSliceSession, saveGameSliceSession, withGameSliceSession } from "../game-slice/gameSliceState";
import { preserveSliceSession, syncSliceFromTravelArrival } from "../game-slice/sliceNavigationBridge";
import { SliceDemoLinks } from "../game-slice/ui/SliceDemoLinks";
import type { GameSliceSession } from "../game-slice/types";

type DestinationPoi = PoiNavigationTarget & {
  poi: SystemPointOfInterest;
};

const buildPoiTarget = (systemId: string, poi: SystemPointOfInterest, status: PoiNavigationTarget["status"] = "approaching"): DestinationPoi => ({
  systemId,
  poiId: poi.id,
  poiName: poi.name,
  status,
  poi,
});

export function StarMapDemo() {
  const [state, setState] = useState<StarMapState | null>(null);
  const [travelSession, setTravelSession] = useState<TravelSession | null>(null);
  const [focusTarget, setFocusTarget] = useState<Vec3 | null>(null);
  const [selectedPoi, setSelectedPoi] = useState<{ systemId: string; poi: SystemPointOfInterest } | null>(null);
  const [destinationPoi, setDestinationPoi] = useState<DestinationPoi | null>(null);
  const [sliceSession, setSliceSession] = useState<GameSliceSession | null>(null);
  const [contextMenu, setContextMenu] = useState<{ x: number; y: number; target: GalaxySystem } | null>(null);
  const lastPersistedAt = useRef(0);

  useEffect(() => {
    loadGalaxyData().then(({ systems, connections }) => {
      const initial = createStarMapState(systems, connections);
      const navSessionId = getTravelSessionIdFromUrl();
      const sliceSessionId = getGameSliceSessionIdFromUrl();
      const loadedSlice = sliceSessionId ? loadGameSliceSession(sliceSessionId) : null;
      const loadedSession = navSessionId ? loadTravelSession(navSessionId) : null;
      if (loadedSlice) setSliceSession(loadedSlice);
      if (!loadedSession) {
        setState(loadedSlice ? { ...initial, currentSystemId: loadedSlice.currentSystemId, selectedSystemId: loadedSlice.currentSystemId } : initial);
        return;
      }
      if (loadedSession.status === "arrived") {
        const synced = syncSliceFromTravelArrival(sliceSessionId, loadedSession);
        if (synced) setSliceSession(synced);
      }

      const poiTarget = loadedSession.destinationPoi
        ? systems
          .find((system) => system.id === loadedSession.destinationPoi?.systemId)
          ?.pointsOfInterest.find((poi) => poi.id === loadedSession.destinationPoi?.poiId)
        : null;
      if (poiTarget && loadedSession.destinationPoi) {
        setDestinationPoi(buildPoiTarget(loadedSession.destinationPoi.systemId, poiTarget, loadedSession.destinationPoi.status));
        setSelectedPoi({ systemId: loadedSession.destinationPoi.systemId, poi: poiTarget });
      }

      setTravelSession(loadedSession);
      setState({
        ...initial,
        currentSystemId: loadedSession.currentSystemId,
        selectedSystemId: loadedSession.destinationSystemId ?? loadedSession.currentSystemId,
        destinationSystemId: loadedSession.destinationSystemId,
        waypointSystemIds: loadedSession.waypointSystemIds,
        route: loadedSession.route,
      });
    });
  }, []);

  useEffect(() => {
    const close = () => setContextMenu(null);
    window.addEventListener("click", close);
    return () => window.removeEventListener("click", close);
  }, []);

  useEffect(() => {
    if (!state || !travelSession || travelSession.status !== "in_transit" || travelSession.mode !== "star_map_tracking") return;
    let raf = 0;
    let last = performance.now();

    const tick = (now: number) => {
      const dt = Math.min(0.08, (now - last) / 1000);
      last = now;
      setTravelSession((prev) => {
        if (!prev || prev.status !== "in_transit" || prev.mode !== "star_map_tracking") return prev;
        const next = advanceTravelSession(prev, dt);
        setState((prevState) => prevState && {
          ...prevState,
          currentSystemId: next.currentSystemId,
          destinationSystemId: next.destinationSystemId,
          waypointSystemIds: next.waypointSystemIds,
          route: next.route,
        });
        if (now - lastPersistedAt.current > 250 || next.status === "arrived") {
          saveTravelSession(next);
          if (next.status === "arrived") {
            const synced = syncSliceFromTravelArrival(getGameSliceSessionIdFromUrl(), next);
            if (synced) setSliceSession(synced);
          }
          lastPersistedAt.current = now;
        }
        return next;
      });
      raf = requestAnimationFrame(tick);
    };

    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [state, travelSession?.id, travelSession?.mode, travelSession?.status]);

  const filteredSystems = useMemo(() => {
    if (!state) return [];
    let systems = state.systems;
    if (state.searchQuery) {
      const query = state.searchQuery.toLowerCase();
      systems = systems.filter((system) => system.name.toLowerCase().includes(query) || system.id.includes(query));
    }
    if (state.securityFilter === "highsec") systems = systems.filter((system) => system.security >= 0.5);
    if (state.securityFilter === "lowsec") systems = systems.filter((system) => system.security >= 0.2 && system.security < 0.5);
    if (state.securityFilter === "nullsec") systems = systems.filter((system) => system.security < 0.2);
    return systems;
  }, [state]);

  if (!state) return <div style={{ padding: 24 }}>Loading star map...</div>;

  const selected = state.selectedSystemId ? state.systems.find((system) => system.id === state.selectedSystemId) ?? null : null;
  const destination = state.destinationSystemId ? state.systems.find((system) => system.id === state.destinationSystemId) ?? null : null;
  const activeTravel = travelSession?.status === "in_transit";
  const arrived = travelSession?.status === "arrived";
  const canOpenMovement = !activeTravel || arrived;
  const sliceSessionId = sliceSession?.id ?? getGameSliceSessionIdFromUrl();
  const movementUrlBase = destinationPoi && arrived
    ? `/docs/demos/movement?navSession=${travelSession.id}&systemId=${encodeURIComponent(destinationPoi.systemId)}&poiId=${encodeURIComponent(destinationPoi.poiId)}`
    : destinationPoi && destinationPoi.systemId === state.currentSystemId
      ? `/docs/demos/movement?systemId=${encodeURIComponent(destinationPoi.systemId)}&poiId=${encodeURIComponent(destinationPoi.poiId)}`
      : travelSession
        ? withTravelSession("/docs/demos/movement", travelSession.id)
        : "/docs/demos/movement";
  const movementUrl = preserveSliceSession(movementUrlBase, sliceSessionId);

  const rememberSessionUrl = (session: TravelSession) => {
    if (getTravelSessionIdFromUrl() === session.id) return;
    window.history.replaceState(null, "", withTravelSession(`${window.location.pathname}${window.location.search}`, session.id));
  };

  const persistSession = (session: TravelSession) => {
    saveTravelSession(session);
    setTravelSession(session);
    if (sliceSession) {
      const nextSlice = { ...sliceSession, activeTravelSessionId: session.id, currentSystemId: session.status === "in_transit" ? session.currentSystemId : sliceSession.currentSystemId };
      saveGameSliceSession(nextSlice);
      setSliceSession(nextSlice);
    }
    rememberSessionUrl(session);
  };

  const ensureSession = () => {
    if (travelSession) return travelSession;
    const session = createTravelSession({ currentSystemId: state.currentSystemId, mode: "star_map_planning" });
    persistSession(session);
    return session;
  };

  const routeTo = (destinationId: string, waypointIds = state.waypointSystemIds) => {
    const stops = [state.currentSystemId, ...waypointIds, destinationId];
    if (stops.length < 2) return null;
    return waypointIds.length
      ? computeWaypointRoute(state, destinationId, waypointIds)
      : findShortestRoute(state.currentSystemId, destinationId, state.systems, state.connections);
  };

  const setDestination = (system: GalaxySystem) => {
    const route = routeTo(system.id);
    const session = planRouteForSession({ ...ensureSession(), currentSystemId: state.currentSystemId, destinationPoi: null }, route, system.id, state.waypointSystemIds);
    persistSession(session);
    setDestinationPoi(null);
    setState((prev) => prev && { ...prev, selectedSystemId: system.id, destinationSystemId: system.id, route });
  };

  const navigateToPoi = (system: GalaxySystem, poi: SystemPointOfInterest) => {
    const route = routeTo(system.id);
    const poiTarget = buildPoiTarget(system.id, poi);
    const session = planRouteForSession({ ...ensureSession(), currentSystemId: state.currentSystemId, destinationPoi: poiTarget }, route, system.id, state.waypointSystemIds);
    const nextSession = { ...session, destinationPoi: poiTarget };
    persistSession(nextSession);
    setSelectedPoi({ systemId: system.id, poi });
    setDestinationPoi(poiTarget);
    setState((prev) => prev && { ...prev, selectedSystemId: system.id, destinationSystemId: system.id, route });
  };

  const addWaypoint = (system: GalaxySystem) => {
    if (state.waypointSystemIds.includes(system.id)) return;
    const waypointSystemIds = [...state.waypointSystemIds, system.id];
    const route = state.destinationSystemId ? routeTo(state.destinationSystemId, waypointSystemIds) : null;
    const session = planRouteForSession(ensureSession(), route, state.destinationSystemId, waypointSystemIds);
    persistSession(session);
    setState((prev) => prev && { ...prev, waypointSystemIds, route });
  };

  const beginJump = () => {
    if (!state.route) return;
    const session = startJump({ ...ensureSession(), route: state.route, destinationSystemId: state.destinationSystemId, waypointSystemIds: state.waypointSystemIds, destinationPoi });
    persistSession(session);
    setState((prev) => prev && {
      ...prev,
      currentSystemId: session.currentSystemId,
      destinationSystemId: session.destinationSystemId,
      waypointSystemIds: session.waypointSystemIds,
      route: session.route,
    });
  };

  const viewWarp = () => {
    if (!travelSession) return;
    const session = setTravelMode(travelSession, travelSession.status === "in_transit" ? "warp_bubble" : "star_map_planning");
    persistSession(session);
    window.location.href = preserveSliceSession(withTravelSession("/docs/demos/warp", session.id), sliceSessionId);
  };

  const trackOnMap = () => {
    if (!travelSession) return;
    persistSession(setTravelMode(travelSession, "star_map_tracking"));
  };

  const openMovement = () => {
    window.location.href = movementUrl;
  };

  const clearRoute = () => {
    const session = ensureSession();
    const nextSession = {
      ...session,
      destinationSystemId: null,
      waypointSystemIds: [],
      route: null,
      status: "idle" as const,
      activeSegmentIndex: 0,
      segmentProgress: 0,
      totalProgress: 0,
      mode: "star_map_planning" as const,
      arrivalMessage: null,
      destinationPoi: null,
    };
    persistSession(nextSession);
    setDestinationPoi(null);
    setState((prev) => prev && { ...prev, destinationSystemId: null, waypointSystemIds: [], route: null });
  };

  const handleContext = (system: GalaxySystem, event: ThreeEvent<MouseEvent>) => {
    event.nativeEvent.preventDefault();
    setContextMenu({ target: system, x: Math.min(event.nativeEvent.clientX, window.innerWidth - 220), y: Math.min(event.nativeEvent.clientY, window.innerHeight - 220) });
  };

  return (
    <div style={{ position: "relative", width: "100%", height: "100vh", background: "#040810", overflow: "hidden", color: "#d4dce8" }}>
      <StarMapScene
        systems={state.systems}
        connections={state.connections}
        selectedSystemId={state.selectedSystemId}
        hoveredSystemId={state.hoveredSystemId}
        currentSystemId={state.currentSystemId}
        destinationSystemId={state.destinationSystemId}
        selectedPoiId={selectedPoi?.poi.id ?? destinationPoi?.poiId ?? null}
        poiNavigationTarget={destinationPoi}
        waypointSystemIds={state.waypointSystemIds}
        route={state.route}
        travelStatus={travelSession?.status}
        activeSegmentIndex={travelSession?.activeSegmentIndex}
        segmentProgress={travelSession?.segmentProgress}
        focusTarget={focusTarget}
        onSelect={(system) => {
          setSelectedPoi(null);
          setState((prev) => prev && { ...prev, selectedSystemId: system.id });
        }}
        onPoiSelect={(system, poi) => {
          setSelectedPoi({ systemId: system.id, poi });
          setState((prev) => prev && { ...prev, selectedSystemId: system.id });
        }}
        onPoiNavigationStatusChange={(status) => {
          setDestinationPoi((prev) => prev && { ...prev, status });
        }}
        onHover={(system) => setState((prev) => prev && { ...prev, hoveredSystemId: system?.id ?? null })}
        onDoubleClick={(system) => setFocusTarget(starMapToWorld(system))}
        onContextMenu={handleContext}
      />
      {contextMenu && (
        <div onClick={(event) => event.stopPropagation()} style={{ position: "fixed", left: contextMenu.x, top: contextMenu.y, zIndex: 5, minWidth: 210, background: "rgba(10,16,28,0.97)", border: "1px solid var(--border-light)", borderRadius: 8, overflow: "hidden", fontFamily: "var(--font-mono)", fontSize: 11 }}>
          <div style={{ padding: 10, borderBottom: "1px solid var(--border)", color: contextMenu.target.color }}>{contextMenu.target.name}</div>
          <button style={{ display: "block", width: "100%" }} onClick={() => { setState((prev) => prev && { ...prev, selectedSystemId: contextMenu.target.id }); setContextMenu(null); }}>Show Details</button>
          <button style={{ display: "block", width: "100%" }} onClick={() => { setDestination(contextMenu.target); setContextMenu(null); }}>Set Destination</button>
          <button style={{ display: "block", width: "100%" }} onClick={() => { addWaypoint(contextMenu.target); setContextMenu(null); }}>Add Waypoint</button>
          <button style={{ display: "block", width: "100%" }} onClick={() => { setFocusTarget(starMapToWorld(contextMenu.target)); setContextMenu(null); }}>Focus Camera</button>
        </div>
      )}
      <div style={{ position: "absolute", inset: 0, zIndex: 1, pointerEvents: "none", display: "flex", flexDirection: "column" }}>
        <div style={{ height: 44, display: "flex", alignItems: "center", gap: 14, padding: "0 14px", background: "rgba(8,12,20,0.88)", pointerEvents: "auto" }}>
          <button onClick={() => window.history.back()}>DOCS</button>
          <strong>STAR MAP</strong>
          <span>Current: {state.systems.find((system) => system.id === state.currentSystemId)?.name ?? state.currentSystemId}</span>
          <span>Destination: {destinationPoi ? `${destinationPoi.poiName} @ ${destination?.name ?? destinationPoi.systemId}` : destination?.name ?? "None"}</span>
          <span>Status: {travelSession?.arrivalMessage ?? travelSession?.status ?? "idle"}</span>
          <span>{Math.round((travelSession?.totalProgress ?? 0) * 100)}%</span>
          <button onClick={beginJump} disabled={!state.route || activeTravel}>Start Jump</button>
          <button onClick={viewWarp} disabled={!travelSession || (!state.route && !activeTravel && !arrived)}>View Warp</button>
          <button onClick={trackOnMap} disabled={!activeTravel}>Track on Map</button>
          <button onClick={openMovement} disabled={!canOpenMovement}>Open Movement</button>
          <button onClick={clearRoute}>Clear Route</button>
        </div>
        <div style={{ flex: 1, display: "flex", justifyContent: "space-between", padding: 10 }}>
          <div style={{ width: 270, pointerEvents: "auto", background: "rgba(15,22,35,0.86)", border: "1px solid var(--border)", borderRadius: 8, padding: 12, alignSelf: "flex-start" }}>
            <input value={state.searchQuery} onChange={(event) => setState((prev) => prev && { ...prev, searchQuery: event.target.value })} placeholder="Search systems..." style={{ width: "100%", marginBottom: 8 }} />
            <select value={state.securityFilter} onChange={(event) => setState((prev) => prev && { ...prev, securityFilter: event.target.value as StarMapState["securityFilter"] })} style={{ width: "100%", marginBottom: 8 }}>
              <option value="all">All security</option><option value="highsec">High sec</option><option value="lowsec">Low sec</option><option value="nullsec">Null sec</option>
            </select>
            <div style={{ maxHeight: 420, overflow: "auto" }}>
              {filteredSystems.map((system) => (
                <div key={system.id} style={{ display: "grid", gridTemplateColumns: "1fr auto", gap: 6, padding: "6px 0", borderBottom: "1px solid rgba(255,255,255,0.05)" }}>
                  <button onClick={() => setState((prev) => prev && { ...prev, selectedSystemId: system.id })} style={{ textAlign: "left" }}>{system.name}</button>
                  <button onClick={() => setDestination(system)}>Set Destination</button>
                </div>
              ))}
            </div>
          </div>
          <div style={{ width: 300, pointerEvents: "auto", background: "rgba(15,22,35,0.86)", border: "1px solid var(--border)", borderRadius: 8, padding: 12, alignSelf: "flex-start" }}>
            <h3 style={{ marginTop: 0 }}>{selected?.name ?? "No System Selected"}</h3>
            {selected && (
              <>
                <p style={{ color: "var(--fg-dim)" }}>{selected.type} · sec {selected.security.toFixed(1)} · {selected.planetCount} planets · {selected.pointsOfInterest.length} POIs</p>
                <div style={{ marginBottom: 10 }}>
                  <strong style={{ display: "block", marginBottom: 4 }}>Planets</strong>
                  {selected.planets.length ? (
                    <div style={{ display: "grid", gap: 4 }}>
                      {selected.planets.map((planet) => (
                        <span key={planet.id} style={{ color: "var(--fg-dim)", fontSize: 12 }}>
                          {planet.name} · {planet.type}{planet.moons ? ` · ${planet.moons} moons` : ""}
                        </span>
                      ))}
                    </div>
                  ) : (
                    <span style={{ color: "var(--fg-dim)", fontSize: 12 }}>No surveyed planet records</span>
                  )}
                </div>
                <p>Stations: {selected.stations.length ? selected.stations.join(", ") : "None"}</p>
                <div style={{ marginBottom: 10 }}>
                  <strong style={{ display: "block", marginBottom: 4 }}>Points of Interest</strong>
                  <div style={{ display: "grid", gap: 4 }}>
                    {selected.pointsOfInterest.map((poi) => (
                      <button
                        key={poi.id}
                        onClick={() => setSelectedPoi({ systemId: selected.id, poi })}
                        style={{ textAlign: "left", color: selectedPoi?.poi.id === poi.id ? "#f0a030" : "var(--fg-dim)", fontSize: 12, padding: "3px 0", background: "transparent", border: 0 }}
                      >
                        {poi.name} · {poi.type.replace(/_/g, " ")}
                      </button>
                    ))}
                  </div>
                </div>
                {selectedPoi?.systemId === selected.id && (
                  <div style={{ marginBottom: 10, padding: 8, border: "1px solid rgba(240,160,48,0.35)", borderRadius: 6, background: "rgba(240,160,48,0.08)" }}>
                    <strong style={{ color: "#f0a030" }}>{selectedPoi.poi.name}</strong>
                    <p style={{ margin: "4px 0 8px", color: "var(--fg-dim)", fontSize: 12 }}>{selectedPoi.poi.description}</p>
                    <button onClick={() => navigateToPoi(selected, selectedPoi.poi)}>Navigate to POI</button>
                    {destinationPoi?.poiId === selectedPoi.poi.id && <button onClick={openMovement} disabled={!canOpenMovement} style={{ marginLeft: 8 }}>Open Movement</button>}
                  </div>
                )}
                <button onClick={() => setDestination(selected)}>Set Destination</button>
                <button onClick={() => addWaypoint(selected)} style={{ marginLeft: 8 }}>Add Waypoint</button>
              </>
            )}
          </div>
        </div>
        {sliceSession && (
          <div style={{ pointerEvents: "auto", margin: "10px 10px 0", alignSelf: "center", maxWidth: 880 }}>
            <SliceDemoLinks session={sliceSession} compact />
          </div>
        )}
        {state.route && (
          <div style={{ pointerEvents: "auto", margin: 10, padding: 10, background: "rgba(15,22,35,0.9)", border: "1px solid var(--border)", borderRadius: 8, display: "flex", gap: 8, overflowX: "auto" }}>
            {state.route.systems.map((system, index) => <span key={`${system.id}-${index}`} style={{ color: system.id === state.destinationSystemId ? "#22d3ee" : "#d4dce8" }}>{index > 0 ? "-> " : ""}{system.name}</span>)}
            {destinationPoi && <span style={{ color: "#f0a030" }}>-&gt; {destinationPoi.poiName}</span>}
          </div>
        )}
      </div>
    </div>
  );
}
