import { describe, it, expect, vi, beforeEach } from 'vitest';
import { CompleteRaceCreationUseCase } from 'apps/companion/main/automation/application/use-cases/CompleteRaceCreationUseCase';
import { Result } from '@core/shared/application/Result';
import { RaceCreationResult } from 'apps/companion/main/automation/domain/value-objects/RaceCreationResult';
import { CheckoutPrice } from 'apps/companion/main/automation/domain/value-objects/CheckoutPrice';
import type { CheckoutServicePort } from 'apps/companion/main/automation/application/ports/CheckoutServicePort';
import { CheckoutState } from 'apps/companion/main/automation/domain/value-objects/CheckoutState';

describe('CompleteRaceCreationUseCase', () => {
  let mockCheckoutService: CheckoutServicePort;
  let useCase: CompleteRaceCreationUseCase;

  beforeEach(() => {
    mockCheckoutService = {
      extractCheckoutInfo: vi.fn(),
      proceedWithCheckout: vi.fn(),
    };

    useCase = new CompleteRaceCreationUseCase(mockCheckoutService);
  });

  describe('execute', () => {
    it('should extract checkout price and create RaceCreationResult', async () => {
      const price = CheckoutPrice.fromString('$25.50');
      const state = CheckoutState.ready();
      const sessionId = 'test-session-123';
 
      vi.mocked(mockCheckoutService.extractCheckoutInfo).mockResolvedValue(
        Result.ok({ price, state, buttonHtml: '<a>$25.50</a>' })
      );

      const result = await useCase.execute(sessionId);

      expect(mockCheckoutService.extractCheckoutInfo).toHaveBeenCalled();
      expect(result.isOk()).toBe(true);

      const raceCreationResult = result.unwrap();
      expect(raceCreationResult).toBeInstanceOf(RaceCreationResult);
      expect(raceCreationResult.sessionId).toBe(sessionId);
      expect(raceCreationResult.price).toBe('$25.50');
      expect(raceCreationResult.timestamp).toBeInstanceOf(Date);
    });

    it('should return error if checkout info extraction fails', async () => {
      vi.mocked(mockCheckoutService.extractCheckoutInfo).mockResolvedValue(
        Result.err(new Error('Failed to extract checkout info'))
      );

      const result = await useCase.execute('test-session-123');

      expect(result.isErr()).toBe(true);
      expect(result.unwrapErr().message).toContain('Failed to extract checkout info');
    });

    it('should return error if price is missing', async () => {
      const state = CheckoutState.ready();
 
      vi.mocked(mockCheckoutService.extractCheckoutInfo).mockResolvedValue(
        Result.ok({ price: null, state, buttonHtml: '<a>n/a</a>' })
      );

      const result = await useCase.execute('test-session-123');

      expect(result.isErr()).toBe(true);
      expect(result.unwrapErr().message).toContain('Could not extract price');
    });

    it('should validate session ID is provided', async () => {
      const result = await useCase.execute('');

      expect(result.isErr()).toBe(true);
      expect(result.unwrapErr().message).toContain('Session ID is required');
    });

    it('should format different price values correctly', async () => {
      const testCases = [
        { input: '$10.00', expected: '$10.00' },
        { input: '$100.50', expected: '$100.50' },
        { input: '$0.99', expected: '$0.99' },
      ];
 
      for (const testCase of testCases) {
        const price = CheckoutPrice.fromString(testCase.input);
        const state = CheckoutState.ready();
 
        vi.mocked(mockCheckoutService.extractCheckoutInfo).mockResolvedValue(
          Result.ok({ price, state, buttonHtml: `<a>${testCase.input}</a>` })
        );

        const result = await useCase.execute('test-session');
        expect(result.isOk()).toBe(true);
        
        const raceCreationResult = result.unwrap();
        expect(raceCreationResult.price).toBe(testCase.expected);
      }
    });

    it('should capture current timestamp when creating result', async () => {
      const price = CheckoutPrice.fromString('$25.50');
      const state = CheckoutState.ready();
      const beforeExecution = new Date();
 
      vi.mocked(mockCheckoutService.extractCheckoutInfo).mockResolvedValue(
        Result.ok({ price, state, buttonHtml: '<a>$25.50</a>' })
      );

      const result = await useCase.execute('test-session');
      const afterExecution = new Date();

      expect(result.isOk()).toBe(true);
      const raceCreationResult = result.unwrap();
      
      expect(raceCreationResult.timestamp.getTime()).toBeGreaterThanOrEqual(
        beforeExecution.getTime()
      );
      expect(raceCreationResult.timestamp.getTime()).toBeLessThanOrEqual(
        afterExecution.getTime()
      );
    });
  });
});