import puppeteer, { Browser, Page } from 'puppeteer-core';
import { StepId } from '../../../packages/domain/value-objects/StepId';
import { IBrowserAutomation } from '../../../packages/application/ports/IBrowserAutomation';
import {
  NavigationResult,
  FormFillResult,
  ClickResult,
  WaitResult,
  ModalResult,
  AutomationResult,
} from '../../../packages/application/ports/AutomationResults';
import { IRacingSelectorMap, getStepSelectors, getStepName, isModalStep } from './selectors/IRacingSelectorMap';
import type { ILogger, LogContext } from '../../../application/ports/ILogger';
import { NoOpLogAdapter } from '../logging/NoOpLogAdapter';

/**
 * Configuration for connecting to browser via Chrome DevTools Protocol
 */
export interface DevToolsConfig {
  /** WebSocket endpoint URL (e.g., ws://127.0.0.1:9222/devtools/browser/...) */
  browserWSEndpoint?: string;
  /** Chrome debugging port (default: 9222) */
  debuggingPort?: number;
  /** Default timeout for operations in milliseconds (default: 30000) */
  defaultTimeout?: number;
  /** Human-like typing delay in milliseconds (default: 50) */
  typingDelay?: number;
  /** Whether to wait for network idle after navigation (default: true) */
  waitForNetworkIdle?: boolean;
  /** If true, launch a new browser instead of connecting to existing one */
  launchBrowser?: boolean;
  /** Path to Chrome executable (optional, puppeteer will try to find it) */
  executablePath?: string;
  /** Run browser in headless mode (default: false for visibility) */
  headless?: boolean;
  /** URL to navigate to after launching browser */
  startUrl?: string;
}

/**
 * BrowserDevToolsAdapter - Real browser automation using Puppeteer-core.
 * 
 * This adapter connects to an existing browser session via Chrome DevTools Protocol (CDP)
 * and automates the iRacing hosted session creation workflow.
 * 
 * Key features:
 * - Connects to existing browser (doesn't launch new one)
 * - Uses IRacingSelectorMap for element location
 * - Human-like typing delays for form filling
 * - Waits for network idle after navigation
 * - Disconnects without closing browser
 * 
 * Usage:
 * 1. Start Chrome with remote debugging: 
 *    `/Applications/Google Chrome.app/Contents/MacOS/Google Chrome --remote-debugging-port=9222`
 * 2. Navigate to iRacing and log in manually
 * 3. Create adapter and connect:
 *    ```
 *    const adapter = new BrowserDevToolsAdapter({ debuggingPort: 9222 });
 *    await adapter.connect();
 *    ```
 */
/**
 * Internal type with all config fields required (with defaults applied)
 */
type ResolvedDevToolsConfig = {
  browserWSEndpoint: string;
  debuggingPort: number;
  defaultTimeout: number;
  typingDelay: number;
  waitForNetworkIdle: boolean;
  launchBrowser: boolean;
  executablePath: string;
  headless: boolean;
  startUrl: string;
};

export class BrowserDevToolsAdapter implements IBrowserAutomation {
  private browser: Browser | null = null;
  private page: Page | null = null;
  private config: ResolvedDevToolsConfig;
  private connected: boolean = false;
  private logger: ILogger;

  constructor(config: DevToolsConfig = {}, logger?: ILogger) {
    this.config = {
      browserWSEndpoint: config.browserWSEndpoint ?? '',
      debuggingPort: config.debuggingPort ?? 9222,
      defaultTimeout: config.defaultTimeout ?? 30000,
      typingDelay: config.typingDelay ?? 50,
      waitForNetworkIdle: config.waitForNetworkIdle ?? true,
      launchBrowser: config.launchBrowser ?? false,
      executablePath: config.executablePath ?? '',
      headless: config.headless ?? false,
      startUrl: config.startUrl ?? '',
    };
    this.logger = logger ?? new NoOpLogAdapter();
  }

  /**
   * Connect to an existing browser via Chrome DevTools Protocol,
   * or launch a new browser if launchBrowser is enabled.
   * For connect mode, the browser must be started with --remote-debugging-port flag.
   */
  async connect(): Promise<void> {
    if (this.connected) {
      this.logger.debug('Already connected to browser');
      return;
    }

    const startTime = Date.now();

    try {
      if (this.config.launchBrowser) {
        // LAUNCH mode - start a new browser
        this.logger.info('Launching new browser instance', {
          headless: this.config.headless,
          hasExecutablePath: !!this.config.executablePath,
          startUrl: this.config.startUrl || '(none)'
        });

        const launchArgs = [
          '--start-maximized',
          '--no-sandbox',
        ];

        const launchOptions: Parameters<typeof puppeteer.launch>[0] = {
          headless: this.config.headless,
          args: launchArgs,
        };

        // Use explicit executablePath if provided, otherwise use channel to auto-detect Chrome
        if (this.config.executablePath) {
          launchOptions.executablePath = this.config.executablePath;
        } else {
          launchOptions.channel = 'chrome';
        }

        this.browser = await puppeteer.launch(launchOptions);

        const pages = await this.browser.pages();
        this.page = pages[0] || await this.browser.newPage();

        if (this.config.startUrl) {
          this.logger.debug('Navigating to start URL', { url: this.config.startUrl });
          await this.page.goto(this.config.startUrl, {
            waitUntil: this.config.waitForNetworkIdle ? 'networkidle2' : 'domcontentloaded'
          });
        }
      } else {
        // CONNECT mode - connect to existing browser
        this.logger.info('Connecting to browser via CDP', {
          debuggingPort: this.config.debuggingPort,
          hasWsEndpoint: !!this.config.browserWSEndpoint
        });

        if (this.config.browserWSEndpoint) {
          // Connect using explicit WebSocket endpoint
          this.logger.debug('Using explicit WebSocket endpoint');
          this.browser = await puppeteer.connect({
            browserWSEndpoint: this.config.browserWSEndpoint,
          });
        } else {
          // Connect using debugging port - need to fetch endpoint first
          this.logger.debug('Fetching WebSocket endpoint from debugging port');
          const response = await fetch(`http://127.0.0.1:${this.config.debuggingPort}/json/version`);
          const data = await response.json();
          const wsEndpoint = data.webSocketDebuggerUrl;
          
          this.browser = await puppeteer.connect({
            browserWSEndpoint: wsEndpoint,
          });
        }

        // Find iRacing tab or use the first available tab
        const pages = await this.browser.pages();
        this.page = await this.findIRacingPage(pages) || pages[0];
        
        if (!this.page) {
          throw new Error('No pages found in browser');
        }
      }

      // Set default timeout
      this.page!.setDefaultTimeout(this.config.defaultTimeout);
      
      this.connected = true;
      const durationMs = Date.now() - startTime;
      this.logger.info('Connected to browser successfully', {
        durationMs,
        pageUrl: this.page!.url(),
        launchMode: this.config.launchBrowser
      });
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : String(error);
      this.logger.error('Failed to connect to browser', error instanceof Error ? error : new Error(errorMessage));
      throw new Error(`Failed to connect to browser: ${errorMessage}`);
    }
  }

  /**
   * Disconnect from the browser without closing it.
   * The user can continue using the browser after disconnection.
   */
  async disconnect(): Promise<void> {
    this.logger.info('Disconnecting from browser');
    if (this.browser) {
      // Disconnect without closing - user may still use the browser
      this.browser.disconnect();
      this.browser = null;
      this.page = null;
    }
    this.connected = false;
    this.logger.debug('Browser disconnected');
  }

  /**
   * Check if adapter is connected to browser.
   */
  isConnected(): boolean {
    return this.connected && this.browser !== null && this.page !== null;
  }

  /**
   * Navigate to a URL and wait for the page to load.
   */
  async navigateToPage(url: string): Promise<NavigationResult> {
    this.ensureConnected();
    
    const startTime = Date.now();
    
    try {
      const waitUntil = this.config.waitForNetworkIdle ? 'networkidle2' : 'domcontentloaded';
      await this.page!.goto(url, { waitUntil });
      
      const loadTime = Date.now() - startTime;
      
      return {
        success: true,
        url,
        loadTime,
      };
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : String(error);
      return {
        success: false,
        url,
        loadTime: Date.now() - startTime,
        error: `Navigation failed: ${errorMessage}`,
      };
    }
  }

  /**
   * Fill a form field with human-like typing delay.
   * 
   * @param fieldName - Field identifier (will be looked up in selector map or used directly)
   * @param value - Value to type into the field
   */
  async fillFormField(fieldName: string, value: string): Promise<FormFillResult> {
    this.ensureConnected();

    try {
      // Try to find the element
      const element = await this.page!.$(fieldName);
      
      if (!element) {
        return {
          success: false,
          fieldName,
          valueSet: '',
          error: `Field not found: ${fieldName}`,
        };
      }

      // Clear existing value and type new value with human-like delay
      await element.click({ clickCount: 3 }); // Select all existing text
      await element.type(value, { delay: this.config.typingDelay });

      return {
        success: true,
        fieldName,
        valueSet: value,
      };
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : String(error);
      return {
        success: false,
        fieldName,
        valueSet: '',
        error: `Failed to fill field: ${errorMessage}`,
      };
    }
  }

  /**
   * Click an element on the page.
   */
  async clickElement(selector: string): Promise<ClickResult> {
    this.ensureConnected();

    try {
      // Wait for element to be visible and clickable
      await this.page!.waitForSelector(selector, { 
        visible: true,
        timeout: this.config.defaultTimeout 
      });
      
      await this.page!.click(selector);

      return {
        success: true,
        target: selector,
      };
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : String(error);
      return {
        success: false,
        target: selector,
        error: `Click failed: ${errorMessage}`,
      };
    }
  }

  /**
   * Wait for an element to appear on the page.
   */
  async waitForElement(selector: string, maxWaitMs: number = 5000): Promise<WaitResult> {
    this.ensureConnected();

    const startTime = Date.now();

    try {
      await this.page!.waitForSelector(selector, { 
        timeout: maxWaitMs,
        visible: true 
      });

      return {
        success: true,
        target: selector,
        waitedMs: Date.now() - startTime,
        found: true,
      };
    } catch (error) {
      return {
        success: false,
        target: selector,
        waitedMs: Date.now() - startTime,
        found: false,
        error: `Element not found within ${maxWaitMs}ms`,
      };
    }
  }

  /**
   * Handle modal operations for specific workflow steps.
   * Modal steps are: 6 (SET_ADMINS), 9 (ADD_CAR), 12 (ADD_TRACK)
   */
  async handleModal(stepId: StepId, action: string): Promise<ModalResult> {
    this.ensureConnected();

    if (!stepId.isModalStep()) {
      return {
        success: false,
        stepId: stepId.value,
        action,
        error: `Step ${stepId.value} (${getStepName(stepId.value)}) is not a modal step`,
      };
    }

    try {
      const stepSelectors = getStepSelectors(stepId.value);
      
      if (!stepSelectors?.modal) {
        return {
          success: false,
          stepId: stepId.value,
          action,
          error: `No modal selectors defined for step ${stepId.value}`,
        };
      }

      const modalSelectors = stepSelectors.modal;

      switch (action) {
        case 'open':
          // Wait for and verify modal is open
          await this.page!.waitForSelector(modalSelectors.container, {
            visible: true,
            timeout: this.config.defaultTimeout,
          });
          break;

        case 'close':
          // Click close button
          await this.page!.click(modalSelectors.closeButton);
          // Wait for modal to disappear
          await this.page!.waitForSelector(modalSelectors.container, {
            hidden: true,
            timeout: this.config.defaultTimeout,
          });
          break;

        case 'search':
          // Focus search input if available
          if (modalSelectors.searchInput) {
            await this.page!.click(modalSelectors.searchInput);
          }
          break;

        case 'select':
          // Click select/confirm button
          if (modalSelectors.selectButton) {
            await this.page!.click(modalSelectors.selectButton);
          }
          break;

        default:
          return {
            success: false,
            stepId: stepId.value,
            action,
            error: `Unknown modal action: ${action}`,
          };
      }

      return {
        success: true,
        stepId: stepId.value,
        action,
      };
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : String(error);
      return {
        success: false,
        stepId: stepId.value,
        action,
        error: `Modal operation failed: ${errorMessage}`,
      };
    }
  }

  // ============== Helper Methods ==============

  /**
   * Find the iRacing page among open browser tabs.
   */
  private async findIRacingPage(pages: Page[]): Promise<Page | null> {
    for (const page of pages) {
      const url = page.url();
      if (url.includes('iracing.com') || url.includes('members-ng.iracing.com')) {
        return page;
      }
    }
    return null;
  }

  /**
   * Ensure adapter is connected before operations.
   */
  private ensureConnected(): void {
    if (!this.isConnected()) {
      throw new Error('Not connected to browser. Call connect() first.');
    }
  }

  // ============== Extended Methods for Workflow Automation ==============

  /**
   * Navigate to a specific step in the wizard using sidebar navigation.
   */
  async navigateToStep(stepId: StepId): Promise<NavigationResult> {
    this.ensureConnected();

    const startTime = Date.now();
    const stepSelectors = getStepSelectors(stepId.value);

    if (!stepSelectors?.sidebarLink) {
      return {
        success: false,
        url: '',
        loadTime: 0,
        error: `No sidebar link defined for step ${stepId.value} (${getStepName(stepId.value)})`,
      };
    }

    try {
      await this.page!.click(stepSelectors.sidebarLink);
      
      // Wait for step container to be visible
      if (stepSelectors.container) {
        await this.page!.waitForSelector(stepSelectors.container, { visible: true });
      }

      return {
        success: true,
        url: this.page!.url(),
        loadTime: Date.now() - startTime,
      };
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : String(error);
      return {
        success: false,
        url: this.page!.url(),
        loadTime: Date.now() - startTime,
        error: `Failed to navigate to step: ${errorMessage}`,
      };
    }
  }

  /**
   * Get the current page URL.
   */
  getCurrentUrl(): string {
    if (!this.page) {
      return '';
    }
    return this.page.url();
  }

  /**
   * Take a screenshot of the current page (useful for debugging).
   */
  async takeScreenshot(path: string): Promise<void> {
    this.ensureConnected();
    await this.page!.screenshot({ path, fullPage: true });
  }

  /**
   * Get the current page content (useful for debugging).
   */
  async getPageContent(): Promise<string> {
    this.ensureConnected();
    return await this.page!.content();
  }

  /**
   * Wait for network to be idle (no pending requests).
   */
  async waitForNetworkIdle(timeout: number = 5000): Promise<void> {
    this.ensureConnected();
    await this.page!.waitForNetworkIdle({ timeout });
  }

  /**
   * Execute JavaScript in the page context.
   */
  async evaluate<T>(fn: () => T): Promise<T> {
    this.ensureConnected();
    return await this.page!.evaluate(fn);
  }

  // ============== Step Execution ==============

  /**
   * Execute a complete workflow step with all required browser operations.
   * Uses IRacingSelectorMap to locate elements and performs appropriate actions.
   *
   * Step workflow:
   * 1. LOGIN - Skip (user pre-authenticated)
   * 2. HOSTED_RACING - Navigate to hosted racing page
   * 3. CREATE_RACE - Click create race button
   * 4. RACE_INFORMATION - Fill session name, password, description
   * 5. SERVER_DETAILS - Select server region, launch time
   * 6. SET_ADMINS - Add admins (modal step)
   * 7. TIME_LIMITS - Set practice/qualify/race times
   * 8. SET_CARS - Configure car selection
   * 9. ADD_CAR - Add cars (modal step)
   * 10. SET_CAR_CLASSES - Configure car classes
   * 11. SET_TRACK - Select track
   * 12. ADD_TRACK - Add track (modal step)
   * 13. TRACK_OPTIONS - Track configuration
   * 14. TIME_OF_DAY - Set time of day
   * 15. WEATHER - Weather settings
   * 16. RACE_OPTIONS - Race rules and options
   * 17. TEAM_DRIVING - Team settings
   * 18. TRACK_CONDITIONS - Final review (SAFETY STOP - no checkout)
   *
   * @param stepId - The step to execute (1-18)
   * @param config - Session configuration with form field values
   * @returns AutomationResult with success/failure and metadata
   */
  async executeStep(stepId: StepId, config: Record<string, unknown>): Promise<AutomationResult> {
    this.ensureConnected();

    const stepNumber = stepId.value;
    const stepSelectors = getStepSelectors(stepNumber);
    const stepName = getStepName(stepNumber);
    const startTime = Date.now();

    this.logger.info('Executing step', { stepId: stepNumber, stepName });

    try {
      switch (stepNumber) {
        case 1: // LOGIN - Skip, user already authenticated
          return {
            success: true,
            metadata: {
              skipped: true,
              reason: 'User pre-authenticated',
              step: stepName
            }
          };

        case 2: // HOSTED_RACING - Navigate to hosted racing page
          return await this.executeHostedRacingStep();

        case 3: // CREATE_RACE - Click create race button
          return await this.executeCreateRaceStep(stepSelectors);

        case 4: // RACE_INFORMATION - Fill session details
          return await this.executeRaceInformationStep(stepSelectors, config);

        case 5: // SERVER_DETAILS - Configure server settings
          return await this.executeServerDetailsStep(stepSelectors, config);

        case 6: // SET_ADMINS - Add admins (modal step)
          return await this.executeSetAdminsStep(stepSelectors, config);

        case 7: // TIME_LIMITS - Configure time limits
          return await this.executeTimeLimitsStep(stepSelectors, config);

        case 8: // SET_CARS - Configure car selection
          return await this.executeSetCarsStep(stepSelectors);

        case 9: // ADD_CAR - Add cars (modal step)
          return await this.executeAddCarStep(stepSelectors, config);

        case 10: // SET_CAR_CLASSES - Configure car classes
          return await this.executeSetCarClassesStep(stepSelectors, config);

        case 11: // SET_TRACK - Select track
          return await this.executeSetTrackStep(stepSelectors);

        case 12: // ADD_TRACK - Add track (modal step)
          return await this.executeAddTrackStep(stepSelectors, config);

        case 13: // TRACK_OPTIONS - Track configuration
          return await this.executeTrackOptionsStep(stepSelectors, config);

        case 14: // TIME_OF_DAY - Set time of day
          return await this.executeTimeOfDayStep(stepSelectors, config);

        case 15: // WEATHER - Weather settings
          return await this.executeWeatherStep(stepSelectors, config);

        case 16: // RACE_OPTIONS - Race rules and options
          return await this.executeRaceOptionsStep(stepSelectors, config);

        case 17: // TEAM_DRIVING - Team settings
          return await this.executeTeamDrivingStep(stepSelectors, config);

        case 18: // TRACK_CONDITIONS - Final review (SAFETY STOP)
          return await this.executeTrackConditionsStep(stepSelectors, config);

        default:
          this.logger.warn('Unknown step requested', { stepId: stepNumber });
          return {
            success: false,
            error: `Unknown step: ${stepNumber}`,
            metadata: { step: stepName }
          };
      }
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : String(error);
      const durationMs = Date.now() - startTime;
      this.logger.error('Step execution failed', error instanceof Error ? error : new Error(errorMessage), {
        stepId: stepNumber,
        stepName,
        durationMs
      });
      return {
        success: false,
        error: errorMessage,
        metadata: { step: stepName }
      };
    }
  }

  // ============== Individual Step Implementations ==============

  private async executeHostedRacingStep(): Promise<AutomationResult> {
    const navResult = await this.navigateToPage(IRacingSelectorMap.urls.hostedRacing);
    if (!navResult.success) {
      return { success: false, error: navResult.error, metadata: { step: 'HOSTED_RACING' } };
    }

    // Wait for page to be ready
    const stepSelectors = getStepSelectors(2);
    if (stepSelectors?.container) {
      await this.waitForElement(stepSelectors.container, this.config.defaultTimeout);
    }

    return {
      success: true,
      metadata: { step: 'HOSTED_RACING', url: IRacingSelectorMap.urls.hostedRacing }
    };
  }

  private async executeCreateRaceStep(stepSelectors: ReturnType<typeof getStepSelectors>): Promise<AutomationResult> {
    if (!stepSelectors?.buttons?.createRace) {
      return { success: false, error: 'Create race button selector not defined', metadata: { step: 'CREATE_RACE' } };
    }

    const clickResult = await this.clickElement(stepSelectors.buttons.createRace);
    if (!clickResult.success) {
      return { success: false, error: clickResult.error, metadata: { step: 'CREATE_RACE' } };
    }

    // Wait for wizard modal to appear
    const waitResult = await this.waitForElement(IRacingSelectorMap.common.wizardContainer, this.config.defaultTimeout);
    if (!waitResult.success) {
      return { success: false, error: 'Wizard did not open', metadata: { step: 'CREATE_RACE' } };
    }

    return { success: true, metadata: { step: 'CREATE_RACE' } };
  }

  private async executeRaceInformationStep(
    stepSelectors: ReturnType<typeof getStepSelectors>,
    config: Record<string, unknown>
  ): Promise<AutomationResult> {
    // Fill session name if provided
    if (config.sessionName && stepSelectors?.fields?.sessionName) {
      const fillResult = await this.fillFormField(stepSelectors.fields.sessionName, config.sessionName as string);
      if (!fillResult.success) {
        return { success: false, error: fillResult.error, metadata: { step: 'RACE_INFORMATION', field: 'sessionName' } };
      }
    }

    // Fill password if provided
    if (config.password && stepSelectors?.fields?.password) {
      const fillResult = await this.fillFormField(stepSelectors.fields.password, config.password as string);
      if (!fillResult.success) {
        return { success: false, error: fillResult.error, metadata: { step: 'RACE_INFORMATION', field: 'password' } };
      }
    }

    // Fill description if provided
    if (config.description && stepSelectors?.fields?.description) {
      const fillResult = await this.fillFormField(stepSelectors.fields.description, config.description as string);
      if (!fillResult.success) {
        return { success: false, error: fillResult.error, metadata: { step: 'RACE_INFORMATION', field: 'description' } };
      }
    }

    // Click next button to proceed
    if (stepSelectors?.buttons?.next) {
      const clickResult = await this.clickElement(stepSelectors.buttons.next);
      if (!clickResult.success) {
        return { success: false, error: clickResult.error, metadata: { step: 'RACE_INFORMATION', action: 'next' } };
      }
    }

    return { success: true, metadata: { step: 'RACE_INFORMATION' } };
  }

  private async executeServerDetailsStep(
    stepSelectors: ReturnType<typeof getStepSelectors>,
    config: Record<string, unknown>
  ): Promise<AutomationResult> {
    // Server region selection would require dropdown interaction
    // For now, accept defaults unless specific configuration is provided

    // Click next button to proceed
    if (stepSelectors?.buttons?.next) {
      const clickResult = await this.clickElement(stepSelectors.buttons.next);
      if (!clickResult.success) {
        return { success: false, error: clickResult.error, metadata: { step: 'SERVER_DETAILS', action: 'next' } };
      }
    }

    return { success: true, metadata: { step: 'SERVER_DETAILS' } };
  }

  private async executeSetAdminsStep(
    stepSelectors: ReturnType<typeof getStepSelectors>,
    config: Record<string, unknown>
  ): Promise<AutomationResult> {
    // Admin step is a modal step - check if we need to add admins
    const adminIds = config.adminIds as string[] | undefined;

    if (adminIds && adminIds.length > 0 && stepSelectors?.modal) {
      // Open admin modal
      if (stepSelectors.buttons?.addAdmin) {
        const clickResult = await this.clickElement(stepSelectors.buttons.addAdmin);
        if (!clickResult.success) {
          return { success: false, error: clickResult.error, metadata: { step: 'SET_ADMINS', action: 'openModal' } };
        }

        // Wait for modal to appear
        await this.waitForElement(stepSelectors.modal.container, this.config.defaultTimeout);

        // Search and select admins would require more complex interaction
        // For now, close the modal
        if (stepSelectors.modal.closeButton) {
          await this.clickElement(stepSelectors.modal.closeButton);
        }
      }
    }

    // Click next button to proceed
    if (stepSelectors?.buttons?.next) {
      const clickResult = await this.clickElement(stepSelectors.buttons.next);
      if (!clickResult.success) {
        return { success: false, error: clickResult.error, metadata: { step: 'SET_ADMINS', action: 'next' } };
      }
    }

    return { success: true, metadata: { step: 'SET_ADMINS', isModalStep: true } };
  }

  private async executeTimeLimitsStep(
    stepSelectors: ReturnType<typeof getStepSelectors>,
    config: Record<string, unknown>
  ): Promise<AutomationResult> {
    // Fill practice length if provided
    if (config.practiceLength && stepSelectors?.fields?.practiceLength) {
      await this.fillFormField(stepSelectors.fields.practiceLength, String(config.practiceLength));
    }

    // Fill qualify length if provided
    if (config.qualifyingLength && stepSelectors?.fields?.qualifyLength) {
      await this.fillFormField(stepSelectors.fields.qualifyLength, String(config.qualifyingLength));
    }

    // Fill race length if provided
    if (config.raceLength && stepSelectors?.fields?.raceLength) {
      await this.fillFormField(stepSelectors.fields.raceLength, String(config.raceLength));
    }

    // Fill warmup length if provided
    if (config.warmupLength && stepSelectors?.fields?.warmupLength) {
      await this.fillFormField(stepSelectors.fields.warmupLength, String(config.warmupLength));
    }

    // Click next button to proceed
    if (stepSelectors?.buttons?.next) {
      const clickResult = await this.clickElement(stepSelectors.buttons.next);
      if (!clickResult.success) {
        return { success: false, error: clickResult.error, metadata: { step: 'TIME_LIMITS', action: 'next' } };
      }
    }

    return { success: true, metadata: { step: 'TIME_LIMITS' } };
  }

  private async executeSetCarsStep(stepSelectors: ReturnType<typeof getStepSelectors>): Promise<AutomationResult> {
    // This step shows the car selection overview
    // Actual car addition happens in step 9 (ADD_CAR modal)

    // Click next button to proceed to track selection
    if (stepSelectors?.buttons?.next) {
      const clickResult = await this.clickElement(stepSelectors.buttons.next);
      if (!clickResult.success) {
        return { success: false, error: clickResult.error, metadata: { step: 'SET_CARS', action: 'next' } };
      }
    }

    return { success: true, metadata: { step: 'SET_CARS' } };
  }

  private async executeAddCarStep(
    stepSelectors: ReturnType<typeof getStepSelectors>,
    config: Record<string, unknown>
  ): Promise<AutomationResult> {
    // Add car is a modal step
    const carIds = config.carIds as string[] | undefined;

    if (carIds && carIds.length > 0 && stepSelectors?.modal) {
      // Click add car button to open modal
      const step8Selectors = getStepSelectors(8);
      if (step8Selectors?.buttons?.addCar) {
        const clickResult = await this.clickElement(step8Selectors.buttons.addCar);
        if (!clickResult.success) {
          return { success: false, error: clickResult.error, metadata: { step: 'ADD_CAR', action: 'openModal' } };
        }

        // Wait for modal to appear
        await this.waitForElement(stepSelectors.modal.container, this.config.defaultTimeout);

        // Search for car would require typing in search field
        // For each car, we would search and select
        // For now, this is a placeholder for more complex car selection logic

        // Close modal after selection (or if no action needed)
        if (stepSelectors.modal.closeButton) {
          await this.clickElement(stepSelectors.modal.closeButton);
        }
      }
    }

    return { success: true, metadata: { step: 'ADD_CAR', isModalStep: true, carCount: carIds?.length ?? 0 } };
  }

  private async executeSetCarClassesStep(
    stepSelectors: ReturnType<typeof getStepSelectors>,
    config: Record<string, unknown>
  ): Promise<AutomationResult> {
    // Car classes configuration - usually auto-configured based on selected cars
    // Click next button to proceed
    if (stepSelectors?.buttons?.next) {
      const clickResult = await this.clickElement(stepSelectors.buttons.next);
      if (!clickResult.success) {
        return { success: false, error: clickResult.error, metadata: { step: 'SET_CAR_CLASSES', action: 'next' } };
      }
    }

    return { success: true, metadata: { step: 'SET_CAR_CLASSES' } };
  }

  private async executeSetTrackStep(stepSelectors: ReturnType<typeof getStepSelectors>): Promise<AutomationResult> {
    // This step shows the track selection overview
    // Actual track selection happens in step 12 (ADD_TRACK modal)

    // Click next button to proceed
    if (stepSelectors?.buttons?.next) {
      const clickResult = await this.clickElement(stepSelectors.buttons.next);
      if (!clickResult.success) {
        return { success: false, error: clickResult.error, metadata: { step: 'SET_TRACK', action: 'next' } };
      }
    }

    return { success: true, metadata: { step: 'SET_TRACK' } };
  }

  private async executeAddTrackStep(
    stepSelectors: ReturnType<typeof getStepSelectors>,
    config: Record<string, unknown>
  ): Promise<AutomationResult> {
    // Add track is a modal step
    const trackId = config.trackId as string | undefined;

    if (trackId && stepSelectors?.modal) {
      // Click add track button to open modal
      const step11Selectors = getStepSelectors(11);
      if (step11Selectors?.buttons?.addTrack) {
        const clickResult = await this.clickElement(step11Selectors.buttons.addTrack);
        if (!clickResult.success) {
          return { success: false, error: clickResult.error, metadata: { step: 'ADD_TRACK', action: 'openModal' } };
        }

        // Wait for modal to appear
        await this.waitForElement(stepSelectors.modal.container, this.config.defaultTimeout);

        // Search for track would require typing in search field
        // For now, this is a placeholder for more complex track selection logic

        // Close modal after selection (or if no action needed)
        if (stepSelectors.modal.closeButton) {
          await this.clickElement(stepSelectors.modal.closeButton);
        }
      }
    }

    return { success: true, metadata: { step: 'ADD_TRACK', isModalStep: true, trackId } };
  }

  private async executeTrackOptionsStep(
    stepSelectors: ReturnType<typeof getStepSelectors>,
    config: Record<string, unknown>
  ): Promise<AutomationResult> {
    // Track options like configuration, pit stalls etc.
    // Accept defaults for now

    // Click next button to proceed
    if (stepSelectors?.buttons?.next) {
      const clickResult = await this.clickElement(stepSelectors.buttons.next);
      if (!clickResult.success) {
        return { success: false, error: clickResult.error, metadata: { step: 'TRACK_OPTIONS', action: 'next' } };
      }
    }

    return { success: true, metadata: { step: 'TRACK_OPTIONS' } };
  }

  private async executeTimeOfDayStep(
    stepSelectors: ReturnType<typeof getStepSelectors>,
    config: Record<string, unknown>
  ): Promise<AutomationResult> {
    // Time of day configuration
    // Accept defaults for now - time sliders are complex to interact with

    // Click next button to proceed
    if (stepSelectors?.buttons?.next) {
      const clickResult = await this.clickElement(stepSelectors.buttons.next);
      if (!clickResult.success) {
        return { success: false, error: clickResult.error, metadata: { step: 'TIME_OF_DAY', action: 'next' } };
      }
    }

    return { success: true, metadata: { step: 'TIME_OF_DAY' } };
  }

  private async executeWeatherStep(
    stepSelectors: ReturnType<typeof getStepSelectors>,
    config: Record<string, unknown>
  ): Promise<AutomationResult> {
    // Weather configuration
    // Accept defaults for now

    // Click next button to proceed
    if (stepSelectors?.buttons?.next) {
      const clickResult = await this.clickElement(stepSelectors.buttons.next);
      if (!clickResult.success) {
        return { success: false, error: clickResult.error, metadata: { step: 'WEATHER', action: 'next' } };
      }
    }

    return { success: true, metadata: { step: 'WEATHER' } };
  }

  private async executeRaceOptionsStep(
    stepSelectors: ReturnType<typeof getStepSelectors>,
    config: Record<string, unknown>
  ): Promise<AutomationResult> {
    // Race options like max drivers, hardcore incidents, etc.
    // Fill max drivers if provided
    if (config.maxDrivers && stepSelectors?.fields?.maxDrivers) {
      await this.fillFormField(stepSelectors.fields.maxDrivers, String(config.maxDrivers));
    }

    // Click next button to proceed
    if (stepSelectors?.buttons?.next) {
      const clickResult = await this.clickElement(stepSelectors.buttons.next);
      if (!clickResult.success) {
        return { success: false, error: clickResult.error, metadata: { step: 'RACE_OPTIONS', action: 'next' } };
      }
    }

    return { success: true, metadata: { step: 'RACE_OPTIONS' } };
  }

  private async executeTeamDrivingStep(
    stepSelectors: ReturnType<typeof getStepSelectors>,
    config: Record<string, unknown>
  ): Promise<AutomationResult> {
    // Team driving configuration
    // Accept defaults for now (usually disabled)

    // Click next button to proceed
    if (stepSelectors?.buttons?.next) {
      const clickResult = await this.clickElement(stepSelectors.buttons.next);
      if (!clickResult.success) {
        return { success: false, error: clickResult.error, metadata: { step: 'TEAM_DRIVING', action: 'next' } };
      }
    }

    return { success: true, metadata: { step: 'TEAM_DRIVING' } };
  }

  private async executeTrackConditionsStep(
    stepSelectors: ReturnType<typeof getStepSelectors>,
    config: Record<string, unknown>
  ): Promise<AutomationResult> {
    // FINAL STEP - SAFETY STOP
    // We fill track conditions but DO NOT click checkout button
    
    // Track state selection would require dropdown interaction
    // For now, accept defaults

    return {
      success: true,
      metadata: {
        step: 'TRACK_CONDITIONS',
        safetyStop: true,
        message: 'Automation stopped at final step. User must review configuration and click checkout manually.',
        checkoutButtonSelector: IRacingSelectorMap.common.checkoutButton
      }
    };
  }
}