import type { AutomationResult } from 'apps/companion/main/automation/application/ports/AutomationResults';
import { StepId } from 'apps/companion/main/automation/domain/value-objects/StepId';
import {
  PlaywrightAutomationAdapter,
  FixtureServer,
} from 'core/automation/infrastructure//automation';
import { PinoLogAdapter } from 'core/automation/infrastructure//logging/PinoLogAdapter';

export interface StepHarness {
  server: FixtureServer;
  adapter: PlaywrightAutomationAdapter;
  baseUrl: string;
  getFixtureUrl(step: number): string;
  navigateToFixtureStep(step: number): Promise<void>;
  executeStep(step: number, config: Record<string, unknown>): Promise<AutomationResult>;
  executeStepWithAutoNavigation(
    step: number,
    config: Record<string, unknown>,
  ): Promise<AutomationResult>;
  executeStepWithFixtureMismatch(
    step: number,
    config: Record<string, unknown>,
  ): Promise<AutomationResult>;
  dispose(): Promise<void>;
}

async function createRealAdapter(baseUrl: string): Promise<PlaywrightAutomationAdapter> {
  const logger = new PinoLogAdapter();

  const adapter = new PlaywrightAutomationAdapter(
    {
      headless: true,
      timeout: 8000,
      mode: 'real',
      baseUrl,
      userDataDir: '',
    },
    logger,
  );

  const result = await adapter.connect(false);
  if (!result.success) {
    throw new Error(result.error || 'Failed to connect Playwright adapter');
  }

  return adapter;
}

async function createMockAdapter(): Promise<PlaywrightAutomationAdapter> {
  const logger = new PinoLogAdapter();

  const adapter = new PlaywrightAutomationAdapter(
    {
      headless: true,
      timeout: 5000,
      mode: 'mock',
    },
    logger,
  );

  const result = await adapter.connect();
  if (!result.success) {
    throw new Error(result.error || 'Failed to connect mock Playwright adapter');
  }

  return adapter;
}

export async function createStepHarness(useMock: boolean = false): Promise<StepHarness> {
  const server = new FixtureServer();
  const { url } = await server.start();

  const adapter = useMock ? await createMockAdapter() : await createRealAdapter(url);

  async function navigateToFixtureStep(step: number): Promise<void> {
    await adapter.navigateToPage(server.getFixtureUrl(step));
    await adapter.getPage()?.waitForLoadState('domcontentloaded');
  }

  async function executeStepWithAutoNavigation(
    step: number,
    config: Record<string, unknown>,
  ): Promise<AutomationResult> {
    const skipFixtureNavigationFlag =
      (config as { __skipFixtureNavigation?: unknown }).__skipFixtureNavigation;
    if (skipFixtureNavigationFlag === true) {
      throw new Error(
        '__skipFixtureNavigation is not allowed in auto-navigation path',
      );
    }
    return adapter.executeStep(StepId.create(step), config);
  }

  async function executeStepWithFixtureMismatch(
    step: number,
    config: Record<string, unknown>,
  ): Promise<AutomationResult> {
    return adapter.executeStep(StepId.create(step), {
      ...config,
      __skipFixtureNavigation: true,
    });
  }

  async function executeStep(
    step: number,
    config: Record<string, unknown>,
  ): Promise<AutomationResult> {
    return executeStepWithFixtureMismatch(step, config);
  }

  async function dispose(): Promise<void> {
    await adapter.disconnect();
    await server.stop();
  }

  return {
    server,
    adapter,
    baseUrl: url,
    getFixtureUrl: (step) => server.getFixtureUrl(step),
    navigateToFixtureStep,
    executeStep,
    executeStepWithAutoNavigation,
    executeStepWithFixtureMismatch,
    dispose,
  };
}