import { writeFileSync, mkdirSync } from 'fs';
import { join } from 'path';
import type { CoverageReport, PackageCoverage } from '../types';

export class HtmlCompactReporter {
  report(coverage: CoverageReport, outputDir: string): void {
    const htmlDir = join(outputDir, 'html');
    mkdirSync(htmlDir, { recursive: true });

    const html = this.generateHTML(coverage);
    writeFileSync(join(htmlDir, 'index.html'), html);
    
    this.writeStyles(htmlDir);
    
    console.log(`HTML coverage report written to: ${join(htmlDir, 'index.html')}`);
  }

  private generateHTML(report: CoverageReport): string {
    const timestamp = new Date(report.timestamp).toLocaleString();
    const { overall, packages } = report;

    // Sort packages by line coverage descending
    const sortedPackages = [...packages].sort((a, b) => b.lines.percentage - a.lines.percentage);
    
    // Filter packages with 0% coverage separately
    const coveredPackages = sortedPackages.filter(p => p.lines.percentage > 0);
    const uncoveredPackages = sortedPackages.filter(p => p.lines.percentage === 0);

    return `<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Coverage Report</title>
    <link rel="stylesheet" href="styles.css">
</head>
<body>
    <div class="container-compact">
        <div class="header">
            <div class="title">Coverage Report</div>
            <div class="overall">
                <span class="label">Overall:</span>
                ${this.formatMetric('L', overall.lines)}
                ${this.formatMetric('F', overall.functions)}
                ${this.formatMetric('B', overall.branches)}
                ${this.formatMetric('S', overall.statements)}
            </div>
            <div class="timestamp">${timestamp}</div>
        </div>

        <table class="coverage-table">
            <thead>
                <tr>
                    <th>Package</th>
                    <th>L</th>
                    <th>F</th>
                    <th>B</th>
                    <th>S</th>
                    <th>Details</th>
                </tr>
            </thead>
            <tbody>
                ${coveredPackages.map(pkg => this.generatePackageRow(pkg, report.config.thresholds)).join('\n')}
                ${uncoveredPackages.length > 0 ? `
                <tr class="separator">
                    <td colspan="6">Uncovered Packages (${uncoveredPackages.length})</td>
                </tr>
                ${uncoveredPackages.map(pkg => this.generateUncoveredRow(pkg)).join('\n')}
                ` : ''}
            </tbody>
        </table>
    </div>
    <script>
    function toggleDetails(pkg) {
        const row = document.getElementById('details-' + pkg);
        const btn = document.getElementById('btn-' + pkg);
        if (row.style.display === 'none' || !row.style.display) {
            row.style.display = 'table-row';
            btn.textContent = '−';
        } else {
            row.style.display = 'none';
            btn.textContent = '+';
        }
    }
    </script>
</body>
</html>`;
  }

  private formatMetric(label: string, metric: any): string {
    const percentage = metric.percentage.toFixed(1);
    const threshold = this.getThreshold(label);
    const cssClass = this.getCoverageClass(metric.percentage, threshold);
    return `<span class="metric ${cssClass}">${label}: ${percentage}%</span>`;
  }

  private getThreshold(label: string): number {
    const map: Record<string, number> = { L: 80, F: 80, B: 80, S: 80 };
    return map[label] || 80;
  }

  private getCoverageClass(percentage: number, threshold: number = 80): string {
    if (percentage >= threshold) return 'good';
    if (percentage >= threshold * 0.9) return 'warn';
    return 'bad';
  }

  private generatePackageRow(pkg: PackageCoverage, thresholds: any): string {
    const id = pkg.name.replace(/[@/]/g, '-');
    const hasFiles = pkg.files && pkg.files.length > 0;
    
    return `
      <tr>
        <td class="pkg-name">${pkg.name}</td>
        <td class="${this.getCoverageClass(pkg.lines.percentage, thresholds.lines)}">${pkg.lines.percentage.toFixed(1)}</td>
        <td class="${this.getCoverageClass(pkg.functions.percentage, thresholds.functions)}">${pkg.functions.percentage.toFixed(1)}</td>
        <td class="${this.getCoverageClass(pkg.branches.percentage, thresholds.branches)}">${pkg.branches.percentage.toFixed(1)}</td>
        <td class="${this.getCoverageClass(pkg.statements.percentage, thresholds.statements)}">${pkg.statements.percentage.toFixed(1)}</td>
        <td class="details-btn">
          ${hasFiles ? `<button id="btn-${id}" onclick="toggleDetails('${id}')">+</button>` : ''}
        </td>
      </tr>
      ${hasFiles ? `
      <tr id="details-${id}" class="details-row" style="display:none">
        <td colspan="6">
          <div class="file-details">
            <table class="files-table">
              <thead>
                <tr>
                  <th>File</th>
                  <th>L</th>
                  <th>F</th>
                  <th>B</th>
                  <th>S</th>
                </tr>
              </thead>
              <tbody>
                ${pkg.files.map(f => this.generateFileRow(f)).join('\n')}
              </tbody>
            </table>
          </div>
        </td>
      </tr>` : ''}`;
  }

  private generateUncoveredRow(pkg: PackageCoverage): string {
    return `
      <tr class="uncovered">
        <td class="pkg-name">${pkg.name}</td>
        <td colspan="4" class="zero">0.0</td>
        <td></td>
      </tr>`;
  }

  private generateFileRow(file: any): string {
    const shortPath = this.shortenPath(file.path);
    return `
      <tr>
        <td class="file-path" title="${file.path}">${shortPath}</td>
        <td class="${this.getCoverageClass(file.lines.percentage)}">${file.lines.percentage.toFixed(1)}</td>
        <td class="${this.getCoverageClass(file.functions.percentage)}">${file.functions.percentage.toFixed(1)}</td>
        <td class="${this.getCoverageClass(file.branches.percentage)}">${file.branches.percentage.toFixed(1)}</td>
        <td class="${this.getCoverageClass(file.statements.percentage)}">${file.statements.percentage.toFixed(1)}</td>
      </tr>`;
  }

  private shortenPath(path: string): string {
    const parts = path.split('/');
    const relevantParts = [];
    let foundSrc = false;
    
    for (let i = parts.length - 1; i >= 0; i--) {
      relevantParts.unshift(parts[i]);
      if (parts[i] === 'src' || parts[i] === 'test') {
        foundSrc = true;
        if (i > 0) relevantParts.unshift(parts[i - 1]);
        break;
      }
    }
    
    return foundSrc ? relevantParts.join('/') : parts.slice(-3).join('/');
  }

  private writeStyles(htmlDir: string): void {
    const css = `
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, monospace;
  font-size: 13px;
  line-height: 1.4;
  color: #333;
  background: #f5f5f5;
}

.container-compact {
  max-width: 1000px;
  margin: 20px auto;
  background: white;
  border-radius: 8px;
  box-shadow: 0 2px 4px rgba(0,0,0,0.1);
  overflow: hidden;
}

.header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 16px;
  background: #2d3748;
  color: white;
}

.title {
  font-size: 16px;
  font-weight: 600;
}

.overall {
  display: flex;
  gap: 12px;
  align-items: center;
}

.overall .label {
  opacity: 0.8;
}

.metric {
  padding: 2px 8px;
  border-radius: 3px;
  font-weight: 500;
  font-size: 12px;
}

.metric.good { background: #48bb78; color: white; }
.metric.warn { background: #ed8936; color: white; }
.metric.bad { background: #f56565; color: white; }

.timestamp {
  font-size: 11px;
  opacity: 0.7;
}

.coverage-table {
  width: 100%;
  border-collapse: collapse;
}

.coverage-table th {
  background: #f7fafc;
  padding: 8px 12px;
  text-align: left;
  font-weight: 600;
  font-size: 12px;
  border-bottom: 1px solid #e2e8f0;
  color: #4a5568;
}

.coverage-table td {
  padding: 6px 12px;
  border-bottom: 1px solid #f0f0f0;
}

.coverage-table tbody tr:hover {
  background: #f9f9f9;
}

.pkg-name {
  font-weight: 500;
  color: #2d3748;
}

.good { color: #22863a; font-weight: 600; }
.warn { color: #b08800; font-weight: 600; }
.bad { color: #dc3545; font-weight: 600; }
.zero { color: #999; text-align: center; }

.details-btn button {
  width: 20px;
  height: 20px;
  border: 1px solid #cbd5e0;
  background: white;
  border-radius: 3px;
  cursor: pointer;
  font-size: 14px;
  line-height: 1;
  color: #4a5568;
}

.details-btn button:hover {
  background: #e2e8f0;
}

.separator td {
  background: #f7fafc;
  font-weight: 600;
  color: #718096;
  padding: 4px 12px;
  font-size: 11px;
}

.uncovered {
  opacity: 0.6;
}

.details-row td {
  padding: 0;
  background: #f9f9f9;
}

.file-details {
  padding: 12px 24px;
}

.files-table {
  width: 100%;
  font-size: 12px;
}

.files-table th {
  background: #edf2f7;
  padding: 4px 8px;
  font-weight: 500;
}

.files-table td {
  padding: 3px 8px;
  border-bottom: 1px solid #e2e8f0;
}

.file-path {
  font-family: 'Courier New', monospace;
  font-size: 11px;
  color: #4a5568;
}

/* Compact mode for smaller screens */
@media (max-width: 768px) {
  .header {
    flex-direction: column;
    gap: 8px;
    align-items: flex-start;
  }
  
  .coverage-table {
    font-size: 12px;
  }
  
  .coverage-table th,
  .coverage-table td {
    padding: 4px 8px;
  }
}
`;

    writeFileSync(join(htmlDir, 'styles.css'), css);
  }
}