/**
 * Test Executor - 封装Playwright MCP调用
 * 
 * 职责：
 * 1. 解析测试文档（09-test-scenarios.md + 10-e2e-test-spec.md）
 * 2. 调用Playwright MCP执行测试
 * 3. 收集执行结果（截图、视频、trace、日志）
 * 4. 生成结构化测试数据
 * 
 * @module testing/tools/test-executor
 * @version 1.0.0
 */

import * as fs from 'fs/promises';
import * as path from 'path';
import { PlaywrightMCPClient } from './mcp-client';
import type {
  TestDocument,
  TestCase,
  TestStep,
  TestAssertion,
  TestResult,
  ExecutionContext,
  ExecutionOptions,
} from './types';

/**
 * 测试执行器
 */
export class TestExecutor {
  private mcpClient: PlaywrightMCPClient;
  private context: ExecutionContext;

  constructor(options: ExecutionOptions) {
    this.mcpClient = new PlaywrightMCPClient(options.mcpConfig);
    this.context = {
      module: options.module,
      environment: options.environment || 'test',
      browser: options.browser || 'chromium',
      headless: options.headless !== false,
      baseUrl: options.baseUrl || 'http://localhost:3000',
      storageState: options.storageState,
      outputDir: options.outputDir || `./reports/${options.module}-${this.getTimestamp()}-e2e`,
    };
  }

  /**
   * 执行测试套件
   * @param testDocPath - 测试文档路径（10-e2e-test-spec.md）
   * @returns 测试结果
   */
  async executeTestSuite(testDocPath: string): Promise<TestResult[]> {
    console.log(`[TestExecutor] 开始执行测试套件: ${testDocPath}`);
    
    // 1. 解析测试文档
    const testDoc = await this.parseTestDocument(testDocPath);
    console.log(`[TestExecutor] 解析到 ${testDoc.testCases.length} 个测试用例`);

    // 2. 创建输出目录
    await this.createOutputDirectories();

    // 3. 初始化MCP客户端
    await this.mcpClient.initialize();

    // 4. 执行所有测试用例
    const results: TestResult[] = [];
    for (const testCase of testDoc.testCases) {
      const result = await this.executeTestCase(testCase);
      results.push(result);
      
      // 实时输出结果
      this.logTestResult(result);
    }

    // 5. 关闭MCP客户端
    await this.mcpClient.close();

    console.log(`[TestExecutor] 测试套件执行完成`);
    return results;
  }

  /**
   * 执行单个测试用例
   * @param testCase - 测试用例
   * @returns 测试结果
   */
  private async executeTestCase(testCase: TestCase): Promise<TestResult> {
    const startTime = Date.now();
    const result: TestResult = {
      testId: testCase.testId,
      title: testCase.title,
      priority: testCase.priority,
      status: 'pending',
      startTime: new Date().toISOString(),
      duration: 0,
      assertions: [],
      artifacts: {
        screenshots: [],
        videos: [],
        traces: [],
        logs: [],
      },
    };

    try {
      console.log(`\n[TestExecutor] 执行测试用例: ${testCase.testId} - ${testCase.title}`);

      // 1. 检查前置条件
      await this.checkPreconditions(testCase);

      // 2. 创建浏览器上下文
      await this.mcpClient.createContext({
        storageState: this.context.storageState,
      });

      // 3. 执行测试步骤
      for (const step of testCase.steps) {
        await this.executeStep(step, result);
      }

      // 4. 执行断言
      for (const assertion of testCase.assertions) {
        await this.executeAssertion(assertion, result);
      }

      // 5. 收集资产
      await this.collectArtifacts(testCase, result);

      result.status = 'passed';
      console.log(`[TestExecutor] ✅ 测试通过: ${testCase.testId}`);
    } catch (error) {
      result.status = 'failed';
      result.error = {
        message: error instanceof Error ? error.message : String(error),
        stack: error instanceof Error ? error.stack : undefined,
        failedStep: this.getCurrentStep(),
      };
      
      // 失败时截图
      await this.captureFailureScreenshot(testCase, result);
      
      console.log(`[TestExecutor] ❌ 测试失败: ${testCase.testId}`);
      console.log(`[TestExecutor] 错误: ${result.error.message}`);
    } finally {
      result.duration = Date.now() - startTime;
      result.endTime = new Date().toISOString();
    }

    return result;
  }

  /**
   * 执行测试步骤
   * @param step - 测试步骤
   * @param result - 测试结果（用于记录）
   */
  private async executeStep(step: TestStep, result: TestResult): Promise<void> {
    console.log(`[TestExecutor]   步骤 ${step.stepNumber}: ${step.description}`);

    switch (step.action) {
      case 'navigate':
        await this.mcpClient.navigate(step.target, {
          waitUntil: step.waitStrategy || 'domcontentloaded',
        });
        break;

      case 'click':
        await this.mcpClient.click(step.selector!, {
          timeout: step.timeout || 30000,
        });
        break;

      case 'fill':
        await this.mcpClient.fill(step.selector!, step.value!, {
          timeout: step.timeout || 30000,
        });
        break;

      case 'select':
        await this.mcpClient.selectOption(step.selector!, step.value!);
        break;

      case 'wait':
        if (step.selector) {
          await this.mcpClient.waitForSelector(step.selector, {
            state: step.waitState || 'visible',
            timeout: step.timeout || 30000,
          });
        } else if (step.timeout) {
          await this.mcpClient.waitForTimeout(step.timeout);
        }
        break;

      case 'hover':
        await this.mcpClient.hover(step.selector!);
        break;

      case 'press':
        await this.mcpClient.press(step.selector!, step.value!);
        break;

      default:
        throw new Error(`未知的操作类型: ${step.action}`);
    }
  }

  /**
   * 执行断言
   * @param assertion - 断言
   * @param result - 测试结果（用于记录）
   */
  private async executeAssertion(
    assertion: TestAssertion,
    result: TestResult
  ): Promise<void> {
    console.log(`[TestExecutor]   断言: ${assertion.description}`);

    const assertionResult = {
      type: assertion.type,
      description: assertion.description,
      passed: false,
      actual: undefined as any,
      expected: assertion.expected,
    };

    try {
      switch (assertion.type) {
        case 'url':
          assertionResult.actual = await this.mcpClient.getUrl();
          assertionResult.passed = assertionResult.actual.includes(assertion.expected);
          break;

        case 'visible':
          assertionResult.passed = await this.mcpClient.isVisible(assertion.selector!);
          assertionResult.actual = assertionResult.passed;
          break;

        case 'text':
          assertionResult.actual = await this.mcpClient.getText(assertion.selector!);
          assertionResult.passed = assertionResult.actual === assertion.expected;
          break;

        case 'count':
          assertionResult.actual = await this.mcpClient.getCount(assertion.selector!);
          assertionResult.passed = this.evaluateCountAssertion(
            assertionResult.actual,
            assertion.expected
          );
          break;

        case 'attribute':
          assertionResult.actual = await this.mcpClient.getAttribute(
            assertion.selector!,
            assertion.attribute!
          );
          assertionResult.passed = assertionResult.actual === assertion.expected;
          break;

        case 'enabled':
          assertionResult.passed = await this.mcpClient.isEnabled(assertion.selector!);
          assertionResult.actual = assertionResult.passed;
          break;

        case 'disabled':
          assertionResult.passed = await this.mcpClient.isDisabled(assertion.selector!);
          assertionResult.actual = assertionResult.passed;
          break;

        default:
          throw new Error(`未知的断言类型: ${assertion.type}`);
      }

      if (!assertionResult.passed) {
        throw new Error(
          `断言失败: ${assertion.description}\n` +
          `预期: ${JSON.stringify(assertion.expected)}\n` +
          `实际: ${JSON.stringify(assertionResult.actual)}`
        );
      }

      console.log(`[TestExecutor]   ✅ 断言通过`);
    } catch (error) {
      assertionResult.passed = false;
      console.log(`[TestExecutor]   ❌ 断言失败`);
      throw error;
    } finally {
      result.assertions.push(assertionResult);
    }
  }

  /**
   * 解析测试文档
   * @param docPath - 文档路径
   * @returns 测试文档对象
   */
  private async parseTestDocument(docPath: string): Promise<TestDocument> {
    const content = await fs.readFile(docPath, 'utf-8');
    
    // 简化的解析逻辑（实际应该更复杂）
    // 这里假设文档遵循标准模板格式
    const testCases: TestCase[] = [];
    
    // 使用正则表达式提取测试用例
    const testCaseRegex = /#### 测试场景 ([\d.]+): (.+?)\n\n\*\*测试ID\*\*: `(.+?)`\s+\n\*\*优先级\*\*: (P\d)/g;
    let match;
    
    while ((match = testCaseRegex.exec(content)) !== null) {
      const [, scenarioNumber, title, testId, priority] = match;
      
      // 提取该测试用例的详细内容
      const testCase: TestCase = {
        testId,
        scenarioNumber,
        title,
        priority: priority as 'P0' | 'P1' | 'P2',
        steps: [], // 需要进一步解析
        assertions: [], // 需要进一步解析
        preconditions: [],
        testData: {},
      };
      
      testCases.push(testCase);
    }

    return {
      module: this.context.module,
      version: '1.0.0',
      testCases,
    };
  }

  /**
   * 创建输出目录
   */
  private async createOutputDirectories(): Promise<void> {
    const dirs = [
      this.context.outputDir,
      path.join(this.context.outputDir, 'screenshots'),
      path.join(this.context.outputDir, 'videos'),
      path.join(this.context.outputDir, 'traces'),
      path.join(this.context.outputDir, 'logs'),
      path.join(this.context.outputDir, 'dom'),
      path.join(this.context.outputDir, 'har'),
    ];

    for (const dir of dirs) {
      await fs.mkdir(dir, { recursive: true });
    }
  }

  /**
   * 收集测试资产
   */
  private async collectArtifacts(testCase: TestCase, result: TestResult): Promise<void> {
    // 截图
    const screenshotPath = path.join(
      this.context.outputDir,
      'screenshots',
      `${testCase.testId}.png`
    );
    await this.mcpClient.screenshot(screenshotPath);
    result.artifacts.screenshots.push(screenshotPath);

    // Trace
    const tracePath = path.join(
      this.context.outputDir,
      'traces',
      `${testCase.testId}.zip`
    );
    await this.mcpClient.saveTrace(tracePath);
    result.artifacts.traces.push(tracePath);

    // 视频（如果启用）
    const videoPath = await this.mcpClient.getVideoPath();
    if (videoPath) {
      result.artifacts.videos.push(videoPath);
    }
  }

  /**
   * 失败时截图
   */
  private async captureFailureScreenshot(
    testCase: TestCase,
    result: TestResult
  ): Promise<void> {
    try {
      const screenshotPath = path.join(
        this.context.outputDir,
        'screenshots',
        `${testCase.testId}-failure.png`
      );
      await this.mcpClient.screenshot(screenshotPath);
      result.artifacts.screenshots.push(screenshotPath);
    } catch (error) {
      console.error('[TestExecutor] 失败截图失败:', error);
    }
  }

  /**
   * 检查前置条件
   */
  private async checkPreconditions(testCase: TestCase): Promise<void> {
    // TODO: 实现前置条件检查
    // 例如：检查测试数据、权限、环境状态等
  }

  /**
   * 评估计数断言
   */
  private evaluateCountAssertion(actual: number, expected: any): boolean {
    if (typeof expected === 'number') {
      return actual === expected;
    }
    
    if (typeof expected === 'object') {
      if ('min' in expected && actual < expected.min) return false;
      if ('max' in expected && actual > expected.max) return false;
      return true;
    }
    
    return false;
  }

  /**
   * 获取当前步骤（用于错误报告）
   */
  private getCurrentStep(): string {
    // TODO: 实现步骤追踪
    return 'unknown';
  }

  /**
   * 记录测试结果
   */
  private logTestResult(result: TestResult): void {
    const statusIcon = result.status === 'passed' ? '✅' : '❌';
    console.log(
      `${statusIcon} ${result.testId} - ${result.title} (${result.duration}ms)`
    );
  }

  /**
   * 获取时间戳
   */
  private getTimestamp(): string {
    const now = new Date();
    return now.toISOString().replace(/[:.]/g, '-').slice(0, -5);
  }
}

/**
 * 便捷函数：执行测试
 */
export async function executeTest(
  module: string,
  options: Partial<ExecutionOptions> = {}
): Promise<TestResult[]> {
  const testDocPath = path.join(
    process.cwd(),
    'docs',
    'modules',
    module,
    '10-e2e-test-spec.md'
  );

  const executor = new TestExecutor({
    module,
    ...options,
  } as ExecutionOptions);

  return await executor.executeTestSuite(testDocPath);
}
