#!/usr/bin/env node

// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc.
// SPDX-License-Identifier: BUSL-1.1
// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.

/**
 * Migration tool to convert composite SPDX headers to canonical multi-line format
 */

import { readdirSync, statSync, readFileSync, writeFileSync } from 'fs';
import path from 'path';

// Current composite format
const COMPOSITE_HEADER =
  '// SPDX-License-Identifier: BUSL-1.1; Copyright (c) 2025 Social Connect Labs, Inc.; Licensed under BUSL-1.1 (see LICENSE); Apache-2.0 from 2029-06-11';

// New canonical format
const CANONICAL_HEADER_LINES = [
  '// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc.',
  '// SPDX-License-Identifier: BUSL-1.1',
  '// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.',
];

function findFiles(
  dir,
  extensions = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs'],
) {
  const files = [];

  function traverse(currentDir) {
    const items = readdirSync(currentDir);

    for (const item of items) {
      const fullPath = path.join(currentDir, item);
      const stat = statSync(fullPath);

      if (stat.isDirectory()) {
        // Skip common directories
        if (
          ![
            'node_modules',
            '.git',
            'dist',
            'build',
            'coverage',
            'ios',
            'android',
            '.next',
            '.turbo',
            'target',
            'Pods',
            '.tamagui',
          ].includes(item)
        ) {
          traverse(fullPath);
        }
      } else if (extensions.some(ext => item.endsWith(ext))) {
        files.push(fullPath);
      }
    }
  }

  traverse(dir);
  return files;
}

function analyzeFile(filePath) {
  const content = readFileSync(filePath, 'utf8');
  const lines = content.split('\n');

  let i = 0;
  // Skip shebang if present
  if (lines[i]?.startsWith('#!')) i++;
  // Skip leading blank lines
  while (i < lines.length && lines[i].trim() === '') i++;

  const currentLine = lines[i];

  if (currentLine === COMPOSITE_HEADER) {
    return {
      type: 'composite',
      headerIndex: i,
      needsMigration: true,
    };
  } else if (currentLine === CANONICAL_HEADER_LINES[0]) {
    // Check if it's the full canonical format
    const isCanonical =
      lines[i + 1] === CANONICAL_HEADER_LINES[1] &&
      lines[i + 2] === CANONICAL_HEADER_LINES[2];
    return {
      type: isCanonical ? 'canonical' : 'partial_canonical',
      headerIndex: i,
      needsMigration: !isCanonical,
    };
  } else if (currentLine?.includes('SPDX-License-Identifier')) {
    return {
      type: 'other_spdx',
      headerIndex: i,
      needsMigration: false,
    };
  } else {
    return {
      type: 'no_header',
      headerIndex: -1,
      needsMigration: false,
    };
  }
}

function migrateFile(filePath, dryRun = false) {
  const content = readFileSync(filePath, 'utf8');
  const lines = content.split('\n');
  const analysis = analyzeFile(filePath);

  if (!analysis.needsMigration) {
    return { success: false, reason: 'No migration needed' };
  }

  if (analysis.type === 'composite') {
    // Replace the composite line with canonical lines
    lines.splice(analysis.headerIndex, 1, ...CANONICAL_HEADER_LINES);

    if (!dryRun) {
      const newContent = lines.join('\n');
      writeFileSync(filePath, newContent, 'utf8');
    }

    return { success: true, reason: 'Migrated composite to canonical' };
  }

  return { success: false, reason: 'Unknown migration path' };
}

function removeHeaderFromFile(filePath, dryRun = false) {
  const content = readFileSync(filePath, 'utf8');
  const lines = content.split('\n');
  const analysis = analyzeFile(filePath);

  if (analysis.headerIndex === -1) {
    return { success: false, reason: 'No header found' };
  }

  if (analysis.type === 'composite') {
    // Remove the composite header line
    lines.splice(analysis.headerIndex, 1);

    // Also remove the following empty line if it exists
    if (lines[analysis.headerIndex] === '') {
      lines.splice(analysis.headerIndex, 1);
    }

    if (!dryRun) {
      const newContent = lines.join('\n');
      writeFileSync(filePath, newContent, 'utf8');
    }

    return { success: true, reason: 'Removed composite header' };
  } else if (analysis.type === 'canonical') {
    // Remove all 3 canonical header lines
    lines.splice(analysis.headerIndex, 3);

    // Also remove the following empty line if it exists
    if (lines[analysis.headerIndex] === '') {
      lines.splice(analysis.headerIndex, 1);
    }

    if (!dryRun) {
      const newContent = lines.join('\n');
      writeFileSync(filePath, newContent, 'utf8');
    }

    return { success: true, reason: 'Removed canonical header' };
  }

  return { success: false, reason: 'Unknown header type' };
}

function generateReport(projectRoot) {
  const files = findFiles(projectRoot);
  const report = {
    composite: [],
    canonical: [],
    partial_canonical: [],
    other_spdx: [],
    no_header: [],
    total: files.length,
  };

  for (const file of files) {
    const analysis = analyzeFile(file);
    report[analysis.type].push({
      file: path.relative(projectRoot, file),
      needsMigration: analysis.needsMigration,
    });
  }

  return report;
}

function main() {
  const args = process.argv.slice(2);
  const command = args[0];
  const isDryRun = args.includes('--dry-run');
  const isVerbose = args.includes('--verbose');
  const projectRoot =
    args.find(arg => !arg.startsWith('--') && arg !== command) || process.cwd();

  switch (command) {
    case 'analyze':
    case 'report': {
      const report = generateReport(projectRoot);

      console.log('📊 License Header Migration Analysis\n');
      console.log(`Total files analyzed: ${report.total}\n`);

      console.log(
        `🔶 Composite headers (need migration): ${report.composite.length}`,
      );
      if (report.composite.length > 0 && isVerbose) {
        report.composite
          .slice(0, 10)
          .forEach(item => console.log(`   ${item.file}`));
        if (report.composite.length > 10) {
          console.log(`   ... and ${report.composite.length - 10} more`);
        }
        console.log();
      }

      console.log(`✅ Canonical headers: ${report.canonical.length}`);
      console.log(
        `⚠️  Partial canonical headers: ${report.partial_canonical.length}`,
      );
      console.log(`ℹ️  Other SPDX headers: ${report.other_spdx.length}`);
      console.log(`❌ No headers: ${report.no_header.length}`);

      if (report.composite.length > 0) {
        console.log(
          `\n🚀 Ready to migrate ${report.composite.length} files with composite headers`,
        );
        console.log(
          'Run: node scripts/migrate-license-headers.mjs migrate [--dry-run] to proceed',
        );
      } else {
        console.log(
          '\n✨ No migration needed - all headers are already in canonical format!',
        );
      }

      break;
    }

    case 'migrate': {
      const files = findFiles(projectRoot);
      const results = { migrated: 0, skipped: 0, errors: 0 };

      console.log(
        `🔄 ${isDryRun ? 'DRY RUN: ' : ''}Migrating license headers...\n`,
      );

      for (const file of files) {
        try {
          const result = migrateFile(file, isDryRun);
          if (result.success) {
            results.migrated++;
            console.log(
              `✅ ${isDryRun ? '[DRY RUN] ' : ''}Migrated: ${path.relative(projectRoot, file)}`,
            );
          } else {
            results.skipped++;
            if (isVerbose) {
              console.log(
                `⏭️  Skipped: ${path.relative(projectRoot, file)} (${result.reason})`,
              );
            }
          }
        } catch (error) {
          results.errors++;
          console.error(`❌ Error processing ${file}: ${error.message}`);
        }
      }

      console.log(`\n📊 Migration Summary:`);
      console.log(`   Migrated: ${results.migrated}`);
      console.log(`   Skipped: ${results.skipped}`);
      console.log(`   Errors: ${results.errors}`);

      if (isDryRun && results.migrated > 0) {
        console.log('\n🚀 Run without --dry-run to apply changes');
      }

      break;
    }

    case 'remove': {
      const files = findFiles(projectRoot);
      const results = { removed: 0, skipped: 0, errors: 0 };

      console.log(
        `🗑️ ${isDryRun ? 'DRY RUN: ' : ''}Removing license headers...\n`,
      );

      for (const file of files) {
        try {
          const result = removeHeaderFromFile(file, isDryRun);
          if (result.success) {
            results.removed++;
            console.log(
              `✅ ${isDryRun ? '[DRY RUN] ' : ''}Removed: ${path.relative(projectRoot, file)}`,
            );
          } else {
            results.skipped++;
            if (isVerbose) {
              console.log(
                `⏭️  Skipped: ${path.relative(projectRoot, file)} (${result.reason})`,
              );
            }
          }
        } catch (error) {
          results.errors++;
          console.error(`❌ Error processing ${file}: ${error.message}`);
        }
      }

      console.log(`\n📊 Removal Summary:`);
      console.log(`   Removed: ${results.removed}`);
      console.log(`   Skipped: ${results.skipped}`);
      console.log(`   Errors: ${results.errors}`);

      if (isDryRun && results.removed > 0) {
        console.log('\n🚀 Run without --dry-run to apply changes');
      }

      break;
    }

    case 'migrate-single': {
      const targetFile = args[1];
      if (!targetFile) {
        console.error('❌ Please specify a file to migrate');
        process.exit(1);
      }

      const fullPath = path.resolve(targetFile);
      const result = migrateFile(fullPath, isDryRun);

      if (result.success) {
        console.log(
          `✅ ${isDryRun ? '[DRY RUN] ' : ''}Migrated: ${targetFile}`,
        );
      } else {
        console.log(`⏭️  ${targetFile}: ${result.reason}`);
      }

      break;
    }

    default:
      console.log(`Usage: node scripts/migrate-license-headers.mjs <command> [options]

Commands:
  analyze, report     Generate analysis report of current header formats
  migrate            Migrate all composite headers to canonical format
  remove             Remove license headers from files
  migrate-single     Migrate a single file

Options:
  --dry-run          Show what would be changed without making changes
  --verbose          Show detailed output

Examples:
  node scripts/migrate-license-headers.mjs analyze --verbose
  node scripts/migrate-license-headers.mjs migrate --dry-run
  node scripts/migrate-license-headers.mjs remove common --dry-run
  node scripts/migrate-license-headers.mjs migrate packages/mobile-sdk-alpha
  node scripts/migrate-license-headers.mjs migrate-single src/index.ts --dry-run
`);
      break;
  }
}

if (import.meta.url === `file://${process.argv[1]}`) {
  main();
}
