#!/usr/bin/env tsx
/**
 * V8 to Istanbul Coverage Converter
 *
 * Converts Playwright's V8 coverage format to Istanbul format
 * so it can be merged with Jest coverage data.
 *
 * Usage:
 *   npm run coverage:convert
 *   # or directly:
 *   tsx scripts/convert-v8-to-istanbul.ts
 *
 * Input:
 *   - V8 coverage files in: ./coverage-e2e/raw/*.json
 *   - Generated by Playwright's page.coverage API
 *
 * Output:
 *   - Istanbul coverage in: ./coverage-e2e/.nyc_output/e2e-coverage.json
 *   - Ready to merge with Jest coverage
 *
 * Prerequisites:
 *   - npm install -D v8-to-istanbul
 *   - E2E tests must collect coverage using page.coverage API
 */

import fs from 'fs/promises';
import path from 'path';
import { fileURLToPath } from 'url';

// V8 coverage entry format
interface V8CoverageEntry {
  url: string;
  scriptId: string;
  source?: string;
  functions: Array<{
    functionName: string;
    ranges: Array<{
      startOffset: number;
      endOffset: number;
      count: number;
    }>;
    isBlockCoverage: boolean;
  }>;
}

// Istanbul coverage format
interface IstanbulCoverage {
  [filePath: string]: {
    path: string;
    statementMap: Record<string, any>;
    fnMap: Record<string, any>;
    branchMap: Record<string, any>;
    s: Record<string, number>;
    f: Record<string, number>;
    b: Record<string, number[]>;
  };
}

async function convertV8ToIstanbul() {
  console.log('\n🔄 Converting V8 Coverage to Istanbul Format...\n');

  const rawDir = path.join(process.cwd(), 'coverage-e2e/raw');
  const outputDir = path.join(process.cwd(), 'coverage-e2e/.nyc_output');

  // Check if raw directory exists
  try {
    await fs.access(rawDir);
  } catch {
    console.log('❌ No V8 coverage found at:', rawDir);
    console.log('\nℹ️  To generate V8 coverage:');
    console.log('   1. Add coverage helpers to E2E tests (see E2E_COVERAGE_GUIDE.md)');
    console.log('   2. Run: E2E_COVERAGE=true npm run test:e2e');
    console.log('\nℹ️  Alternatively, use Istanbul instrumentation (see guide)');
    return;
  }

  // Create output directory
  await fs.mkdir(outputDir, { recursive: true });

  // Check for v8-to-istanbul dependency
  let v8toIstanbul: any;
  try {
    // Dynamic import to handle both scenarios (installed vs not installed)
    const module = await import('v8-to-istanbul');
    v8toIstanbul = module.default || module;
  } catch (error) {
    console.log('❌ v8-to-istanbul not installed\n');
    console.log('📦 Install it with:');
    console.log('   npm install -D v8-to-istanbul\n');
    console.log('⚠️  Note: V8 coverage approach requires this dependency.');
    console.log('   Alternatively, use Istanbul instrumentation (no extra deps needed).\n');
    process.exit(1);
  }

  // Read all V8 coverage files
  const files = await fs.readdir(rawDir);
  const jsonFiles = files.filter(f => f.endsWith('.json'));

  if (jsonFiles.length === 0) {
    console.log('⚠️  No coverage files found in:', rawDir);
    console.log('\nRun E2E tests with coverage enabled:');
    console.log('   E2E_COVERAGE=true npm run test:e2e\n');
    return;
  }

  console.log(`📁 Found ${jsonFiles.length} V8 coverage file(s)\n`);

  const istanbulCoverage: IstanbulCoverage = {};
  const projectRoot = process.cwd();
  let totalConverted = 0;
  let totalSkipped = 0;

  // Process each V8 coverage file
  for (const file of jsonFiles) {
    const filePath = path.join(rawDir, file);
    console.log(`📄 Processing: ${file}`);

    try {
      const content = await fs.readFile(filePath, 'utf-8');
      const v8Coverage: V8CoverageEntry[] = JSON.parse(content);

      for (const entry of v8Coverage) {
        try {
          // Skip non-source files
          if (
            !entry.url.startsWith('http://localhost') &&
            !entry.url.startsWith('file://')
          ) {
            continue;
          }

          // Skip node_modules, .next, and other irrelevant files
          if (
            entry.url.includes('node_modules') ||
            entry.url.includes('/.next/') ||
            entry.url.includes('/_next/') ||
            entry.url.includes('/webpack/')
          ) {
            totalSkipped++;
            continue;
          }

          // Convert URL to file path
          let sourcePath: string;
          if (entry.url.startsWith('file://')) {
            sourcePath = fileURLToPath(entry.url);
          } else {
            // HTTP URL - extract path
            const url = new URL(entry.url);
            // Try to map to source file
            sourcePath = path.join(projectRoot, 'src', url.pathname);
          }

          // Skip if not in src/
          if (!sourcePath.includes('/src/')) {
            totalSkipped++;
            continue;
          }

          // Verify file exists
          try {
            await fs.access(sourcePath);
          } catch {
            totalSkipped++;
            continue;
          }

          // Convert using v8-to-istanbul
          const converter = v8toIstanbul(sourcePath);
          await converter.load();
          converter.applyCoverage(entry.functions);
          const converted = converter.toIstanbul();

          // Merge into combined coverage
          Object.assign(istanbulCoverage, converted);
          totalConverted++;

        } catch (error: any) {
          console.log(`   ⚠️  Skipped ${entry.url}: ${error.message}`);
          totalSkipped++;
        }
      }
    } catch (error: any) {
      console.log(`   ❌ Failed to process ${file}: ${error.message}`);
    }
  }

  // Write Istanbul coverage
  const outputPath = path.join(outputDir, 'e2e-coverage.json');
  await fs.writeFile(outputPath, JSON.stringify(istanbulCoverage, null, 2));

  console.log('\n' + '='.repeat(70));
  console.log('✅ Conversion Complete');
  console.log('='.repeat(70));
  console.log(`\n  Files converted:  ${totalConverted}`);
  console.log(`  Files skipped:    ${totalSkipped}`);
  console.log(`  Output location:  ${outputPath}\n`);

  if (totalConverted === 0) {
    console.log('⚠️  No files were converted. Possible reasons:');
    console.log('   • V8 coverage doesn\'t contain source files from src/');
    console.log('   • Coverage was collected for build artifacts instead of source');
    console.log('   • Source maps are not correctly configured\n');
    console.log('💡 Consider using Istanbul instrumentation instead (see guide)\n');
  } else {
    console.log('✅ Ready to merge with Jest coverage:');
    console.log('   npm run coverage:merge\n');
  }
}

// Run the conversion
convertV8ToIstanbul().catch((error) => {
  console.error('\n❌ Error converting coverage:', error);
  process.exit(1);
});
