/**
 * Package Compatibility Validator
 *
 * Validates NuGet package versions against .NET compatibility matrix.
 * Critical for .NET 10 migration - prevents runtime errors.
 *
 * MORPH-SPEC 3.0 - Sprint 4
 */

import { readFileSync } from 'fs';
import { glob } from 'glob';
import chalk from 'chalk';

/**
 * Compatibility matrix for .NET versions
 * Source: framework/standards/dotnet10-compatibility.md
 */
const COMPATIBILITY_MATRIX = {
  'MudBlazor': {
    9: '6.21.0',
    10: '8.15.0',
    breaking: true,
    reason: 'Theme API changed: Palette → PaletteLight/PaletteDark',
    migration: 'See framework/standards/dotnet10-compatibility.md'
  },
  'Microsoft.FluentUI.AspNetCore.Components': {
    9: '4.10.0',
    10: '5.0.0',
    breaking: false,
    reason: 'API stable, version bump for .NET 10 compatibility'
  },
  'Hangfire.AspNetCore': {
    9: '1.8.0',
    10: '1.8.22',
    breaking: false,
    reason: '.NET 10 runtime compatibility'
  },
  'Hangfire.Core': {
    9: '1.8.0',
    10: '1.8.22',
    breaking: false
  },
  'Microsoft.EntityFrameworkCore': {
    9: '9.0.0',
    10: '10.0.0',
    breaking: true,
    reason: 'EF Core 10 required for .NET 10 runtime'
  },
  'Microsoft.EntityFrameworkCore.SqlServer': {
    9: '9.0.0',
    10: '10.0.0',
    breaking: true
  },
  'Microsoft.EntityFrameworkCore.Design': {
    9: '9.0.0',
    10: '10.0.0',
    breaking: true
  },
  'Microsoft.AspNetCore.Components.WebAssembly': {
    9: '9.0.0',
    10: '10.0.0',
    breaking: false
  },
  'Microsoft.SemanticKernel': {
    9: null, // Not compatible with .NET 10
    10: null,
    deprecated: true,
    replacement: 'Microsoft.Extensions.AI.Abstractions',
    reason: 'Use Microsoft Agent Framework for .NET 10'
  }
};

/**
 * Package Validator Class
 */
export class PackageValidator {
  constructor(projectPath = '.') {
    this.projectPath = projectPath;
  }

  /**
   * Validate all .csproj files in project
   */
  async validateAll() {
    const csprojFiles = await glob('**/*.csproj', {
      cwd: this.projectPath,
      ignore: ['**/obj/**', '**/bin/**', '**/node_modules/**']
    });

    if (csprojFiles.length === 0) {
      return {
        status: 'ok',
        message: 'No .csproj files found'
      };
    }

    const results = [];

    for (const file of csprojFiles) {
      const result = await this.validateFile(file);
      if (result.issues.length > 0) {
        results.push({ file, ...result });
      }
    }

    return {
      status: results.length === 0 ? 'ok' : 'error',
      totalFiles: csprojFiles.length,
      filesWithIssues: results.length,
      results
    };
  }

  /**
   * Validate single .csproj file
   */
  async validateFile(filePath) {
    const content = readFileSync(filePath, 'utf-8');

    // Extract .NET version
    const tfmMatch = content.match(/<TargetFramework>(.*?)<\/TargetFramework>/);
    if (!tfmMatch) {
      return {
        dotnetVersion: null,
        issues: [{
          level: 'warning',
          message: 'Could not detect TargetFramework'
        }]
      };
    }

    const targetFramework = tfmMatch[1];
    const dotnetVersion = parseInt(targetFramework.replace('net', ''));

    if (dotnetVersion < 9) {
      return {
        dotnetVersion,
        issues: [{
          level: 'info',
          message: `Using .NET ${dotnetVersion} (no validation rules for versions < 9)`
        }]
      };
    }

    // Extract package references
    const packages = this.extractPackages(content);

    // Validate each package
    const issues = [];

    for (const pkg of packages) {
      const issue = this.validatePackage(pkg, dotnetVersion);
      if (issue) {
        issues.push(issue);
      }
    }

    return {
      dotnetVersion,
      packages,
      issues
    };
  }

  /**
   * Extract package references from .csproj content
   */
  extractPackages(csprojContent) {
    const packages = [];
    const packageRegex = /<PackageReference\s+Include="([^"]+)"\s+Version="([^"]+)"/g;
    let match;

    while ((match = packageRegex.exec(csprojContent)) !== null) {
      packages.push({
        name: match[1],
        version: match[2]
      });
    }

    return packages;
  }

  /**
   * Validate single package against compatibility matrix
   */
  validatePackage(pkg, dotnetVersion) {
    const rule = COMPATIBILITY_MATRIX[pkg.name];

    if (!rule) {
      // Package not in matrix - no validation
      return null;
    }

    // Check if deprecated
    if (rule.deprecated) {
      return {
        level: 'error',
        package: pkg.name,
        installedVersion: pkg.version,
        message: `${pkg.name} is deprecated for .NET ${dotnetVersion}`,
        reason: rule.reason,
        replacement: rule.replacement,
        autoFix: false
      };
    }

    // Check if version is sufficient
    const requiredVersion = rule[dotnetVersion];

    if (!requiredVersion) {
      return {
        level: 'info',
        package: pkg.name,
        installedVersion: pkg.version,
        message: `No validation rule for ${pkg.name} on .NET ${dotnetVersion}`
      };
    }

    const comparison = this.compareVersions(pkg.version, requiredVersion);

    if (comparison < 0) {
      return {
        level: rule.breaking ? 'error' : 'warning',
        package: pkg.name,
        installedVersion: pkg.version,
        requiredVersion,
        message: `${pkg.name} ${pkg.version} is incompatible with .NET ${dotnetVersion} (requires ≥${requiredVersion})`,
        reason: rule.reason,
        migration: rule.migration,
        breaking: rule.breaking,
        autoFix: {
          available: true,
          command: `dotnet add package ${pkg.name} --version ${requiredVersion}`
        }
      };
    }

    return null; // Package is compatible
  }

  /**
   * Compare semantic versions
   * Returns: -1 (v1 < v2), 0 (equal), 1 (v1 > v2)
   */
  compareVersions(v1, v2) {
    const parts1 = v1.split('.').map(p => parseInt(p.replace(/[^0-9]/g, '')) || 0);
    const parts2 = v2.split('.').map(p => parseInt(p.replace(/[^0-9]/g, '')) || 0);

    const maxLength = Math.max(parts1.length, parts2.length);

    for (let i = 0; i < maxLength; i++) {
      const p1 = parts1[i] || 0;
      const p2 = parts2[i] || 0;

      if (p1 < p2) return -1;
      if (p1 > p2) return 1;
    }

    return 0;
  }

  /**
   * Format validation results for console output
   */
  formatResults(results) {
    if (results.status === 'ok') {
      console.log(chalk.green('✅ All packages are compatible'));
      return;
    }

    console.log(chalk.yellow(`\n⚠️  Found issues in ${results.filesWithIssues} of ${results.totalFiles} .csproj files:\n`));

    for (const fileResult of results.results) {
      console.log(chalk.cyan(`📄 ${fileResult.file} (.NET ${fileResult.dotnetVersion})`));

      const errors = fileResult.issues.filter(i => i.level === 'error');
      const warnings = fileResult.issues.filter(i => i.level === 'warning');
      const infos = fileResult.issues.filter(i => i.level === 'info');

      if (errors.length > 0) {
        console.log(chalk.red(`\n  ❌ ${errors.length} error(s):`));
        errors.forEach(e => {
          console.log(chalk.red(`    - ${e.message}`));
          if (e.reason) {
            console.log(chalk.gray(`      Reason: ${e.reason}`));
          }
          if (e.replacement) {
            console.log(chalk.cyan(`      Use instead: ${e.replacement}`));
          }
          if (e.autoFix?.available) {
            console.log(chalk.green(`      Auto-fix: ${e.autoFix.command}`));
          }
        });
      }

      if (warnings.length > 0) {
        console.log(chalk.yellow(`\n  ⚠️  ${warnings.length} warning(s):`));
        warnings.forEach(w => {
          console.log(chalk.yellow(`    - ${w.message}`));
          if (w.autoFix?.available) {
            console.log(chalk.green(`      Auto-fix: ${w.autoFix.command}`));
          }
        });
      }

      if (infos.length > 0) {
        console.log(chalk.gray(`\n  ℹ️  ${infos.length} info(s):`));
        infos.forEach(i => {
          console.log(chalk.gray(`    - ${i.message}`));
        });
      }

      console.log('');
    }
  }

  /**
   * Auto-fix compatible issues
   */
  async autoFix(results) {
    const { execSync } = await import('child_process');
    let fixedCount = 0;

    for (const fileResult of results.results) {
      for (const issue of fileResult.issues) {
        if (issue.autoFix?.available) {
          try {
            console.log(chalk.cyan(`🔧 Fixing ${issue.package}...`));
            execSync(issue.autoFix.command, { stdio: 'inherit' });
            fixedCount++;
            console.log(chalk.green(`  ✅ Fixed: ${issue.package} → ${issue.requiredVersion}`));
          } catch (error) {
            console.log(chalk.red(`  ❌ Failed to fix ${issue.package}: ${error.message}`));
          }
        }
      }
    }

    return fixedCount;
  }
}

/**
 * Quick validation function (for imports)
 */
export async function validatePackages(projectPath = '.', options = {}) {
  const validator = new PackageValidator(projectPath);
  const results = await validator.validateAll();

  if (options.verbose) {
    validator.formatResults(results);
  }

  if (options.autoFix && results.status !== 'ok') {
    const fixedCount = await validator.autoFix(results);
    console.log(chalk.green(`\n✅ Auto-fixed ${fixedCount} issue(s)`));
  }

  return results;
}
