import { describe, it, expect, vi, beforeEach } from 'vitest';
import type { BrowserWindow } from 'electron';

// Mock electron module with factory function
vi.mock('electron', () => ({
  ipcMain: {
    on: vi.fn(),
    removeAllListeners: vi.fn(),
  },
}));

import { ElectronCheckoutConfirmationAdapter } from '@core/automation/infrastructure//ipc/ElectronCheckoutConfirmationAdapter';
import { CheckoutPrice } from 'apps/companion/main/automation/domain/value-objects/CheckoutPrice';
import { CheckoutState } from 'apps/companion/main/automation/domain/value-objects/CheckoutState';
import { ipcMain } from 'electron';

describe('ElectronCheckoutConfirmationAdapter', () => {
  let mockWindow: BrowserWindow;
  let adapter: ElectronCheckoutConfirmationAdapter;
  type IpcEventLike = { sender?: unknown };
  let ipcMainOnCallback: ((event: IpcEventLike, decision: 'confirmed' | 'cancelled' | 'timeout') => void) | null = null;

  beforeEach(() => {
    vi.clearAllMocks();
    ipcMainOnCallback = null;
    
    // Capture the IPC handler callback
    vi.mocked(ipcMain.on).mockImplementation((channel, callback) => {
      if (channel === 'checkout:confirm') {
        ipcMainOnCallback = callback as (event: IpcEventLike, decision: 'confirmed' | 'cancelled' | 'timeout') => void;
      }
      return ipcMain;
    });

    mockWindow = {
      webContents: {
        send: vi.fn(),
      },
    } as unknown as BrowserWindow;

    adapter = new ElectronCheckoutConfirmationAdapter(mockWindow);
  });

  describe('requestCheckoutConfirmation', () => {
    it('should send IPC message to renderer with request details', async () => {
      const request = {
        price: CheckoutPrice.fromString('$25.50'),
        state: CheckoutState.ready(),
        sessionMetadata: {
          sessionName: 'Test Race',
          trackId: 'spa',
          carIds: ['car1', 'car2'],
        },
        timeoutMs: 30000,
      };

      // Simulate immediate confirmation via IPC
      setTimeout(() => {
        if (ipcMainOnCallback) {
          ipcMainOnCallback({} as IpcEventLike, 'confirmed');
        }
      }, 10);

      const result = await adapter.requestCheckoutConfirmation(request);

      expect(mockWindow.webContents.send).toHaveBeenCalledWith(
        'checkout:request-confirmation',
        expect.objectContaining({
          price: '$25.50',
          sessionMetadata: request.sessionMetadata,
          timeoutMs: 30000,
        })
      );

      expect(result.isOk()).toBe(true);
      const confirmation = result.unwrap();
      expect(confirmation.isConfirmed()).toBe(true);
    });

    it('should handle user confirmation', async () => {
      const request = {
        price: CheckoutPrice.fromString('$10.00'),
        state: CheckoutState.ready(),
        sessionMetadata: {
          sessionName: 'Test',
          trackId: 'spa',
          carIds: ['car1'],
        },
        timeoutMs: 30000,
      };

      setTimeout(() => {
        if (ipcMainOnCallback) {
          ipcMainOnCallback({} as IpcEventLike, 'confirmed');
        }
      }, 10);

      const result = await adapter.requestCheckoutConfirmation(request);

      expect(result.isOk()).toBe(true);
      const confirmation = result.unwrap();
      expect(confirmation.isConfirmed()).toBe(true);
    });

    it('should handle user cancellation', async () => {
      const request = {
        price: CheckoutPrice.fromString('$10.00'),
        state: CheckoutState.ready(),
        sessionMetadata: {
          sessionName: 'Test',
          trackId: 'spa',
          carIds: ['car1'],
        },
        timeoutMs: 30000,
      };

      setTimeout(() => {
        if (ipcMainOnCallback) {
          ipcMainOnCallback({} as IpcEventLike, 'cancelled');
        }
      }, 10);

      const result = await adapter.requestCheckoutConfirmation(request);

      expect(result.isOk()).toBe(true);
      const confirmation = result.unwrap();
      expect(confirmation.isCancelled()).toBe(true);
    });

    it('should timeout when no response received', async () => {
      const request = {
        price: CheckoutPrice.fromString('$10.00'),
        state: CheckoutState.ready(),
        sessionMetadata: {
          sessionName: 'Test',
          trackId: 'spa',
          carIds: ['car1'],
        },
        timeoutMs: 100,
      };

      const result = await adapter.requestCheckoutConfirmation(request);

      expect(result.isOk()).toBe(true);
      const confirmation = result.unwrap();
      expect(confirmation.isTimeout()).toBe(true);
    });

    it('should reject when already pending', async () => {
      const request = {
        price: CheckoutPrice.fromString('$10.00'),
        state: CheckoutState.ready(),
        sessionMetadata: {
          sessionName: 'Test',
          trackId: 'spa',
          carIds: ['car1'],
        },
        timeoutMs: 30000,
      };

      // Start first request
      const promise1 = adapter.requestCheckoutConfirmation(request);

      // Try to start second request immediately (should fail)
      const result2 = await adapter.requestCheckoutConfirmation(request);
      
      expect(result2.isErr()).toBe(true);
      expect(result2.unwrapErr().message).toContain('already pending');

      // Confirm first request to clean up
      if (ipcMainOnCallback) {
        ipcMainOnCallback({} as IpcEventLike, 'confirmed');
      }
      
      await promise1;
    });

    it('should send correct state to renderer', async () => {
      const request = {
        price: CheckoutPrice.fromString('$10.00'),
        state: CheckoutState.ready(),
        sessionMetadata: {
          sessionName: 'Test',
          trackId: 'spa',
          carIds: ['car1'],
        },
        timeoutMs: 100,
      };

      await adapter.requestCheckoutConfirmation(request);

      expect(mockWindow.webContents.send).toHaveBeenCalledWith(
        'checkout:request-confirmation',
        expect.objectContaining({
          state: 'ready',
        })
      );
    });

    it('should handle insufficient funds state', async () => {
      const request = {
        price: CheckoutPrice.fromString('$10.00'),
        state: CheckoutState.insufficientFunds(),
        sessionMetadata: {
          sessionName: 'Test',
          trackId: 'spa',
          carIds: ['car1'],
        },
        timeoutMs: 100,
      };

      await adapter.requestCheckoutConfirmation(request);

      expect(mockWindow.webContents.send).toHaveBeenCalledWith(
        'checkout:request-confirmation',
        expect.objectContaining({
          state: 'insufficient_funds',
        })
      );
    });
  });
});