import type { AppModeEvent, AppModeState } from "./types";
import { initialAppModeState } from "./types";

/**
 * Pure transition function. Events that don't apply to the current mode are
 * no-ops (state is returned unchanged) rather than throwing — a screen that
 * fires a stale event (e.g. a debounced search result landing after the
 * user already backed out) shouldn't crash the app.
 */
export function appModeReducer(
  state: AppModeState,
  event: AppModeEvent
): AppModeState {
  switch (event.type) {
    case "RESET":
      return initialAppModeState;

    case "OPEN_SEARCH":
      if (state.mode !== "EXPLORE") return state;
      return { mode: "SEARCH", query: "" };

    case "UPDATE_SEARCH_QUERY":
      if (state.mode !== "SEARCH") return state;
      return { ...state, query: event.query };

    case "CANCEL_SEARCH":
      if (state.mode !== "SEARCH") return state;
      return { mode: "EXPLORE" };

    case "SELECT_PLACE":
      if (state.mode !== "SEARCH" && state.mode !== "EXPLORE") return state;
      return { mode: "PLACE", place: event.place };

    case "BACK":
      if (state.mode === "PLACE") return { mode: "EXPLORE" };
      if (state.mode === "PREVIEW") {
        return { mode: "PLACE", place: state.place };
      }
      return state;

    case "REQUEST_DIRECTIONS":
      if (state.mode !== "PLACE") return state;
      return {
        mode: "PREVIEW",
        place: state.place,
        profile: event.profile ?? "car",
        activeRouteIndex: 0,
        isComputing: true,
      };

    case "CHANGE_PROFILE":
      if (state.mode !== "PREVIEW") return state;
      return {
        ...state,
        profile: event.profile,
        isComputing: true,
        error: undefined,
      };

    case "ROUTE_COMPUTING":
      if (state.mode !== "PREVIEW") return state;
      return { ...state, isComputing: true, error: undefined };

    case "ROUTE_COMPUTED":
      if (state.mode !== "PREVIEW") return state;
      return {
        ...state,
        routeResult: event.result,
        activeRouteIndex: event.activeRouteIndex ?? 0,
        isComputing: false,
        error: undefined,
      };

    case "ROUTE_FAILED":
      if (state.mode !== "PREVIEW") return state;
      return { ...state, isComputing: false, error: event.error };

    case "SELECT_ROUTE":
      if (state.mode !== "PREVIEW") return state;
      return { ...state, activeRouteIndex: event.routeIndex };

    case "START": {
      if (state.mode !== "PREVIEW" || !state.routeResult) return state;
      const route =
        state.routeResult.routes.find((r) => r.index === state.activeRouteIndex) ??
        state.routeResult.routes[state.activeRouteIndex];
      return {
        mode: "GUIDANCE",
        place: state.place,
        profile: state.profile,
        routeIndex: state.activeRouteIndex,
        route,
        isRerouting: false,
        isFollowingCamera: true,
        isOverview: false,
      };
    }

    case "GUIDANCE_UPDATE":
      if (state.mode !== "GUIDANCE") return state;
      return {
        ...state,
        guidance: event.event,
        isRerouting: event.event.state === "rerouting",
      };

    case "REROUTE_COMPLETE":
      if (state.mode !== "GUIDANCE") return state;
      // Swap to the new route (index 0 of the fresh result), clear the rerouting
      // banner, and re-centre. useLiveGuidance re-subscribes against the new
      // route object, resetting its off-route/prepared state for free.
      return {
        ...state,
        route: event.route,
        routeIndex: 0,
        isRerouting: false,
        isFollowingCamera: true,
      };

    case "SET_FOLLOWING_CAMERA":
      if (state.mode !== "GUIDANCE") return state;
      return { ...state, isFollowingCamera: event.following };

    case "TOGGLE_OVERVIEW":
      if (state.mode !== "GUIDANCE") return state;
      return {
        ...state,
        isOverview: !state.isOverview,
        // Overview = fit-route north-up; leaving it restores chase/follow.
        isFollowingCamera: state.isOverview,
      };

    case "ARRIVE":
      if (state.mode !== "GUIDANCE") return state;
      return { mode: "ARRIVED", place: state.place, journey: event.journey };

    case "EXIT":
      if (state.mode !== "GUIDANCE") return state;
      return { mode: "EXPLORE" };

    case "DONE":
      if (state.mode !== "ARRIVED") return state;
      return { mode: "EXPLORE" };

    default: {
      const exhaustiveCheck: never = event;
      return exhaustiveCheck;
    }
  }
}
