import type { NavigationRoute, PoiNavigationTarget } from "../shared/types";

export type TravelMode =
  | "local_impulse"
  | "star_map_planning"
  | "jumping"
  | "warp_bubble"
  | "star_map_tracking"
  | "arrived";

export type InterstellarTravelStatus = "idle" | "planned" | "in_transit" | "arrived";

export type TravelSession = {
  id: string;
  currentSystemId: string;
  destinationSystemId: string | null;
  waypointSystemIds: string[];
  route: NavigationRoute | null;
  status: InterstellarTravelStatus;
  activeSegmentIndex: number;
  segmentProgress: number;
  totalProgress: number;
  mode: TravelMode;
  arrivalMessage: string | null;
  destinationPoi: PoiNavigationTarget | null;
  updatedAt: number;
};

type CreateTravelSessionArgs = {
  id?: string;
  currentSystemId?: string;
  destinationSystemId?: string | null;
  waypointSystemIds?: string[];
  route?: NavigationRoute | null;
  status?: InterstellarTravelStatus;
  mode?: TravelMode;
  destinationPoi?: PoiNavigationTarget | null;
};

const STORAGE_PREFIX = "void-nav.travelSession.";
const SEGMENT_TRAVEL_RATE = 0.34;

const clamp01 = (value: number) => Math.max(0, Math.min(1, value));

const storageKey = (id: string) => `${STORAGE_PREFIX}${id}`;

const safeLocalStorage = () => {
  if (typeof window === "undefined") return null;
  try {
    return window.localStorage;
  } catch {
    return null;
  }
};

const withUpdatedAt = (session: TravelSession): TravelSession => ({ ...session, updatedAt: Date.now() });

export function createTravelSessionId() {
  if (typeof crypto !== "undefined" && "randomUUID" in crypto) return crypto.randomUUID();
  return `nav-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
}

export function createTravelSession({
  id = createTravelSessionId(),
  currentSystemId = "sol",
  destinationSystemId = null,
  waypointSystemIds = [],
  route = null,
  status = route ? "planned" : "idle",
  mode = "local_impulse",
  destinationPoi = null,
}: CreateTravelSessionArgs = {}): TravelSession {
  return {
    id,
    currentSystemId,
    destinationSystemId,
    waypointSystemIds,
    route,
    status,
    activeSegmentIndex: 0,
    segmentProgress: 0,
    totalProgress: 0,
    mode,
    arrivalMessage: null,
    destinationPoi,
    updatedAt: Date.now(),
  };
}

export function loadTravelSession(id: string): TravelSession | null {
  const storage = safeLocalStorage();
  if (!storage) return null;
  try {
    const raw = storage.getItem(storageKey(id));
    if (!raw) return null;
    const parsed = JSON.parse(raw) as TravelSession;
    if (!parsed || parsed.id !== id || !parsed.currentSystemId) return null;
    return {
      ...parsed,
      destinationSystemId: parsed.destinationSystemId ?? null,
      waypointSystemIds: parsed.waypointSystemIds ?? [],
      route: parsed.route ?? null,
      activeSegmentIndex: parsed.activeSegmentIndex ?? 0,
      segmentProgress: clamp01(parsed.segmentProgress ?? 0),
      totalProgress: clamp01(parsed.totalProgress ?? 0),
      arrivalMessage: parsed.arrivalMessage ?? null,
      destinationPoi: parsed.destinationPoi ?? null,
      updatedAt: parsed.updatedAt ?? Date.now(),
    };
  } catch {
    return null;
  }
}

export function saveTravelSession(session: TravelSession) {
  const storage = safeLocalStorage();
  if (!storage) return;
  storage.setItem(storageKey(session.id), JSON.stringify(withUpdatedAt(session)));
}

export function clearTravelSession(id: string) {
  const storage = safeLocalStorage();
  storage?.removeItem(storageKey(id));
}

export function getTravelSessionIdFromUrl() {
  if (typeof window === "undefined") return null;
  return new URLSearchParams(window.location.search).get("navSession");
}

export function withTravelSession(url: string, id: string) {
  const base = typeof window === "undefined" ? "http://void-nav.local" : window.location.origin;
  const next = new URL(url, base);
  next.searchParams.set("navSession", id);
  return `${next.pathname}${next.search}${next.hash}`;
}

export function completeTravelSession(session: TravelSession): TravelSession {
  const destination = session.route?.destination;
  return withUpdatedAt({
    ...session,
    currentSystemId: destination?.id ?? session.currentSystemId,
    destinationSystemId: null,
    waypointSystemIds: [],
    status: "arrived",
    mode: "arrived",
    activeSegmentIndex: 0,
    segmentProgress: 1,
    totalProgress: 1,
    arrivalMessage: destination ? `Arrived in ${destination.name}` : "Arrived",
  });
}

export function advanceTravelSession(session: TravelSession, dt: number): TravelSession {
  if (session.status !== "in_transit" || !session.route) return session;
  if (session.route.segments.length === 0) return completeTravelSession(session);

  const segmentCount = session.route.segments.length;
  const activeSegmentIndex = Math.min(session.activeSegmentIndex, segmentCount - 1);
  let nextSegmentIndex = activeSegmentIndex;
  let nextSegmentProgress = clamp01(session.segmentProgress + dt * SEGMENT_TRAVEL_RATE);

  if (nextSegmentProgress >= 1) {
    if (activeSegmentIndex >= segmentCount - 1) return completeTravelSession(session);
    nextSegmentIndex = activeSegmentIndex + 1;
    nextSegmentProgress = 0;
  }

  const totalProgress = clamp01((nextSegmentIndex + nextSegmentProgress) / segmentCount);
  return withUpdatedAt({
    ...session,
    activeSegmentIndex: nextSegmentIndex,
    segmentProgress: nextSegmentProgress,
    totalProgress,
    currentSystemId: session.route.systems[nextSegmentIndex]?.id ?? session.currentSystemId,
  });
}

export function planRouteForSession(
  session: TravelSession,
  route: NavigationRoute | null,
  destinationSystemId: string | null,
  waypointSystemIds: string[] = session.waypointSystemIds,
): TravelSession {
  if (route && route.segments.length === 0) {
    return completeTravelSession({
      ...session,
      route,
      destinationSystemId,
      waypointSystemIds,
      destinationPoi: session.destinationPoi,
    });
  }

  return withUpdatedAt({
    ...session,
    destinationSystemId,
    waypointSystemIds,
    route,
    status: route ? "planned" : "idle",
    activeSegmentIndex: 0,
    segmentProgress: 0,
    totalProgress: 0,
    mode: "star_map_planning",
    arrivalMessage: null,
  });
}

export function startJump(session: TravelSession): TravelSession {
  if (!session.route) return session;
  if (session.route.segments.length === 0) return completeTravelSession(session);
  return withUpdatedAt({
    ...session,
    status: "in_transit",
    mode: "jumping",
    activeSegmentIndex: Math.min(session.activeSegmentIndex, session.route.segments.length - 1),
    segmentProgress: clamp01(session.segmentProgress),
    totalProgress: clamp01(session.totalProgress),
    arrivalMessage: null,
  });
}

export function setTravelMode(session: TravelSession, mode: TravelMode): TravelSession {
  return withUpdatedAt({ ...session, mode });
}
