const { DIRS, CONFIG, ensureDir, timestamp, log, writeMarkdown } = require('./utils');
const context = require('./context-manager');
const path = require('path');
const fs = require('fs');
const { execSync, spawn } = require('child_process');

function executeTests(testName, options = {}) {
  const testCfg = CONFIG.test || {};
  const {
    headed = CONFIG.browser && CONFIG.browser.headed || false,
    timeout = testCfg.timeout || 120000,
    retries = testCfg.retries || 0,
    workers = testCfg.workers || 1,
    //screenshots = 'off',
  } = options;

  const runId = `run-${timestamp()}`;
  const runDir = path.join(DIRS.testResults, runId);
  ensureDir(runDir);

  const storyName = testName ? testName.replace(/\.spec\.ts$/, '').split(/[\/\\]/).pop() : null;

  log('EXECUTOR', `Run ID: ${runId}`);
  log('EXECUTOR', `Test: ${testName || 'all tests'}`);
  if (storyName) log('EXECUTOR', `Story: ${storyName}`);

  const args = ['playwright', 'test'];

  if (testName) {
    let testFile = testName.endsWith('.spec.ts') ? testName : `${testName}.spec.ts`;
    if (testFile.includes('/') || testFile.includes('\\')) {
      args.push(testFile);
    } else {
      args.push(path.join('tests', testFile));
    }
  }

  args.push('--reporter', 'list,json');

  if (headed) args.push('--headed');
  if (retries > 0) args.push('--retries', retries.toString());
  if (workers) args.push('--workers', workers.toString());
  if (timeout) args.push('--timeout', timeout.toString());
  // Screenshots configured in playwright.config.ts — not a valid CLI arg

  const outputPath = path.join(runDir, 'execution-output.json');
  const resultPath = path.join(runDir, 'execution-result.json');

  log('EXECUTOR', `Command: npx ${args.join(' ')}`);

  const startTime = Date.now();

  try {
    const stdout = execSync(`npx ${args.join(' ')}`, {
      cwd: DIRS.ROOT,
      encoding: 'utf-8',
      timeout: 300000,
      maxBuffer: 10 * 1024 * 1024,
      stdio: ['pipe', 'pipe', 'pipe'],
    });

    fs.writeFileSync(outputPath, stdout, 'utf-8');

    const result = {
      runId,
      story: storyName,
      test: testName || 'all',
      success: true,
      exitCode: 0,
      duration: Date.now() - startTime,
      timestamp: new Date().toISOString(),
      output: stdout.substring(0, 50000),
      failedTests: [],
      passedTests: [],
    };

    writeMarkdown(resultPath, JSON.stringify(result, null, 2));
    context.setLastRun(runId);
    if (storyName) {
      context.addRunId(storyName, runId, true, testName);
      context.phaseComplete('execute', storyName);
    }
    log('EXECUTOR', `Tests passed (${result.duration}ms)`);

    return result;
  } catch (err) {
    const stderr = err.stderr || '';
    const stdout = err.stdout || '';

    const combinedOutput = stdout + '\n' + stderr;
    fs.writeFileSync(outputPath, combinedOutput, 'utf-8');

    const failedTests = extractFailedTests(combinedOutput);

    const result = {
      runId,
      story: storyName,
      test: testName || 'all',
      success: false,
      exitCode: err.status || 1,
      duration: Date.now() - startTime,
      timestamp: new Date().toISOString(),
      error: err.message,
      output: combinedOutput.substring(0, 50000),
      failedTests,
    };

    writeMarkdown(resultPath, JSON.stringify(result, null, 2));
    context.setLastRun(runId);
    if (storyName) {
      context.addRunId(storyName, runId, false, testName);
      context.phaseComplete('execute', storyName);
    }
    log('EXECUTOR', `Tests failed (${result.duration}ms) - ${failedTests.length} failure(s)`);

    return result;
  }
}

function extractFailedTests(output) {
  const failed = [];
  const lines = output.split('\n');

  let current = null;
  for (const line of lines) {
    const failMatch = line.match(/\s+×\s+\d+\)\s+\[(.+?)\]\s+(.+)/);
    if (failMatch) {
      failed.push({ file: failMatch[1], test: failMatch[2].trim() });
      continue;
    }

    const failSimple = line.match(/\s+×\s+(.+)/);
    if (failSimple && !line.includes('ms')) {
      current = { test: failSimple[1].trim(), error: '' };
      continue;
    }

    if (current && line.trim()) {
      current.error += line.trim() + ' ';
    }

    if (current && line.trim() === '') {
      failed.push(current);
      current = null;
    }
  }

  return failed;
}

if (require.main === module) {
  const testName = process.argv[2];
  const headed = process.argv.includes('--headed');
  executeTests(testName, { headed });
}

module.exports = { executeTests };
