import { describe, it, expect } from 'vitest';
import { AutomationSession } from '../../../../packages/domain/entities/AutomationSession';
import { StepId } from '../../../../packages/domain/value-objects/StepId';
import { SessionState } from '../../../../packages/domain/value-objects/SessionState';

describe('AutomationSession Entity', () => {
  describe('create', () => {
    it('should create a new session with PENDING state', () => {
      const config = {
        sessionName: 'Test Race Session',
        trackId: 'spa',
        carIds: ['dallara-f3'],
      };

      const session = AutomationSession.create(config);

      expect(session.id).toBeDefined();
      expect(session.currentStep.value).toBe(1);
      expect(session.state.isPending()).toBe(true);
      expect(session.config).toEqual(config);
    });

    it('should throw error for empty session name', () => {
      const config = {
        sessionName: '',
        trackId: 'spa',
        carIds: ['dallara-f3'],
      };

      expect(() => AutomationSession.create(config)).toThrow('Session name cannot be empty');
    });

    it('should throw error for missing track ID', () => {
      const config = {
        sessionName: 'Test Race',
        trackId: '',
        carIds: ['dallara-f3'],
      };

      expect(() => AutomationSession.create(config)).toThrow('Track ID is required');
    });

    it('should throw error for empty car list', () => {
      const config = {
        sessionName: 'Test Race',
        trackId: 'spa',
        carIds: [],
      };

      expect(() => AutomationSession.create(config)).toThrow('At least one car must be selected');
    });
  });

  describe('start', () => {
    it('should transition from PENDING to IN_PROGRESS', () => {
      const session = AutomationSession.create({
        sessionName: 'Test Race',
        trackId: 'spa',
        carIds: ['dallara-f3'],
      });

      session.start();

      expect(session.state.isInProgress()).toBe(true);
      expect(session.startedAt).toBeDefined();
    });

    it('should throw error when starting non-PENDING session', () => {
      const session = AutomationSession.create({
        sessionName: 'Test Race',
        trackId: 'spa',
        carIds: ['dallara-f3'],
      });
      session.start();

      expect(() => session.start()).toThrow('Cannot start session that is not pending');
    });
  });

  describe('transitionToStep', () => {
    it('should advance to next step when in progress', () => {
      const session = AutomationSession.create({
        sessionName: 'Test Race',
        trackId: 'spa',
        carIds: ['dallara-f3'],
      });
      session.start();

      session.transitionToStep(StepId.create(2));

      expect(session.currentStep.value).toBe(2);
    });

    it('should throw error when transitioning while not in progress', () => {
      const session = AutomationSession.create({
        sessionName: 'Test Race',
        trackId: 'spa',
        carIds: ['dallara-f3'],
      });

      expect(() => session.transitionToStep(StepId.create(2))).toThrow(
        'Cannot transition steps when session is not in progress'
      );
    });

    it('should throw error when skipping steps', () => {
      const session = AutomationSession.create({
        sessionName: 'Test Race',
        trackId: 'spa',
        carIds: ['dallara-f3'],
      });
      session.start();

      expect(() => session.transitionToStep(StepId.create(3))).toThrow(
        'Cannot skip steps - must transition sequentially'
      );
    });

    it('should throw error when moving backward', () => {
      const session = AutomationSession.create({
        sessionName: 'Test Race',
        trackId: 'spa',
        carIds: ['dallara-f3'],
      });
      session.start();
      session.transitionToStep(StepId.create(2));

      expect(() => session.transitionToStep(StepId.create(1))).toThrow(
        'Cannot move backward - steps must progress forward only'
      );
    });

    it('should stop at step 18 (safety checkpoint)', () => {
      const session = AutomationSession.create({
        sessionName: 'Test Race',
        trackId: 'spa',
        carIds: ['dallara-f3'],
      });
      session.start();

      // Advance through all steps to 18
      for (let i = 2; i <= 18; i++) {
        session.transitionToStep(StepId.create(i));
      }

      expect(session.currentStep.value).toBe(18);
      expect(session.state.isStoppedAtStep18()).toBe(true);
      expect(session.completedAt).toBeDefined();
    });
  });

  describe('pause', () => {
    it('should transition from IN_PROGRESS to PAUSED', () => {
      const session = AutomationSession.create({
        sessionName: 'Test Race',
        trackId: 'spa',
        carIds: ['dallara-f3'],
      });
      session.start();

      session.pause();

      expect(session.state.value).toBe('PAUSED');
    });

    it('should throw error when pausing non-in-progress session', () => {
      const session = AutomationSession.create({
        sessionName: 'Test Race',
        trackId: 'spa',
        carIds: ['dallara-f3'],
      });

      expect(() => session.pause()).toThrow('Cannot pause session that is not in progress');
    });
  });

  describe('resume', () => {
    it('should transition from PAUSED to IN_PROGRESS', () => {
      const session = AutomationSession.create({
        sessionName: 'Test Race',
        trackId: 'spa',
        carIds: ['dallara-f3'],
      });
      session.start();
      session.pause();

      session.resume();

      expect(session.state.isInProgress()).toBe(true);
    });

    it('should throw error when resuming non-paused session', () => {
      const session = AutomationSession.create({
        sessionName: 'Test Race',
        trackId: 'spa',
        carIds: ['dallara-f3'],
      });
      session.start();

      expect(() => session.resume()).toThrow('Cannot resume session that is not paused');
    });
  });

  describe('fail', () => {
    it('should transition to FAILED state with error message', () => {
      const session = AutomationSession.create({
        sessionName: 'Test Race',
        trackId: 'spa',
        carIds: ['dallara-f3'],
      });
      session.start();

      const errorMessage = 'Browser automation failed at step 5';
      session.fail(errorMessage);

      expect(session.state.isFailed()).toBe(true);
      expect(session.errorMessage).toBe(errorMessage);
      expect(session.completedAt).toBeDefined();
    });

    it('should allow failing from PENDING state', () => {
      const session = AutomationSession.create({
        sessionName: 'Test Race',
        trackId: 'spa',
        carIds: ['dallara-f3'],
      });

      session.fail('Initialization failed');

      expect(session.state.isFailed()).toBe(true);
    });

    it('should allow failing from PAUSED state', () => {
      const session = AutomationSession.create({
        sessionName: 'Test Race',
        trackId: 'spa',
        carIds: ['dallara-f3'],
      });
      session.start();
      session.pause();

      session.fail('Failed during pause');

      expect(session.state.isFailed()).toBe(true);
    });

    it('should throw error when failing already completed session', () => {
      const session = AutomationSession.create({
        sessionName: 'Test Race',
        trackId: 'spa',
        carIds: ['dallara-f3'],
      });
      session.start();
      
      // Advance to step 18
      for (let i = 2; i <= 18; i++) {
        session.transitionToStep(StepId.create(i));
      }

      expect(() => session.fail('Too late')).toThrow('Cannot fail terminal session');
    });
  });

  describe('isAtModalStep', () => {
    it('should return true when at step 6', () => {
      const session = AutomationSession.create({
        sessionName: 'Test Race',
        trackId: 'spa',
        carIds: ['dallara-f3'],
      });
      session.start();
      
      for (let i = 2; i <= 6; i++) {
        session.transitionToStep(StepId.create(i));
      }

      expect(session.isAtModalStep()).toBe(true);
    });

    it('should return true when at step 9', () => {
      const session = AutomationSession.create({
        sessionName: 'Test Race',
        trackId: 'spa',
        carIds: ['dallara-f3'],
      });
      session.start();
      
      for (let i = 2; i <= 9; i++) {
        session.transitionToStep(StepId.create(i));
      }

      expect(session.isAtModalStep()).toBe(true);
    });

    it('should return true when at step 12', () => {
      const session = AutomationSession.create({
        sessionName: 'Test Race',
        trackId: 'spa',
        carIds: ['dallara-f3'],
      });
      session.start();
      
      for (let i = 2; i <= 12; i++) {
        session.transitionToStep(StepId.create(i));
      }

      expect(session.isAtModalStep()).toBe(true);
    });

    it('should return false when at non-modal step', () => {
      const session = AutomationSession.create({
        sessionName: 'Test Race',
        trackId: 'spa',
        carIds: ['dallara-f3'],
      });
      session.start();

      expect(session.isAtModalStep()).toBe(false);
    });
  });

  describe('getElapsedTime', () => {
    it('should return 0 for non-started session', () => {
      const session = AutomationSession.create({
        sessionName: 'Test Race',
        trackId: 'spa',
        carIds: ['dallara-f3'],
      });

      expect(session.getElapsedTime()).toBe(0);
    });

    it('should return elapsed milliseconds for in-progress session', () => {
      const session = AutomationSession.create({
        sessionName: 'Test Race',
        trackId: 'spa',
        carIds: ['dallara-f3'],
      });
      session.start();

      // Wait a bit (in real test, this would be mocked)
      const elapsed = session.getElapsedTime();
      expect(elapsed).toBeGreaterThan(0);
    });

    it('should return total duration for completed session', () => {
      const session = AutomationSession.create({
        sessionName: 'Test Race',
        trackId: 'spa',
        carIds: ['dallara-f3'],
      });
      session.start();
      
      // Advance to step 18
      for (let i = 2; i <= 18; i++) {
        session.transitionToStep(StepId.create(i));
      }

      const elapsed = session.getElapsedTime();
      expect(elapsed).toBeGreaterThan(0);
      expect(session.state.isStoppedAtStep18()).toBe(true);
    });
  });
});