/**
 * MORPH-SPEC Validation Runner
 *
 * Unified orchestrator that runs the right validators based on the feature's
 * active agents and decisions. Called by task-done before marking tasks complete.
 *
 * MORPH-SPEC 3.0 - Phase A: Active Validation Pipeline
 */

import { existsSync, readFileSync } from 'fs';
import { join } from 'path';
import chalk from 'chalk';
import { loadState } from '../../core/state/state-manager.js';
import { resolveAgentsConfigPath } from '../stacks/stack-resolver.js';

/**
 * Load agent → validators map from agents.json (data-driven)
 * @param {string} projectPath - Root path of the project
 * @returns {Object} Map of agent IDs to validator arrays
 */
function loadAgentValidatorMap(projectPath) {
  try {
    const agentsPath = resolveAgentsConfigPath(projectPath);
    const agentsConfig = JSON.parse(readFileSync(agentsPath, 'utf8'));

    const map = {};
    Object.entries(agentsConfig.agents || {}).forEach(([agentId, agent]) => {
      if (!agentId.startsWith('_comment') && agent.validators && agent.validators.length > 0) {
        map[agentId] = agent.validators;
      }
    });

    return map;
  } catch (err) {
    console.warn(chalk.yellow('⚠️  Failed to load agents.json - no validators will run'));
    console.warn(chalk.gray(`   Run 'morph-spec init' to set up MORPH-SPEC configuration`));
    return {}; // Empty map = honest failure
  }
}

/**
 * Run all relevant validators for a feature
 *
 * @param {string} projectPath - Project root path
 * @param {string} featureName - Feature name from state.json
 * @param {Object} options - Options
 * @param {boolean} options.verbose - Show detailed output
 * @param {string[]} options.validators - Specific validators to run (overrides agent-based detection)
 * @returns {Object} { passed, errors, warnings, issues, summary }
 */
export async function runValidation(projectPath, featureName, options = {}) {
  const result = {
    passed: true,
    errors: 0,
    warnings: 0,
    issues: [],
    summary: '',
    results: {} // Per-validator results for hooks integration
  };

  // Decision constraints support (stub - decision-constraint-loader removed as orphaned code)
  const constraints = {
    enabledValidators: [], // No constraint-based validators
    validators: {} // No validator-specific constraints
  };

  // Determine which validators to run
  const validatorIds = options.validators || detectValidators(featureName, projectPath);

  // Merge constraint-enabled validators
  validatorIds.push(...constraints.enabledValidators);

  if (validatorIds.length === 0) {
    result.summary = 'No validators applicable for this feature';
    return result;
  }

  if (options.verbose) {
    console.log(chalk.gray(`\n  Running validators: ${validatorIds.join(', ')}`));
    if (constraints.enabledValidators.length > 0) {
      console.log(chalk.gray(`  (+ ${constraints.enabledValidators.length} from decisions)`));
    }
  }

  // Always run contract compliance if contracts.cs exists
  const contractsPath = join(projectPath, '.morph/project/outputs', featureName, 'contracts.cs');
  if (existsSync(contractsPath)) {
    validatorIds.push('contract-compliance');
  }

  // Deduplicate
  const uniqueValidators = [...new Set(validatorIds)];

  // Run each validator
  for (const validatorId of uniqueValidators) {
    try {
      // Pass validator-specific constraints from decisions.md
      const validatorOptions = {
        ...options,
        constraints: constraints.validators[validatorId] || {}
      };

      const validatorResult = await runSingleValidator(validatorId, projectPath, featureName, validatorOptions);

      if (validatorResult) {
        result.errors += validatorResult.errors || 0;
        result.warnings += validatorResult.warnings || 0;

        if (validatorResult.issues) {
          result.issues.push(...validatorResult.issues);
        }

        // Store per-validator results for hooks
        result.results[validatorId] = {
          passed: (validatorResult.errors || 0) === 0,
          issues: validatorResult.issues || []
        };
      }
    } catch (err) {
      if (options.verbose) {
        console.log(chalk.yellow(`  ⚠ Validator '${validatorId}' failed: ${err.message}`));
      }
      result.results[validatorId] = {
        passed: false,
        error: err.message
      };
    }
  }

  result.passed = result.errors === 0;
  result.summary = result.passed
    ? `✅ All validations passed (${result.warnings} warnings)`
    : `❌ ${result.errors} error(s), ${result.warnings} warning(s)`;

  return result;
}

/**
 * Detect which validators to run based on feature's active agents
 * @param {string} featureName - Feature name
 * @param {string} projectPath - Project root path (default: current directory)
 * @returns {string[]} Array of validator IDs to run
 */
function detectValidators(featureName, projectPath = '.') {
  const state = loadState(false);
  if (!state || !state.features[featureName]) {
    return ['architecture', 'packages']; // Default validators
  }

  const feature = state.features[featureName];
  const agents = feature.activeAgents || [];
  const validatorSet = new Set();

  // Load agent → validators map from agents.json (data-driven)
  const agentValidatorMap = loadAgentValidatorMap(projectPath);

  for (const agentId of agents) {
    const validators = agentValidatorMap[agentId] || [];
    validators.forEach(v => validatorSet.add(v));
  }

  // Always include architecture and packages as baseline
  validatorSet.add('architecture');
  validatorSet.add('packages');

  return Array.from(validatorSet);
}

/**
 * Run a single validator by ID
 */
async function runSingleValidator(validatorId, projectPath, featureName, options) {
  switch (validatorId) {
    case 'architecture': {
      const { validateArchitecture } = await import('./architecture/architecture-validator.js');
      return await validateArchitecture(projectPath, options);
    }

    case 'packages': {
      const { validatePackages } = await import('./packages/package-validator.js');
      return await validatePackages(projectPath, options);
    }

    case 'contrast': {
      const { validateContrast } = await import('./ui/ui-contrast-validator.js');
      return await validateContrast(projectPath, options);
    }

    case 'blazor': {
      const { validateBlazorPatterns } = await import('./blazor/blazor-validator.js');
      return await validateBlazorPatterns(projectPath, options);
    }

    case 'blazor-concurrency': {
      const { analyzeConcurrency } = await import('./blazor/blazor-concurrency-analyzer.js');
      return await analyzeConcurrency(projectPath, options);
    }

    case 'blazor-state': {
      const { validateState } = await import('./blazor/blazor-state-validator.js');
      return await validateState(projectPath, options);
    }

    case 'css': {
      const { validateCss } = await import('./css/css-validator.js');
      return await validateCss(projectPath, options);
    }

    case 'contract-compliance': {
      const { validateContracts } = await import('./contracts/contract-compliance-validator.js');
      return await validateContracts(projectPath, featureName, options);
    }

    case 'design-system': {
      const { validateDesignSystem } = await import('./design-system/design-system-validator.js');
      return await validateDesignSystem(projectPath, featureName, options);
    }

    default:
      if (options.verbose) {
        console.log(chalk.gray(`  Unknown validator: ${validatorId}`));
      }
      return null;
  }
}

/**
 * Format validation results for console output
 */
export function formatValidationResults(result) {
  if (result.passed && result.warnings === 0) {
    console.log(chalk.green('\n  ✅ All validations passed!\n'));
    return;
  }

  if (result.passed && result.warnings > 0) {
    console.log(chalk.yellow(`\n  ⚠️  Passed with ${result.warnings} warning(s)\n`));
  } else {
    console.log(chalk.red(`\n  ❌ Validation FAILED: ${result.errors} error(s), ${result.warnings} warning(s)\n`));
  }

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

  if (errors.length > 0) {
    console.log(chalk.red.bold('  Errors:'));
    for (const issue of errors.slice(0, 10)) {
      console.log(chalk.red(`    ✗ ${issue.message}`));
      if (issue.file) console.log(chalk.gray(`      ${issue.file}${issue.line ? ':' + issue.line : ''}`));
      if (issue.solution) console.log(chalk.cyan(`      Fix: ${issue.solution}`));
    }
    if (errors.length > 10) {
      console.log(chalk.gray(`    ... and ${errors.length - 10} more errors`));
    }
  }

  if (warnings.length > 0) {
    console.log(chalk.yellow('\n  Warnings:'));
    for (const issue of warnings.slice(0, 5)) {
      console.log(chalk.yellow(`    ⚠ ${issue.message}`));
      if (issue.file) console.log(chalk.gray(`      ${issue.file}`));
    }
    if (warnings.length > 5) {
      console.log(chalk.gray(`    ... and ${warnings.length - 5} more warnings`));
    }
  }

  console.log('');
}
