/**
 * Architecture Pattern Validator
 *
 * Validates architectural patterns and detects anti-patterns in Blazor apps.
 * Prevents common DI mistakes, lifecycle issues, and coupling problems.
 *
 * Source: framework/standards/blazor-pitfalls.md
 * MORPH-SPEC 3.0 - Sprint 4
 */

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

/**
 * Architecture Validation Rules
 */
const VALIDATION_RULES = {
  // DI Anti-patterns
  applicationLayerInjection: {
    pattern: /\[Inject\].*I\w+(Command|Query|Handler)/,
    level: 'error',
    message: 'Blazor component injecting Application layer directly (use HttpClient pattern)',
    reference: 'framework/standards/blazor-pitfalls.md#di-pattern',
    solution: 'Replace with HttpClient and call API endpoint'
  },

  dbContextInjection: {
    pattern: /\[Inject\].*DbContext/,
    level: 'error',
    message: 'Blazor component injecting DbContext directly (use HttpClient → API → Handler)',
    reference: 'framework/standards/blazor-pitfalls.md#di-pattern',
    solution: 'Use HttpClient to call API, which then uses handler/repository'
  },

  repositoryInjection: {
    pattern: /\[Inject\].*I\w+Repository/,
    level: 'warning',
    message: 'Blazor component injecting Repository directly (prefer HttpClient → API pattern)',
    reference: 'framework/standards/blazor-pitfalls.md#di-pattern',
    solution: 'Consider using HttpClient for better separation'
  },

  // Lifecycle Issues
  jsRuntimeInOnInitialized: {
    pattern: /OnInitialized(?:Async)?\(\)[\s\S]*?JSRuntime/,
    level: 'error',
    message: 'JSRuntime called in OnInitialized (must use OnAfterRenderAsync)',
    reference: 'framework/standards/blazor-lifecycle.md#jsruntime',
    solution: 'Move JSRuntime calls to OnAfterRenderAsync with firstRender guard'
  },

  asyncVoidEventHandler: {
    pattern: /async\s+void\s+On\w+/,
    level: 'warning',
    message: 'Async void event handler (prefer async Task for error handling)',
    reference: 'framework/standards/blazor-lifecycle.md#async',
    solution: 'Change return type to async Task'
  },

  // Program.cs Issues
  missingHttpClient: {
    pattern: /AddHttpClient\(\)/,
    inverse: true, // Rule triggers if pattern NOT found
    level: 'error',
    message: 'AddHttpClient() missing in Program.cs',
    reference: 'framework/standards/program-cs-checklist.md',
    solution: 'Add builder.Services.AddHttpClient() before builder.Build()'
  },

  missingHttpContextAccessor: {
    pattern: /AddHttpContextAccessor\(\)/,
    inverse: true,
    level: 'warning',
    message: 'AddHttpContextAccessor() missing (needed for auth contexts)',
    reference: 'framework/standards/program-cs-checklist.md',
    solution: 'Add builder.Services.AddHttpContextAccessor()'
  },

  missingStaticFiles: {
    pattern: /app\.UseStaticFiles\(\)/,
    inverse: true,
    level: 'error',
    message: 'app.UseStaticFiles() missing (CSS/JS won\'t load)',
    reference: 'framework/standards/program-cs-checklist.md',
    solution: 'Add app.UseStaticFiles() before app.UseAntiforgery()'
  },

  wrongMiddlewareOrder: {
    // Custom validator (checked in validateProgramCs)
    level: 'error',
    message: 'app.UseStaticFiles() must come BEFORE app.UseAntiforgery()',
    reference: 'framework/standards/program-cs-checklist.md',
    solution: 'Reorder: UseStaticFiles() → UseAntiforgery() → UseAuthorization()'
  },

  // File Upload Issues
  wrongFileType: {
    pattern: /IFormFile.*Blazor/,
    level: 'error',
    message: 'Using IFormFile in Blazor (use IBrowserFile)',
    reference: 'framework/standards/blazor-pitfalls.md#file-upload',
    solution: 'Use IBrowserFile in Blazor components, convert to IFormFile in API'
  },

  // State Management
  inMemoryState: {
    pattern: /private\s+(?:static\s+)?List<.*>.*=\s*new/,
    level: 'warning',
    message: 'In-memory state in component (consider using proper state management)',
    reference: 'framework/standards/blazor-pitfalls.md#state',
    solution: 'Use Fluxor, or move state to API/database'
  }
};

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

  /**
   * Validate all Blazor files
   */
  async validateAll() {
    const results = {
      blazor: await this.validateBlazorComponents(),
      programCs: await this.validateProgramCs()
    };

    const allIssues = [
      ...results.blazor.issues,
      ...results.programCs.issues
    ];

    return {
      status: allIssues.length === 0 ? 'ok' : 'error',
      totalIssues: allIssues.length,
      errors: allIssues.filter(i => i.level === 'error').length,
      warnings: allIssues.filter(i => i.level === 'warning').length,
      results
    };
  }

  /**
   * Validate Blazor components (.razor, .razor.cs)
   */
  async validateBlazorComponents() {
    const files = await glob('**/*.razor?(.cs)', {
      cwd: this.projectPath,
      ignore: ['**/obj/**', '**/bin/**', '**/node_modules/**']
    });

    if (files.length === 0) {
      return {
        totalFiles: 0,
        issues: []
      };
    }

    const issues = [];

    for (const file of files) {
      const content = readFileSync(file, 'utf-8');
      const fileIssues = this.validateBlazorFile(file, content);
      issues.push(...fileIssues);
    }

    return {
      totalFiles: files.length,
      filesWithIssues: new Set(issues.map(i => i.file)).size,
      issues
    };
  }

  /**
   * Validate single Blazor file
   */
  validateBlazorFile(filePath, content) {
    const issues = [];

    // Check each rule
    for (const [ruleId, rule] of Object.entries(VALIDATION_RULES)) {
      // Skip Program.cs rules
      if (['missingHttpClient', 'missingHttpContextAccessor', 'missingStaticFiles', 'wrongMiddlewareOrder'].includes(ruleId)) {
        continue;
      }

      const match = rule.pattern.test(content);

      if (match) {
        issues.push({
          file: filePath,
          rule: ruleId,
          level: rule.level,
          message: rule.message,
          reference: rule.reference,
          solution: rule.solution,
          lineNumber: this.findLineNumber(content, rule.pattern)
        });
      }
    }

    return issues;
  }

  /**
   * Validate Program.cs
   */
  async validateProgramCs() {
    const programCsPath = 'Program.cs';

    try {
      const content = readFileSync(programCsPath, 'utf-8');
      return this.validateProgramCsContent(content);
    } catch (error) {
      // No Program.cs found (not a Blazor project)
      return {
        exists: false,
        issues: []
      };
    }
  }

  /**
   * Validate Program.cs content
   */
  validateProgramCsContent(content) {
    const issues = [];

    // Check missing patterns (inverse rules)
    const inverseRules = ['missingHttpClient', 'missingHttpContextAccessor', 'missingStaticFiles'];

    for (const ruleId of inverseRules) {
      const rule = VALIDATION_RULES[ruleId];
      const found = rule.pattern.test(content);

      if (!found) {
        issues.push({
          file: 'Program.cs',
          rule: ruleId,
          level: rule.level,
          message: rule.message,
          reference: rule.reference,
          solution: rule.solution
        });
      }
    }

    // Check middleware order
    const staticFilesIndex = content.indexOf('UseStaticFiles');
    const antiforgeryIndex = content.indexOf('UseAntiforgery');

    if (staticFilesIndex > 0 && antiforgeryIndex > 0 && staticFilesIndex > antiforgeryIndex) {
      const rule = VALIDATION_RULES.wrongMiddlewareOrder;
      issues.push({
        file: 'Program.cs',
        rule: 'wrongMiddlewareOrder',
        level: rule.level,
        message: rule.message,
        reference: rule.reference,
        solution: rule.solution,
        lineNumber: this.findLineNumber(content, /UseStaticFiles/)
      });
    }

    return {
      exists: true,
      issues
    };
  }

  /**
   * Find line number of pattern match
   */
  findLineNumber(content, pattern) {
    const lines = content.split('\n');
    for (let i = 0; i < lines.length; i++) {
      if (pattern.test(lines[i])) {
        return i + 1;
      }
    }
    return null;
  }

  /**
   * Format validation results for console output
   */
  formatResults(results) {
    if (results.status === 'ok') {
      console.log(chalk.green('✅ No architecture issues found'));
      return;
    }

    console.log(chalk.yellow(`\n⚠️  Found ${results.totalIssues} architecture issue(s):\n`));

    // Group by file
    const issuesByFile = new Map();
    const allIssues = [
      ...results.results.blazor.issues,
      ...results.results.programCs.issues
    ];

    for (const issue of allIssues) {
      if (!issuesByFile.has(issue.file)) {
        issuesByFile.set(issue.file, []);
      }
      issuesByFile.get(issue.file).push(issue);
    }

    // Display by file
    for (const [file, issues] of issuesByFile) {
      console.log(chalk.cyan(`\n📄 ${file}`));

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

      if (errors.length > 0) {
        console.log(chalk.red(`  ❌ ${errors.length} error(s):`));
        errors.forEach(e => {
          const location = e.lineNumber ? `:${e.lineNumber}` : '';
          console.log(chalk.red(`    ${file}${location}`));
          console.log(chalk.red(`    - ${e.message}`));
          console.log(chalk.gray(`      Solution: ${e.solution}`));
          console.log(chalk.blue(`      Reference: ${e.reference}`));
        });
      }

      if (warnings.length > 0) {
        console.log(chalk.yellow(`  ⚠️  ${warnings.length} warning(s):`));
        warnings.forEach(w => {
          const location = w.lineNumber ? `:${w.lineNumber}` : '';
          console.log(chalk.yellow(`    ${file}${location}`));
          console.log(chalk.yellow(`    - ${w.message}`));
          console.log(chalk.gray(`      Solution: ${w.solution}`));
        });
      }
    }

    console.log('');
  }

  /**
   * Get suggested fixes
   */
  getSuggestedFixes(results) {
    const fixes = [];
    const allIssues = [
      ...results.results.blazor.issues,
      ...results.results.programCs.issues
    ];

    for (const issue of allIssues) {
      fixes.push({
        file: issue.file,
        line: issue.lineNumber,
        issue: issue.message,
        solution: issue.solution,
        reference: issue.reference,
        severity: issue.level
      });
    }

    return fixes;
  }
}

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

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

  if (options.fixes) {
    const fixes = validator.getSuggestedFixes(results);
    return { ...results, fixes };
  }

  return results;
}
