"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.HTMLReporter = void 0;
exports.generateHTMLReport = generateHTMLReport;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const utils_1 = require("./utils");
/**
 * HTML reporter - generates a styled HTML report
 */
class HTMLReporter {
    constructor(outputDir) {
        this.allResults = [];
        this.outputDir = outputDir;
    }
    onTestFileComplete(file, testFile, results) {
        this.allResults.push({ file, testFile, results });
        const passed = results.filter(r => r.status === 'passed').length;
        const failed = results.filter(r => r.status !== 'passed').length;
        console.log(`  ${passed} passed, ${failed} failed\n`);
    }
    onComplete(allResults) {
        if (!fs.existsSync(this.outputDir)) {
            fs.mkdirSync(this.outputDir, { recursive: true });
        }
        const timestamp = (0, utils_1.getTimestamp)();
        const outputPath = path.join(this.outputDir, `report-${timestamp}.html`);
        const totalTests = allResults.reduce((sum, r) => sum + r.results.length, 0);
        const passed = allResults.reduce((sum, r) => sum + r.results.filter(t => t.status === 'passed').length, 0);
        const failed = allResults.reduce((sum, r) => sum + r.results.filter(t => t.status !== 'passed').length, 0);
        const totalDuration = allResults.reduce((sum, r) => sum + r.results.reduce((s, t) => s + t.duration, 0), 0);
        const html = generateHTMLReport({
            timestamp: new Date().toISOString(),
            summary: { totalTests, passed, failed, duration: totalDuration },
            testFiles: this.allResults,
        });
        fs.writeFileSync(outputPath, html);
        console.log(`HTML report saved to: ${outputPath}`);
    }
}
exports.HTMLReporter = HTMLReporter;
/**
 * Generate HTML report from report data
 */
function generateHTMLReport(data) {
    const { timestamp, summary, testFiles } = data;
    // Separate lifecycle hooks from actual tests for counting
    let actualTests = 0;
    let actualPassed = 0;
    let actualFailed = 0;
    for (const { results } of testFiles) {
        for (const result of results) {
            if (!result.isLifecycle) {
                actualTests++;
                if (result.status === 'passed')
                    actualPassed++;
                else
                    actualFailed++;
            }
        }
    }
    const passRate = actualTests > 0
        ? ((actualPassed / actualTests) * 100).toFixed(1)
        : '0';
    let html = `<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>TestBlocks Report - ${timestamp}</title>
  <style>
${getHTMLStyles()}
  </style>
</head>
<body>
  <div class="container">
    <h1>TestBlocks Test Report</h1>
    <div class="timestamp">Generated: ${new Date(timestamp).toLocaleString()}</div>

    <div class="summary">
      <div class="summary-card">
        <div class="value">${actualTests}</div>
        <div class="label">Total Tests</div>
      </div>
      <div class="summary-card passed">
        <div class="value">${actualPassed}</div>
        <div class="label">Passed</div>
      </div>
      <div class="summary-card failed">
        <div class="value">${actualFailed}</div>
        <div class="label">Failed</div>
      </div>
      <div class="summary-card">
        <div class="value">${passRate}%</div>
        <div class="label">Pass Rate</div>
      </div>
      <div class="summary-card">
        <div class="value">${(summary.duration / 1000).toFixed(1)}s</div>
        <div class="label">Duration</div>
      </div>
    </div>
`;
    for (const { file, testFile, results } of testFiles) {
        html += renderTestFile(file, testFile, results);
    }
    html += `
  </div>
</body>
</html>`;
    return html;
}
/**
 * Render a single test file section
 */
function renderTestFile(file, testFile, results) {
    // Separate lifecycle hooks from tests
    const lifecycleResults = results.filter(r => r.isLifecycle);
    const testResults = results.filter(r => !r.isLifecycle);
    const filePassed = testResults.filter(r => r.status === 'passed').length;
    const fileFailed = testResults.filter(r => r.status !== 'passed').length;
    let html = `
    <div class="test-file">
      <div class="test-file-header">
        <span>${(0, utils_1.escapeHtml)(testFile.name)}</span>
        <span class="test-file-path">${(0, utils_1.escapeHtml)(file)} • ${filePassed} passed, ${fileFailed} failed</span>
      </div>
`;
    // Render lifecycle hooks (beforeAll)
    const beforeAllHooks = lifecycleResults.filter(r => r.lifecycleType === 'beforeAll');
    if (beforeAllHooks.length > 0) {
        html += renderLifecycleSection('Before All', beforeAllHooks);
    }
    // Render actual tests
    for (const result of testResults) {
        html += renderTestCase(result, testFile);
    }
    // Render lifecycle hooks (afterAll)
    const afterAllHooks = lifecycleResults.filter(r => r.lifecycleType === 'afterAll');
    if (afterAllHooks.length > 0) {
        html += renderLifecycleSection('After All', afterAllHooks);
    }
    html += `    </div>\n`;
    return html;
}
/**
 * Render lifecycle section (beforeAll/afterAll)
 */
function renderLifecycleSection(title, hooks) {
    let html = `      <div class="lifecycle-section">\n`;
    html += `        <div class="lifecycle-header">${title}</div>\n`;
    for (const hook of hooks) {
        const iconClass = hook.status === 'passed' ? 'passed' : 'failed';
        html += `        <div class="lifecycle-item">
          <span class="step-dot ${iconClass}"></span>
          <span>${hook.steps.length} steps</span>
          <span style="color: var(--color-text-secondary); font-size: 12px;">${hook.duration}ms</span>
        </div>\n`;
        if (hook.error) {
            html += `        <div class="error-message" style="margin: 4px 0 0 16px;">${(0, utils_1.escapeHtml)(hook.error.message)}</div>\n`;
        }
    }
    html += `      </div>\n`;
    return html;
}
/**
 * Render a single test case
 */
function renderTestCase(result, testFile) {
    const statusIcon = result.status === 'passed' ? '✓' : result.status === 'failed' ? '✗' : '○';
    const testId = `test-${Math.random().toString(36).substr(2, 9)}`;
    let html = `
      <div class="test-case ${result.status}">
        <div class="test-case-header">
          <div class="status-icon ${result.status}">${statusIcon}</div>
          <div class="test-name">${(0, utils_1.escapeHtml)(result.testName)}</div>
          <div class="test-duration">${result.duration}ms</div>
          <button class="steps-toggle" onclick="document.getElementById('${testId}').classList.toggle('open'); this.textContent = this.textContent.includes('▶') ? '▼ ${result.steps.length} steps' : '▶ ${result.steps.length} steps'">
            ▶ ${result.steps.length} steps
          </button>
        </div>
`;
    if (result.error) {
        html += `        <div class="error-message">${(0, utils_1.escapeHtml)(result.error.message)}</div>\n`;
    }
    html += `        <div class="steps-list" id="${testId}">\n`;
    for (const step of result.steps) {
        html += renderStep(step);
    }
    html += `        </div>\n`;
    html += `      </div>\n`;
    return html;
}
/**
 * Render a single step
 */
function renderStep(step) {
    const summary = (0, utils_1.getStepSummary)(step.stepType, step.output);
    const isApiRequest = (0, utils_1.isApiRequestStep)(step.stepType);
    const response = step.output;
    let html = `          <div class="step">
            <div class="step-header">
              <span class="step-dot ${step.status}"></span>
              <span class="step-type">${(0, utils_1.escapeHtml)((0, utils_1.formatStepType)(step.stepType))}</span>
              ${summary ? `<span class="step-summary">${(0, utils_1.escapeHtml)(summary)}</span>` : ''}
              <span class="step-duration">${step.duration}ms</span>
            </div>
`;
    // Show step error
    if (step.error) {
        html += `            <div class="step-error">${(0, utils_1.escapeHtml)(step.error.message)}</div>\n`;
    }
    // Show API response details
    if (isApiRequest && response) {
        html += `            <div class="step-details">\n`;
        if (response.status) {
            const statusClass = response.status >= 200 && response.status < 300 ? 'success'
                : response.status >= 400 && response.status < 500 ? 'client-error'
                    : response.status >= 500 ? 'server-error' : '';
            html += `              <div class="response-status">
                <span class="response-label">Status:</span>
                <span class="status-code ${statusClass}">${response.status}</span>
              </div>\n`;
        }
        if (response.headers && Object.keys(response.headers).length > 0) {
            html += `              <details class="response-section">
                <summary>Headers</summary>
                <pre class="response-pre">${(0, utils_1.escapeHtml)(JSON.stringify(response.headers, null, 2))}</pre>
              </details>\n`;
        }
        if (response.body !== undefined) {
            const bodyStr = typeof response.body === 'string'
                ? response.body
                : JSON.stringify(response.body, null, 2);
            html += `              <details class="response-section">
                <summary>Body</summary>
                <pre class="response-pre">${(0, utils_1.escapeHtml)(bodyStr)}</pre>
              </details>\n`;
        }
        html += `            </div>\n`;
    }
    else if (step.output !== undefined && step.output !== null && !step.screenshot) {
        // Show generic output for non-API steps
        const outputStr = (0, utils_1.formatStepOutput)(step.output, step.stepType);
        if (outputStr) {
            html += `            <div class="step-details">
              <pre class="response-pre">${(0, utils_1.escapeHtml)(outputStr)}</pre>
            </div>\n`;
        }
    }
    // Show stack trace for errors
    if (step.error?.stack) {
        html += `            <details class="response-section">
              <summary>Stack Trace</summary>
              <pre class="response-pre stack-trace">${(0, utils_1.escapeHtml)(step.error.stack)}</pre>
            </details>\n`;
    }
    // Show screenshot
    if (step.screenshot) {
        html += `            <img class="screenshot" src="${step.screenshot}" alt="Screenshot at failure" onclick="window.open(this.src, '_blank')">\n`;
    }
    html += `          </div>\n`;
    return html;
}
/**
 * Get CSS styles for HTML report
 */
function getHTMLStyles() {
    return `    :root {
      --color-passed: #22c55e;
      --color-failed: #ef4444;
      --color-skipped: #f59e0b;
      --color-bg: #f8fafc;
      --color-surface: #ffffff;
      --color-border: #e2e8f0;
      --color-text: #334155;
      --color-text-secondary: #64748b;
    }
    * { box-sizing: border-box; margin: 0; padding: 0; }
    body {
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
      background: var(--color-bg);
      color: var(--color-text);
      line-height: 1.5;
      padding: 24px;
    }
    .container { max-width: 1200px; margin: 0 auto; }
    h1 { font-size: 24px; font-weight: 600; margin-bottom: 8px; }
    .timestamp { color: var(--color-text-secondary); font-size: 14px; margin-bottom: 24px; }
    .summary {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
      gap: 16px;
      margin-bottom: 32px;
    }
    .summary-card {
      background: var(--color-surface);
      border: 1px solid var(--color-border);
      border-radius: 8px;
      padding: 16px;
      text-align: center;
    }
    .summary-card .value { font-size: 32px; font-weight: 700; }
    .summary-card .label { font-size: 14px; color: var(--color-text-secondary); }
    .summary-card.passed .value { color: var(--color-passed); }
    .summary-card.failed .value { color: var(--color-failed); }
    .test-file {
      background: var(--color-surface);
      border: 1px solid var(--color-border);
      border-radius: 8px;
      margin-bottom: 16px;
      overflow: hidden;
    }
    .test-file-header {
      padding: 16px;
      border-bottom: 1px solid var(--color-border);
      font-weight: 600;
      display: flex;
      justify-content: space-between;
      align-items: center;
    }
    .test-file-path { font-size: 12px; color: var(--color-text-secondary); font-weight: normal; }
    .lifecycle-section {
      background: #f1f5f9;
      padding: 8px 16px;
      border-bottom: 1px solid var(--color-border);
    }
    .lifecycle-header {
      font-size: 12px;
      font-weight: 600;
      color: var(--color-text-secondary);
      text-transform: uppercase;
      letter-spacing: 0.5px;
      margin-bottom: 4px;
    }
    .lifecycle-item {
      display: flex;
      align-items: center;
      gap: 8px;
      padding: 4px 0;
      font-size: 13px;
    }
    .test-case {
      padding: 12px 16px;
      border-bottom: 1px solid var(--color-border);
    }
    .test-case:last-child { border-bottom: none; }
    .test-case.passed { border-left: 3px solid var(--color-passed); }
    .test-case.failed { border-left: 3px solid var(--color-failed); }
    .test-case.skipped { border-left: 3px solid var(--color-skipped); }
    .test-case-header {
      display: flex;
      align-items: center;
      gap: 12px;
    }
    .status-icon {
      width: 20px;
      height: 20px;
      border-radius: 50%;
      display: flex;
      align-items: center;
      justify-content: center;
      font-size: 12px;
      color: white;
      flex-shrink: 0;
    }
    .status-icon.passed { background: var(--color-passed); }
    .status-icon.failed { background: var(--color-failed); }
    .status-icon.skipped { background: var(--color-skipped); }
    .test-name { flex: 1; font-weight: 500; }
    .test-duration { color: var(--color-text-secondary); font-size: 14px; }
    .error-message {
      background: #fef2f2;
      border: 1px solid #fecaca;
      border-radius: 4px;
      padding: 8px 12px;
      margin: 8px 0 0 32px;
      font-size: 13px;
      color: #991b1b;
    }
    .steps-toggle {
      background: none;
      border: none;
      color: var(--color-text-secondary);
      cursor: pointer;
      font-size: 12px;
      padding: 4px 8px;
    }
    .steps-toggle:hover { text-decoration: underline; }
    .steps-list {
      display: none;
      margin: 12px 0 0 32px;
      border-left: 2px solid var(--color-border);
      padding-left: 16px;
    }
    .steps-list.open { display: block; }
    .step {
      padding: 8px 0;
      border-bottom: 1px solid #f1f5f9;
    }
    .step:last-child { border-bottom: none; }
    .step-header {
      display: flex;
      align-items: center;
      gap: 8px;
      font-size: 13px;
    }
    .step-dot {
      width: 8px;
      height: 8px;
      border-radius: 50%;
      flex-shrink: 0;
    }
    .step-dot.passed { background: var(--color-passed); }
    .step-dot.failed { background: var(--color-failed); }
    .step-type { font-weight: 500; }
    .step-summary { color: var(--color-text-secondary); }
    .step-duration { color: var(--color-text-secondary); font-size: 12px; margin-left: auto; }
    .step-details {
      margin-top: 8px;
      padding: 8px;
      background: #f8fafc;
      border-radius: 4px;
      font-size: 12px;
    }
    .step-error {
      color: #991b1b;
      margin-top: 4px;
    }
    .response-status {
      display: flex;
      align-items: center;
      gap: 8px;
      margin-bottom: 8px;
    }
    .response-label { font-weight: 500; }
    .status-code { font-family: monospace; padding: 2px 6px; border-radius: 4px; }
    .status-code.success { background: #dcfce7; color: #166534; }
    .status-code.client-error { background: #fef3c7; color: #92400e; }
    .status-code.server-error { background: #fef2f2; color: #991b1b; }
    .response-section {
      margin-top: 8px;
    }
    .response-section summary {
      cursor: pointer;
      font-weight: 500;
      font-size: 12px;
      color: var(--color-text-secondary);
    }
    .response-pre {
      background: #1e293b;
      color: #e2e8f0;
      padding: 12px;
      border-radius: 4px;
      overflow-x: auto;
      font-family: monospace;
      font-size: 12px;
      margin-top: 4px;
      white-space: pre-wrap;
      word-break: break-word;
    }
    .stack-trace {
      background: #fef2f2;
      color: #991b1b;
    }
    .screenshot {
      max-width: 100%;
      max-height: 300px;
      border: 1px solid var(--color-border);
      border-radius: 4px;
      margin-top: 8px;
      cursor: pointer;
    }`;
}
