import type { MotionDiagnostic } from "../kdl/types.js";

export type VirtualControllerState =
  | "unloaded"
  | "stopped"
  | "running"
  | "paused"
  | "hold"
  | "faulted";

export type VirtualControllerCommand =
  | "load"
  | "run"
  | "pause"
  | "stop"
  | "reset"
  | "step"
  | "hold";

export interface StateTransitionResult {
  ok: boolean;
  previous: VirtualControllerState;
  state: VirtualControllerState;
  command: VirtualControllerCommand;
  diagnostic?: MotionDiagnostic;
}

export interface VirtualControllerStateMachineSnapshot {
  state: VirtualControllerState;
  diagnostics: MotionDiagnostic[];
}

const TRANSITIONS: Record<
  VirtualControllerCommand,
  Partial<Record<VirtualControllerState, VirtualControllerState>>
> = {
  load: {
    unloaded: "stopped",
    stopped: "stopped",
    faulted: "stopped"
  },
  run: {
    stopped: "running",
    paused: "running",
    hold: "running"
  },
  pause: {
    running: "paused"
  },
  stop: {
    running: "stopped",
    paused: "stopped",
    hold: "stopped",
    faulted: "stopped"
  },
  reset: {
    stopped: "stopped",
    paused: "stopped",
    hold: "stopped",
    faulted: "stopped"
  },
  step: {
    stopped: "paused",
    paused: "paused"
  },
  hold: {
    running: "hold",
    paused: "hold"
  }
};

export class VirtualControllerStateMachine {
  private stateValue: VirtualControllerState;
  private readonly diagnosticsValue: MotionDiagnostic[] = [];

  constructor(initialState: VirtualControllerState = "unloaded") {
    this.stateValue = initialState;
  }

  get state(): VirtualControllerState {
    return this.stateValue;
  }

  get diagnostics(): MotionDiagnostic[] {
    return [...this.diagnosticsValue];
  }

  dispatch(command: VirtualControllerCommand): StateTransitionResult {
    const previous = this.stateValue;
    const next = TRANSITIONS[command][previous];
    if (!next) {
      const diagnostic: MotionDiagnostic = {
        severity: "warning",
        code: "VC_INVALID_STATE_TRANSITION",
        message: `Cannot ${command} while controller is ${previous}`,
        data: { command, state: previous }
      };
      this.diagnosticsValue.push(diagnostic);
      return {
        ok: false,
        previous,
        state: previous,
        command,
        diagnostic
      };
    }

    this.stateValue = next;
    return {
      ok: true,
      previous,
      state: next,
      command
    };
  }

  fault(message: string, code = "VC_CONTROLLER_FAULT"): StateTransitionResult {
    const previous = this.stateValue;
    const diagnostic: MotionDiagnostic = {
      severity: "error",
      code,
      message
    };
    this.diagnosticsValue.push(diagnostic);
    this.stateValue = "faulted";
    return {
      ok: true,
      previous,
      state: "faulted",
      command: "stop",
      diagnostic
    };
  }

  resetDiagnostics(): void {
    this.diagnosticsValue.length = 0;
  }

  snapshot(): VirtualControllerStateMachineSnapshot {
    return {
      state: this.stateValue,
      diagnostics: this.diagnostics
    };
  }
}
