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

export class TextReporter {
  report(coverage: CoverageReport, outputDir: string): void {
    const outputPath = join(outputDir, 'coverage.txt');
    const content = this.generateText(coverage);
    
    writeFileSync(outputPath, content);
    console.log(`Text coverage report written to: ${outputPath}`);
  }

  private generateText(coverage: CoverageReport): string {
    const lines: string[] = [];
    const width = 80;
    
    // Header
    lines.push('='.repeat(width));
    lines.push(this.center('STOCK BOT COVERAGE REPORT', width));
    lines.push('='.repeat(width));
    lines.push('');
    lines.push(`Generated: ${new Date(coverage.timestamp).toLocaleString()}`);
    lines.push('');
    
    // Overall Summary
    lines.push('-'.repeat(width));
    lines.push('OVERALL COVERAGE');
    lines.push('-'.repeat(width));
    lines.push('');
    
    const overall = coverage.overall;
    lines.push(this.formatMetricLine('Lines', overall.lines, coverage.config.thresholds.lines));
    lines.push(this.formatMetricLine('Functions', overall.functions, coverage.config.thresholds.functions));
    lines.push(this.formatMetricLine('Branches', overall.branches, coverage.config.thresholds.branches));
    lines.push(this.formatMetricLine('Statements', overall.statements, coverage.config.thresholds.statements));
    lines.push('');
    
    // Package Summary
    if (coverage.packages.length > 0) {
      lines.push('-'.repeat(width));
      lines.push('PACKAGE COVERAGE');
      lines.push('-'.repeat(width));
      lines.push('');
      
      // Table header
      lines.push(this.padRight('Package', 30) + 
                 this.padLeft('Lines', 10) + 
                 this.padLeft('Funcs', 10) + 
                 this.padLeft('Branch', 10) + 
                 this.padLeft('Stmts', 10));
      lines.push('-'.repeat(70));
      
      // Package rows
      for (const pkg of coverage.packages) {
        lines.push(
          this.padRight(pkg.name, 30) +
          this.padLeft(this.formatPercent(pkg.lines.percentage), 10) +
          this.padLeft(this.formatPercent(pkg.functions.percentage), 10) +
          this.padLeft(this.formatPercent(pkg.branches.percentage), 10) +
          this.padLeft(this.formatPercent(pkg.statements.percentage), 10)
        );
      }
      
      lines.push('-'.repeat(70));
      lines.push(
        this.padRight('TOTAL', 30) +
        this.padLeft(this.formatPercent(overall.lines.percentage), 10) +
        this.padLeft(this.formatPercent(overall.functions.percentage), 10) +
        this.padLeft(this.formatPercent(overall.branches.percentage), 10) +
        this.padLeft(this.formatPercent(overall.statements.percentage), 10)
      );
      lines.push('');
      
      // Detailed breakdowns
      lines.push('-'.repeat(width));
      lines.push('PACKAGE DETAILS');
      lines.push('-'.repeat(width));
      
      for (const pkg of coverage.packages) {
        lines.push('');
        lines.push(`Package: ${pkg.name}`);
        lines.push(`Path: ${pkg.path}`);
        lines.push('');
        
        // Coverage details
        lines.push(`  Lines......: ${this.formatMetricDetail(pkg.lines)}`);
        lines.push(`  Functions..: ${this.formatMetricDetail(pkg.functions)}`);
        lines.push(`  Branches...: ${this.formatMetricDetail(pkg.branches)}`);
        lines.push(`  Statements.: ${this.formatMetricDetail(pkg.statements)}`);
        lines.push('');
        
        // File list (top 5 least covered)
        if (pkg.files.length > 0) {
          lines.push('  Least covered files:');
          const sortedFiles = [...pkg.files]
            .sort((a, b) => a.lines.percentage - b.lines.percentage)
            .slice(0, 5);
          
          for (const file of sortedFiles) {
            const shortPath = this.shortenPath(file.path);
            lines.push(`    ${this.padRight(shortPath, 40)} ${this.formatPercent(file.lines.percentage)}`);
          }
        }
      }
    }
    
    // Threshold Summary
    lines.push('');
    lines.push('-'.repeat(width));
    lines.push('THRESHOLD SUMMARY');
    lines.push('-'.repeat(width));
    lines.push('');
    
    const thresholds = coverage.config.thresholds;
    const results = this.checkThresholds(coverage);
    
    lines.push(`Status: ${results.passed ? 'PASS' : 'FAIL'}`);
    lines.push('');
    
    if (results.failures.length > 0) {
      lines.push('Failed thresholds:');
      for (const failure of results.failures) {
        lines.push(`  - ${failure.metric}: ${failure.actual.toFixed(1)}% < ${failure.expected}% (required)`);
      }
    } else {
      lines.push('All coverage thresholds met!');
    }
    
    lines.push('');
    lines.push('='.repeat(width));
    
    return lines.join('\n');
  }

  private formatMetricLine(name: string, metric: CoverageMetric, threshold?: number): string {
    const status = this.getStatus(metric.percentage, threshold);
    return `${this.padRight(name + ':', 15)} ${this.padRight(this.formatPercent(metric.percentage), 10)} ` +
           `(${metric.covered}/${metric.total}) ${status}`;
  }

  private formatMetricDetail(metric: CoverageMetric): string {
    return `${this.formatPercent(metric.percentage)} (${metric.covered}/${metric.total})`;
  }

  private formatPercent(percentage: number): string {
    return `${percentage.toFixed(1)}%`;
  }

  private getStatus(percentage: number, threshold?: number): string {
    if (!threshold) return '';
    
    if (percentage >= threshold) return '[PASS]';
    if (percentage >= threshold * 0.9) return '[WARN]';
    return '[FAIL]';
  }

  private checkThresholds(coverage: CoverageReport): {
    passed: boolean;
    failures: Array<{ metric: string; expected: number; actual: number }>;
  } {
    const failures: Array<{ metric: string; expected: number; actual: number }> = [];
    const { thresholds } = coverage.config;
    const { overall } = coverage;

    const metrics = ['lines', 'functions', 'branches', 'statements'] as const;
    for (const metric of metrics) {
      const threshold = thresholds[metric];
      if (threshold && overall[metric].percentage < threshold) {
        failures.push({
          metric: metric.charAt(0).toUpperCase() + metric.slice(1),
          expected: threshold,
          actual: overall[metric].percentage,
        });
      }
    }

    return {
      passed: failures.length === 0,
      failures,
    };
  }

  private center(text: string, width: number): string {
    const padding = Math.max(0, width - text.length);
    const leftPad = Math.floor(padding / 2);
    const rightPad = padding - leftPad;
    return ' '.repeat(leftPad) + text + ' '.repeat(rightPad);
  }

  private padRight(text: string, width: number): string {
    return text + ' '.repeat(Math.max(0, width - text.length));
  }

  private padLeft(text: string, width: number): string {
    return ' '.repeat(Math.max(0, width - text.length)) + text;
  }

  private shortenPath(path: string): string {
    const parts = path.split('/');
    if (parts.length > 4) {
      return '.../' + parts.slice(-3).join('/');
    }
    return path;
  }
}