import { useCallback, useMemo, useReducer } from "react";
import {
  POSITIONS,
  SCENARIOS,
  type Scenario,
  type V2Position,
} from "../data/simulation";
import {
  buildMigrationTypedData,
  simulatedSignature,
  typedDataDigest,
  type MigrationTypedData,
} from "../lib/eip712";

export const STEPS = [
  { id: "landing", label: "Overview" },
  { id: "compare", label: "Compare" },
  { id: "choose", label: "Choose" },
  { id: "review", label: "Review payload" },
  { id: "sign", label: "Sign (simulated)" },
  { id: "confirm", label: "Confirmation" },
] as const;

export type StepId = (typeof STEPS)[number]["id"];

export type MigrationState = {
  step: StepId;
  scenarioId: Scenario["id"];
  /** Opt-in set. Empty by default: every V2 position keeps its V2 state. */
  selected: string[];
  signed: boolean;
  /** Incremented only by the simulated signing action; proves no side effects. */
  simulatedSignCount: number;
};

export const initialState: MigrationState = {
  step: "landing",
  scenarioId: "nominal",
  selected: [],
  signed: false,
  simulatedSignCount: 0,
};

type Action =
  | { type: "goto"; step: StepId }
  | { type: "toggle"; id: string }
  | { type: "keepAll" }
  | { type: "setScenario"; scenarioId: Scenario["id"] }
  | { type: "simulateSign" }
  | { type: "restart" };

export function reducer(state: MigrationState, action: Action): MigrationState {
  switch (action.type) {
    case "goto":
      return { ...state, step: action.step };
    case "toggle": {
      const position = POSITIONS.find((p) => p.id === action.id);
      // Positions with no V3 equivalent can never be opted in.
      if (!position || position.v3 === null) return state;
      const on = state.selected.includes(action.id);
      return {
        ...state,
        signed: false,
        selected: on
          ? state.selected.filter((s) => s !== action.id)
          : [...state.selected, action.id],
      };
    }
    case "keepAll":
      return { ...state, selected: [], signed: false };
    case "setScenario": {
      // Changing the data-source scenario always clears any prior intent.
      const nowBlocked = SCENARIOS[action.scenarioId].solvency.dataSourceErrors.length > 0;
      const unsafe: StepId[] = ["review", "sign", "confirm"];
      return {
        ...state,
        scenarioId: action.scenarioId,
        selected: [],
        signed: false,
        // A failing feed must not leave the user on a payload/sign/result screen.
        step: nowBlocked && unsafe.includes(state.step) ? "choose" : state.step,
      };
    }
    case "simulateSign":
      return {
        ...state,
        signed: true,
        step: "confirm",
        simulatedSignCount: state.simulatedSignCount + 1,
      };
    case "restart":
      return { ...initialState, scenarioId: state.scenarioId };
    default:
      return state;
  }
}

export type MigrationController = {
  state: MigrationState;
  scenario: Scenario;
  positions: V2Position[];
  selectedPositions: V2Position[];
  keptPositions: V2Position[];
  blocked: boolean;
  typedData: MigrationTypedData;
  digest: string;
  signature: string | null;
  goto: (step: StepId) => void;
  next: () => void;
  back: () => void;
  toggle: (id: string) => void;
  keepAll: () => void;
  setScenario: (id: Scenario["id"]) => void;
  simulateSign: () => void;
  restart: () => void;
};

export function useMigration(
  overrides?: Partial<MigrationState>,
): MigrationController {
  const [state, dispatch] = useReducer(reducer, {
    ...initialState,
    ...overrides,
  });

  const scenario = SCENARIOS[state.scenarioId];
  const blocked = scenario.solvency.dataSourceErrors.length > 0;

  const selectedPositions = useMemo(
    () => POSITIONS.filter((p) => state.selected.includes(p.id)),
    [state.selected],
  );
  const keptPositions = useMemo(
    () => POSITIONS.filter((p) => !state.selected.includes(p.id)),
    [state.selected],
  );

  const typedData = useMemo(
    () => buildMigrationTypedData(blocked ? [] : selectedPositions),
    [selectedPositions, blocked],
  );
  const digest = useMemo(() => typedDataDigest(typedData), [typedData]);
  const signature = state.signed ? simulatedSignature(digest) : null;

  const idx = STEPS.findIndex((s) => s.id === state.step);

  const goto = useCallback((step: StepId) => dispatch({ type: "goto", step }), []);

  return {
    state,
    scenario,
    positions: POSITIONS,
    selectedPositions,
    keptPositions,
    blocked,
    typedData,
    digest,
    signature,
    goto,
    next: () => {
      const target = STEPS[Math.min(idx + 1, STEPS.length - 1)];
      if (target.id === "confirm") return; // only reachable via simulated signing
      dispatch({ type: "goto", step: target.id });
    },
    back: () => dispatch({ type: "goto", step: STEPS[Math.max(idx - 1, 0)].id }),
    toggle: (id: string) => dispatch({ type: "toggle", id }),
    keepAll: () => dispatch({ type: "keepAll" }),
    setScenario: (scenarioId: Scenario["id"]) =>
      dispatch({ type: "setScenario", scenarioId }),
    simulateSign: () => dispatch({ type: "simulateSign" }),
    restart: () => dispatch({ type: "restart" }),
  };
}
