/**
 * Serializable contracts for moving one actor through Tera.
 *
 * Outdoors is one California. Bay Area and Southern California are places on
 * that board — `?city=sf` is a capture renderer, not a player-facing scale.
 * The other scale is an office: you are inside a building, and leaving it
 * returns you to California, not to a metro board the product no longer is.
 */

export type JourneyScale = "california" | "office";
/** Which metro an office or a route end sits in. Geography, not a journey scale. */
export type JourneyCity = "bay-area" | "socal";
export type JourneyMode = "observe" | "play";
export type JourneyActorKind = "humanoid" | "dog" | "crow";
export type JourneyRouteId = "la-sf-us-101" | "la-sf-i-5";
export type JourneyRouteEndpoint = "los-angeles" | "san-francisco";

export interface JourneyActorProfile {
  displayName: string;
  /** Optional URL or application-owned asset key; never interpreted by the reducer. */
  face?: string;
  color?: string;
}

export interface JourneyActor {
  id: string;
  kind: JourneyActorKind;
  signedIn: boolean;
  profile: JourneyActorProfile;
}

export type JourneyLocation =
  | { scale: "california" }
  | {
      scale: "office";
      officeId: JourneyOfficeId;
    };

export interface JourneyRouteProgress {
  routeId: JourneyRouteId;
  /** 1 follows the authored LA-to-SF route; -1 travels back toward LA. */
  direction: 1 | -1;
  /** Normalized distance measured from Los Angeles, regardless of direction. */
  progress: number;
}

export interface JourneyVehiclePossession {
  vehicleId: string;
}

export interface JourneyState {
  actor: JourneyActor;
  location: JourneyLocation;
  mode: JourneyMode;
  vehicle: JourneyVehiclePossession | null;
  route: JourneyRouteProgress | null;
}

export type JourneyOfficeId = "lumbridge-hq" | "frontier-valley" | "mateo-court";

export type JourneyEvent =
  | { type: "set-mode"; mode: JourneyMode }
  /**
   * Leftover of the three-board product. Outdoors is already California, so
   * both events reject with `already-there` (or `leave-office-first`). The
   * renderer still honours `?city=` via `switchCity`; that is not a journey.
   */
  | { type: "navigate-to-city"; city: JourneyCity }
  | { type: "return-to-california" }
  | { type: "select-route"; routeId: JourneyRouteId; direction: 1 | -1 }
  | { type: "update-route-progress"; progress: number }
  | { type: "reach-route-endpoint"; endpoint: JourneyRouteEndpoint }
  | { type: "enter-vehicle"; vehicleId: string }
  | { type: "exit-vehicle" }
  | { type: "enter-office"; officeId: JourneyOfficeId }
  | { type: "leave-office" }
  | { type: "sign-in-actor-swap"; actor: JourneyActor };

export type JourneyTransitionRejection =
  | "invalid-state"
  | "invalid-event"
  | "wrong-scale"
  | "wrong-city"
  | "wrong-endpoint"
  | "observe-only"
  | "route-required"
  | "vehicle-required"
  | "vehicle-occupied"
  | "exit-vehicle-first"
  | "leave-office-first"
  | "already-there"
  | "already-outside";

export type JourneyTransition =
  | { accepted: true; state: JourneyState }
  | { accepted: false; state: JourneyState; reason: JourneyTransitionRejection };

export interface JourneySnapshotV1 {
  version: 1;
  state: JourneyState;
}

export type JourneySnapshotDecodeResult =
  | { ok: true; snapshot: JourneySnapshotV1; state: JourneyState }
  | { ok: false; error: string };

/** Smallest common contract implemented by localStorage, sessionStorage, and server adapters. */
export interface JourneyStorageAdapter {
  getItem(key: string): string | null;
  setItem(key: string, value: string): void;
  removeItem(key: string): void;
}

export type JourneySessionSaveResult = { ok: true } | { ok: false; error: string };

export type JourneySessionLoadResult =
  | { status: "loaded"; state: JourneyState }
  | { status: "missing" }
  | { status: "invalid"; error: string }
  | { status: "unavailable"; error: string };

export type JourneySessionClearResult = { ok: true } | { ok: false; error: string };
