const { DIRS, ensureDir, readMarkdown, writeMarkdown, log } = require('./utils');
const context = require('./context-manager');
const path = require('path');
const fs = require('fs');

function generateReport(runId) {
  const runDir = runId
    ? path.join(DIRS.testResults, runId)
    : findLatestRunDir();

  if (!runDir || !fs.existsSync(runDir)) {
    log('REPORTER', 'No execution results found');
    return null;
  }

  log('REPORTER', `Generating report from: test-results/${path.basename(runDir)}`);

  const report = {
    runId: path.basename(runDir),
    generatedAt: new Date().toISOString(),
    execution: null,
    healing: null,
    testFiles: [],
    stats: { total: 0, passed: 0, failed: 0, healed: 0 },
  };

  const resultPath = path.join(runDir, 'execution-result.json');
  if (fs.existsSync(resultPath)) {
    report.execution = JSON.parse(fs.readFileSync(resultPath, 'utf-8'));
    report.storyName = report.execution.story || null;
  }

  if (report.storyName) {
    const trace = context.getTraceability();
    const storyTrace = trace.stories[report.storyName];
    if (storyTrace) report.traceability = storyTrace;
  }

  const healingPath = path.join(runDir, 'healing-report.json');
  if (fs.existsSync(healingPath)) {
    report.healing = JSON.parse(fs.readFileSync(healingPath, 'utf-8'));
  }

  report.testFiles = listTestFilesWithStatus();

  if (report.execution) {
    report.stats.total = (report.execution.failedTests || []).length + (report.execution.passedTests || []).length;
    if (report.execution.success) {
      report.stats.passed = report.stats.total;
    } else {
      report.stats.failed = (report.execution.failedTests || []).length;
    }
  }

  if (report.healing) {
    report.stats.healed = report.healing.totalHealed || 0;
    report.stats.failed = Math.max(0, report.stats.failed - report.stats.healed);
    report.stats.passed += report.stats.healed;
  }

  const md = generateMarkdownReport(report);
  const reportPath = path.join(runDir, 'final-test-report.md');
  writeMarkdown(reportPath, md);

  log('REPORTER', `Report saved: test-results/${path.basename(runDir)}/final-test-report.md`);

  if (report.storyName) {
    context.phaseComplete('report', report.storyName);
  }

  return {
    reportPath,
    stats: report.stats,
    report,
  };
}

function listTestFilesWithStatus() {
  const testsDir = DIRS.tests;
  if (!fs.existsSync(testsDir)) return [];

  return fs.readdirSync(testsDir)
    .filter(f => f.endsWith('.spec.ts'))
    .map(f => {
      const content = fs.readFileSync(path.join(testsDir, f), 'utf-8');
      const testCount = (content.match(/test\(/g) || []).length;
      return { file: f, tests: testCount };
    });
}

function generateMarkdownReport(report) {
  const { runId, stats, execution, healing, testFiles, storyName, traceability } = report;

  let md = `# AI QA Execution Report

**Run ID**: ${runId}
**Generated**: ${report.generatedAt}
**Status**: ${stats.failed === 0 ? '✅ PASSED' : '❌ FAILED'}
${storyName ? `**Story**: ${storyName}` : ''}

---

## Traceability

${storyName && traceability ? `| Story | Plan | Spec | Status |\n|---|---|---|---|\n| ${storyName} | ${traceability.plan || '—'} | ${traceability.spec || '—'} | ${traceability.status || '—'} |\n` : `No traceability data available.\n`}
${traceability && traceability.runs && traceability.runs.length > 1 ? `\n**Run History:** ${traceability.runs.length} run(s)\n` : ''}

---

## Executive Summary

| Metric | Value |
|--------|-------|
| Total Test Cases | ${stats.total || 'N/A'} |
| Passed | ${stats.passed} |
| Failed | ${stats.failed} |
| Healed (Self-Healing) | ${stats.healed} |
| Pass Rate | ${stats.total > 0 ? Math.round((stats.passed / stats.total) * 100) : 'N/A'}% |
| Duration | ${execution ? `${(execution.duration / 1000).toFixed(1)}s` : 'N/A'} |

---

## Test Coverage

| Test File | Scenarios |
|-----------|-----------|
`;

  testFiles.forEach(tf => {
    md += `| ${tf.file} | ${tf.tests} |\n`;
  });

  if (execution && !execution.success && execution.failedTests && execution.failedTests.length > 0) {
    md += `\n## Failed Tests (Before Healing)\n\n`;
    md += `| # | Test | Error |\n`;
    md += `|---|------|-------|\n`;
    execution.failedTests.forEach((ft, i) => {
      const error = ft.error ? ft.error.substring(0, 100) : 'N/A';
      md += `| ${i + 1} | ${ft.test || ft.file} | ${error} |\n`;
    });
  }

  if (healing) {
    md += `\n## Self-Healing Activity\n\n`;

    if (healing.healingAttempts && healing.healingAttempts.length > 0) {
      md += `| Attempt | Healed | Remaining |\n`;
      md += `|---------|--------|-----------|\n`;
      healing.healingAttempts.forEach(a => {
        md += `| ${a.attempt} | ${a.healed.length} | ${a.stillFailing.length} |\n`;
      });
    }

    if (healing.totalHealed > 0) {
      md += `\n**Healed Tests:**\n\n`;
      healing.healingAttempts.forEach(a => {
        a.healed.forEach(h => {
          md += `- \`${h.test || h.file}\` → Healed via "${h.strategy}" (attempt ${h.healedOnAttempt})\n`;
        });
      });
    }

    if (healing.totalRemaining > 0) {
      md += `\n**Still Failing (requires AI investigation):**\n\n`;
      md += `These tests could not be auto-healed and need manual inspection:\n\n`;
      (healing.healingAttempts[healing.healingAttempts.length - 1]?.stillFailing || []).forEach(ft => {
        md += `- \`${ft.test || ft.file}\`\n`;
      });
    }
  }

  md += `\n## Artifacts\n\n`;
  md += `- **Execution Log**: \`test-results/${runId}/execution-output.json\`\n`;
  md += `- **Execution Result**: \`test-results/${runId}/execution-result.json\`\n`;

  if (healing) {
    md += `- **Healing Report**: \`test-results/${runId}/healing-report.json\`\n`;
  }

  md += `- **Allure Results**: \`allure-results/\`\n`;
  md += `- **Screenshots**: \`test-results/\`\n`;

  md += `\n---\n\n`;
  md += `> **Next Step**: If failures remain, use the Playwright Test Healer agent to investigate.\n`;
  md += `> Run: \`node ai-qa-workflow.js heal ${runId}\`\n`;

  return md;
}

function findLatestRunDir() {
  if (!fs.existsSync(DIRS.testResults)) return null;

  const dirs = fs.readdirSync(DIRS.testResults)
    .map(d => ({ name: d, path: path.join(DIRS.testResults, d) }))
    .filter(d => fs.statSync(d.path).isDirectory())
    .sort((a, b) => fs.statSync(b.path).mtimeMs - fs.statSync(a.path).mtimeMs);

  return dirs.length > 0 ? dirs[0].path : null;
}

if (require.main === module) {
  const runId = process.argv[2];
  const result = generateReport(runId);
  if (result) {
    console.log(`\n  Report: ${result.reportPath}`);
    console.log(`  Passed: ${result.stats.passed} | Failed: ${result.stats.failed} | Healed: ${result.stats.healed}`);
  }
}

module.exports = { generateReport };
