import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { DIContainer } from '../../../..//apps/companion/main/di-container';
import type { HostedSessionConfig } from 'apps/companion/main/automation/domain/types/HostedSessionConfig';
import { StepId } from 'apps/companion/main/automation/domain/value-objects/StepId';
import { PlaywrightAutomationAdapter } from '../../../../apps/companion/main/automation/infrastructure/automation';

describe('companion start automation - browser not connected at step 1', () => {
  const originalEnv = { ...process.env };
  let originalTestLauncher: unknown;

  beforeEach(() => {
    process.env = { ...originalEnv, NODE_ENV: 'production' };
 
    originalTestLauncher = (PlaywrightAutomationAdapter as typeof PlaywrightAutomationAdapter & {
      testLauncher?: unknown;
    }).testLauncher;

    const mockLauncher = {
      launch: async (_opts: unknown) => ({
        newContext: async () => ({
          newPage: async () => ({ setDefaultTimeout: () => {}, close: async () => {} }),
          close: async () => {},
        }),
        newPage: async () => ({ setDefaultTimeout: () => {}, close: async () => {} }),
        close: async () => {},
      }),
      launchPersistentContext: async (_userDataDir: string, _opts: unknown) => ({
        pages: () => [{ setDefaultTimeout: () => {}, close: async () => {} }],
        newPage: async () => ({ setDefaultTimeout: () => {}, close: async () => {} }),
        close: async () => {},
      }),
    };

    (PlaywrightAutomationAdapter as typeof PlaywrightAutomationAdapter & {
      testLauncher?: typeof mockLauncher;
    }).testLauncher = mockLauncher;

    DIContainer.resetInstance();
  });

  afterEach(async () => {
    const container = DIContainer.getInstance();
    await container.shutdown();
    DIContainer.resetInstance();
    (PlaywrightAutomationAdapter as typeof PlaywrightAutomationAdapter & {
      testLauncher?: unknown;
    }).testLauncher = originalTestLauncher;
    process.env = originalEnv;
  });

  it('marks the session as FAILED with Step 1 (LOGIN) browser-not-connected error', async () => {
    const container = DIContainer.getInstance();
    const startAutomationUseCase = container.getStartAutomationUseCase();
    const sessionRepository = container.getSessionRepository();
    const automationEngine = container.getAutomationEngine();

    const connectionResult = await container.initializeBrowserConnection();
    expect(connectionResult.success).toBe(true);

    const browserAutomation = container.getBrowserAutomation();
    if (typeof (browserAutomation as { disconnect?: () => Promise<void> }).disconnect === 'function') {
      await (browserAutomation as { disconnect: () => Promise<void> }).disconnect();
    }

    const config: HostedSessionConfig = {
      sessionName: 'Companion integration browser-not-connected',
      trackId: 'test-track',
      carIds: ['car-1'],
    };

    const dto = await startAutomationUseCase.execute(config);

    await automationEngine.executeStep(StepId.create(1), config);

    const session = await waitForFailedSession(sessionRepository, dto.sessionId);
    expect(session).toBeDefined();
    expect(session!.state!.value).toBe('FAILED');
    const error = session!.errorMessage as string | undefined;
    expect(error).toBeDefined();
    expect(error).toContain('Step 1 (Navigate to Hosted Racing page)');
    expect(error).toContain('Browser not connected');
  });
});

async function waitForFailedSession(
  sessionRepository: { findById: (id: string) => Promise<{ state?: { value?: string }; errorMessage?: unknown } | null> },
  sessionId: string,
  timeoutMs = 5000,
): Promise<{ state?: { value?: string }; errorMessage?: unknown } | null> {
  const start = Date.now();
  let last: any = null;

  // eslint-disable-next-line no-constant-condition
  while (true) {
    last = await sessionRepository.findById(sessionId);
    if (last && last.state && last.state.value === 'FAILED') {
      return last;
    }
    if (Date.now() - start >= timeoutMs) {
      return last;
    }
    await new Promise((resolve) => setTimeout(resolve, 100));
  }
}