import { StepId } from '../../../../domain/value-objects/StepId';
import type { IBrowserAutomation } from '../../../../application/ports/ScreenAutomationPort';
import type { NavigationResultDTO } from '../../../../application/dto/NavigationResultDTO';
import type { FormFillResultDTO } from '../../../../application/dto/FormFillResultDTO';
import type { ClickResultDTO } from '../../../../application/dto/ClickResultDTO';
import type { WaitResultDTO } from '../../../../application/dto/WaitResultDTO';
import type { ModalResultDTO } from '../../../../application/dto/ModalResultDTO';
import type { AutomationResultDTO } from '../../../../application/dto/AutomationResultDTO';
import type { IAutomationLifecycleEmitter, LifecycleCallback } from '../../IAutomationLifecycleEmitter';

interface MockConfig {
  simulateFailures?: boolean;
  failureRate?: number;
}

interface StepExecutionResult {
  success: boolean;
  stepId: number;
  wasModalStep?: boolean;
  shouldStop?: boolean;
  executionTime: number;
  metrics: {
    totalDelay: number;
    operationCount: number;
  };
}

export class MockBrowserAutomationAdapter implements IBrowserAutomation, IAutomationLifecycleEmitter {
  private config: MockConfig;
  private connected: boolean = false;
  private lifecycleCallbacks: Set<LifecycleCallback> = new Set();

  constructor(config: MockConfig = {}) {
    this.config = {
      simulateFailures: config.simulateFailures ?? false,
      failureRate: config.failureRate ?? 0.1,
    };
  }

  async connect(): Promise<AutomationResultDTO> {
    this.connected = true;
    return { success: true };
  }

  async disconnect(): Promise<void> {
    this.connected = false;
  }

  isConnected(): boolean {
    return this.connected;
  }

  async navigateToPage(url: string): Promise<NavigationResultDTO> {
    const delay = this.randomDelay(200, 800);
    await this.sleep(delay);
    return {
      success: true,
      url,
      loadTime: delay,
    };
  }

  async fillFormField(fieldName: string, value: string): Promise<FormFillResultDTO> {
    const delay = this.randomDelay(100, 500);
    await this.sleep(delay);
    return {
      success: true,
      fieldName,
      valueSet: value,
    };
  }

  async clickElement(_selector: string): Promise<ClickResultDTO> {
    const delay = this.randomDelay(50, 300);
    await this.sleep(delay);
    return {
      success: true,
      target: selector,
    };
  }

  async waitForElement(_selector: string, maxWaitMs: number = 5000): Promise<WaitResultDTO> {
    const delay = this.randomDelay(100, 1000);
    
    await this.sleep(delay);
    
    return {
      success: true,
      target: selector,
      waitedMs: delay,
      found: true,
    };
  }

  async handleModal(stepId: StepId, action: string): Promise<ModalResultDTO> {
    if (!stepId.isModalStep()) {
      throw new Error(`Step ${stepId.value} is not a modal step`);
    }
    
    const delay = this.randomDelay(200, 600);
    await this.sleep(delay);
    return {
      success: true,
      stepId: stepId.value,
      action,
    };
  }

  async executeStep(stepId: StepId, config: Record<string, unknown>): Promise<AutomationResultDTO> {
    // Emit a simple lifecycle event for tests/overlay sync
    await this.emitLifecycle({
      type: 'action-started',
      actionId: String(stepId.value),
      timestamp: Date.now(),
      payload: { config },
    });
    if (this.shouldSimulateFailure()) {
      throw new Error(`Simulated failure at step ${stepId.value}`);
    }

    const startTime = Date.now();
    let totalDelay = 0;
    let operationCount = 0;

    const navigationDelay = this.randomDelay(200, 500);
    await this.sleep(navigationDelay);
    totalDelay += navigationDelay;
    operationCount++;

    if (stepId.isModalStep()) {
      const modalDelay = this.randomDelay(200, 400);
      await this.sleep(modalDelay);
      totalDelay += modalDelay;
      operationCount++;
    }

    const executionTime = Date.now() - startTime;

    return {
      success: true,
      metadata: {
        stepId: stepId.value,
        wasModalStep: stepId.isModalStep(),
        shouldStop: stepId.isFinalStep(),
        executionTime,
        totalDelay,
        operationCount,
      },
    };
  }

  private randomDelay(min: number, max: number): number {
    return Math.floor(Math.random() * (max - min + 1)) + min;
  }

  private async sleep(ms: number): Promise<void> {
    return new Promise(resolve => setTimeout(resolve, ms));
  }

  private shouldSimulateFailure(): boolean {
    if (!this.config.simulateFailures) {
      return false;
    }
    return Math.random() < (this.config.failureRate || 0.1);
  }

  onLifecycle(cb: LifecycleCallback): void {
    this.lifecycleCallbacks.add(cb);
  }

  offLifecycle(cb: LifecycleCallback): void {
    this.lifecycleCallbacks.delete(cb);
  }

  private async emitLifecycle(event: Parameters<LifecycleCallback>[0]): Promise<void> {
    for (const cb of Array.from(this.lifecycleCallbacks)) {
      await cb(event);
    }
  }
}