import { randomUUID } from 'crypto';
import { StepId } from '../value-objects/StepId';
import { SessionState } from '../value-objects/SessionState';
import { HostedSessionConfig } from './HostedSessionConfig';

export class AutomationSession {
  private readonly _id: string;
  private _currentStep: StepId;
  private _state: SessionState;
  private readonly _config: HostedSessionConfig;
  private _startedAt?: Date;
  private _completedAt?: Date;
  private _errorMessage?: string;

  private constructor(
    id: string,
    currentStep: StepId,
    state: SessionState,
    config: HostedSessionConfig
  ) {
    this._id = id;
    this._currentStep = currentStep;
    this._state = state;
    this._config = config;
  }

  static create(config: HostedSessionConfig): AutomationSession {
    if (!config.sessionName || config.sessionName.trim() === '') {
      throw new Error('Session name cannot be empty');
    }
    if (!config.trackId || config.trackId.trim() === '') {
      throw new Error('Track ID is required');
    }
    if (!config.carIds || config.carIds.length === 0) {
      throw new Error('At least one car must be selected');
    }

    return new AutomationSession(
      randomUUID(),
      StepId.create(1),
      SessionState.create('PENDING'),
      config
    );
  }

  get id(): string {
    return this._id;
  }

  get currentStep(): StepId {
    return this._currentStep;
  }

  get state(): SessionState {
    return this._state;
  }

  get config(): HostedSessionConfig {
    return this._config;
  }

  get startedAt(): Date | undefined {
    return this._startedAt;
  }

  get completedAt(): Date | undefined {
    return this._completedAt;
  }

  get errorMessage(): string | undefined {
    return this._errorMessage;
  }

  start(): void {
    if (!this._state.isPending()) {
      throw new Error('Cannot start session that is not pending');
    }
    this._state = SessionState.create('IN_PROGRESS');
    this._startedAt = new Date();
  }

  transitionToStep(targetStep: StepId): void {
    if (!this._state.isInProgress()) {
      throw new Error('Cannot transition steps when session is not in progress');
    }

    if (this._currentStep.equals(targetStep)) {
      throw new Error('Already at this step');
    }

    if (targetStep.value < this._currentStep.value) {
      throw new Error('Cannot move backward - steps must progress forward only');
    }

    if (targetStep.value !== this._currentStep.value + 1) {
      throw new Error('Cannot skip steps - must transition sequentially');
    }

    this._currentStep = targetStep;

    if (this._currentStep.isFinalStep()) {
      this._state = SessionState.create('STOPPED_AT_STEP_18');
      this._completedAt = new Date();
    }
  }

  pause(): void {
    if (!this._state.isInProgress()) {
      throw new Error('Cannot pause session that is not in progress');
    }
    this._state = SessionState.create('PAUSED');
  }

  resume(): void {
    if (this._state.value !== 'PAUSED') {
      throw new Error('Cannot resume session that is not paused');
    }
    this._state = SessionState.create('IN_PROGRESS');
  }

  fail(errorMessage: string): void {
    if (this._state.isTerminal()) {
      throw new Error('Cannot fail terminal session');
    }
    this._state = SessionState.create('FAILED');
    this._errorMessage = errorMessage;
    this._completedAt = new Date();
  }

  isAtModalStep(): boolean {
    return this._currentStep.isModalStep();
  }

  getElapsedTime(): number {
    if (!this._startedAt) {
      return 0;
    }

    const endTime = this._completedAt || new Date();
    const elapsed = endTime.getTime() - this._startedAt.getTime();
    return elapsed > 0 ? elapsed : 1;
  }
}