/**
 * UI Contrast Validator
 *
 * Validates color contrast ratios for WCAG 2.1 AA compliance.
 * Ensures text is readable for users with visual impairments.
 *
 * WCAG 2.1 AA Requirements:
 * - Normal text: 4.5:1 minimum
 * - Large text (18pt+ or 14pt+ bold): 3:1 minimum
 * - UI components: 3:1 minimum
 *
 * MORPH-SPEC 3.0 - Sprint 4
 */

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

/**
 * WCAG Contrast Requirements
 */
const WCAG_AA = {
  normalText: 4.5,
  largeText: 3.0,
  uiComponents: 3.0
};

const WCAG_AAA = {
  normalText: 7.0,
  largeText: 4.5,
  uiComponents: 3.0
};

/**
 * UI Contrast Validator Class
 */
export class UIContrastValidator {
  constructor(projectPath = '.', wcagLevel = 'AA') {
    this.projectPath = projectPath;
    this.wcagLevel = wcagLevel;
    this.requirements = wcagLevel === 'AAA' ? WCAG_AAA : WCAG_AA;
  }

  /**
   * Validate all CSS files
   */
  async validateAll() {
    const cssFiles = await glob('wwwroot/**/*.css', {
      cwd: this.projectPath,
      ignore: ['**/node_modules/**', '**/lib/**']
    });

    if (cssFiles.length === 0) {
      return {
        status: 'ok',
        message: 'No CSS files found in wwwroot/'
      };
    }

    const results = [];

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

    return {
      status: results.length === 0 ? 'ok' : 'warning',
      wcagLevel: this.wcagLevel,
      totalFiles: cssFiles.length,
      filesWithIssues: results.length,
      results
    };
  }

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

    // Extract color variables
    const colors = this.extractColors(content);

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

    // Check common color pairs
    const issues = this.checkContrastPairs(colors);

    return {
      colors,
      totalPairs: issues.length,
      issues
    };
  }

  /**
   * Extract colors from CSS
   */
  extractColors(css) {
    const colors = [];

    // Extract CSS variables (--color-name: #hex)
    const cssVarPattern = /--([a-zA-Z0-9-]+):\s*(#[0-9a-fA-F]{3,8}|rgb\([^)]+\)|rgba\([^)]+\))/g;
    let match;

    while ((match = cssVarPattern.exec(css)) !== null) {
      const name = match[1];
      const value = this.normalizeColor(match[2]);

      if (value) {
        colors.push({
          name,
          value,
          type: this.inferColorType(name)
        });
      }
    }

    // Extract regular color declarations
    const colorPattern = /(color|background|background-color|border-color):\s*(#[0-9a-fA-F]{3,8}|rgb\([^)]+\)|rgba\([^)]+\))/g;

    while ((match = colorPattern.exec(css)) !== null) {
      const property = match[1];
      const value = this.normalizeColor(match[2]);

      if (value && !colors.some(c => c.value === value)) {
        colors.push({
          name: `inline-${property}`,
          value,
          type: property.includes('background') ? 'background' : 'text'
        });
      }
    }

    return colors;
  }

  /**
   * Normalize color to hex format
   */
  normalizeColor(colorString) {
    const trimmed = colorString.trim();

    // Already hex
    if (trimmed.startsWith('#')) {
      return this.expandHex(trimmed);
    }

    // rgb/rgba to hex
    if (trimmed.startsWith('rgb')) {
      return this.rgbToHex(trimmed);
    }

    return null;
  }

  /**
   * Expand 3-digit hex to 6-digit
   */
  expandHex(hex) {
    if (hex.length === 4) {
      return '#' + hex[1] + hex[1] + hex[2] + hex[2] + hex[3] + hex[3];
    }
    return hex.substring(0, 7); // Remove alpha if present
  }

  /**
   * Convert rgb/rgba to hex
   */
  rgbToHex(rgb) {
    const match = rgb.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);
    if (!match) return null;

    const r = parseInt(match[1]);
    const g = parseInt(match[2]);
    const b = parseInt(match[3]);

    return '#' + [r, g, b].map(x => {
      const hex = x.toString(16);
      return hex.length === 1 ? '0' + hex : hex;
    }).join('');
  }

  /**
   * Infer color type from variable name
   */
  inferColorType(name) {
    const lowerName = name.toLowerCase();

    if (lowerName.includes('bg') || lowerName.includes('background')) {
      return 'background';
    }

    if (lowerName.includes('text') || lowerName.includes('color') || lowerName.includes('foreground')) {
      return 'text';
    }

    if (lowerName.includes('border')) {
      return 'border';
    }

    return 'unknown';
  }

  /**
   * Check contrast ratios for common color pairs
   */
  checkContrastPairs(colors) {
    const issues = [];

    const backgrounds = colors.filter(c => c.type === 'background');
    const texts = colors.filter(c => c.type === 'text');

    // If no explicit categorization, try all pairs
    if (backgrounds.length === 0 || texts.length === 0) {
      const allColors = colors;
      for (let i = 0; i < allColors.length; i++) {
        for (let j = i + 1; j < allColors.length; j++) {
          const ratio = this.calculateContrastRatio(allColors[i].value, allColors[j].value);

          if (ratio < this.requirements.normalText) {
            issues.push({
              level: 'warning',
              background: allColors[i].name,
              backgroundHex: allColors[i].value,
              foreground: allColors[j].name,
              foregroundHex: allColors[j].value,
              ratio: ratio.toFixed(2),
              required: this.requirements.normalText,
              wcagLevel: this.wcagLevel,
              pass: false,
              message: `Low contrast: ${allColors[i].name} + ${allColors[j].name} = ${ratio.toFixed(2)}:1 (need ${this.requirements.normalText}:1)`
            });
          }
        }
      }
    } else {
      // Check explicit bg + text pairs
      for (const bg of backgrounds) {
        for (const text of texts) {
          const ratio = this.calculateContrastRatio(bg.value, text.value);

          if (ratio < this.requirements.normalText) {
            issues.push({
              level: 'warning',
              background: bg.name,
              backgroundHex: bg.value,
              foreground: text.name,
              foregroundHex: text.value,
              ratio: ratio.toFixed(2),
              required: this.requirements.normalText,
              wcagLevel: this.wcagLevel,
              pass: false,
              message: `Low contrast: ${text.name} on ${bg.name} = ${ratio.toFixed(2)}:1 (need ${this.requirements.normalText}:1 for normal text)`
            });
          }
        }
      }
    }

    return issues;
  }

  /**
   * Calculate contrast ratio (WCAG formula)
   * https://www.w3.org/TR/WCAG21/#dfn-contrast-ratio
   */
  calculateContrastRatio(color1, color2) {
    const l1 = this.getLuminance(color1);
    const l2 = this.getLuminance(color2);

    const lighter = Math.max(l1, l2);
    const darker = Math.min(l1, l2);

    return (lighter + 0.05) / (darker + 0.05);
  }

  /**
   * Get relative luminance
   * https://www.w3.org/TR/WCAG21/#dfn-relative-luminance
   */
  getLuminance(hex) {
    const rgb = this.hexToRgb(hex);
    if (!rgb) return 0;

    const [r, g, b] = rgb.map(val => {
      val = val / 255;
      return val <= 0.03928
        ? val / 12.92
        : Math.pow((val + 0.055) / 1.055, 2.4);
    });

    return 0.2126 * r + 0.7152 * g + 0.0722 * b;
  }

  /**
   * Convert hex to RGB
   */
  hexToRgb(hex) {
    const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
    return result ? [
      parseInt(result[1], 16),
      parseInt(result[2], 16),
      parseInt(result[3], 16)
    ] : null;
  }

  /**
   * Suggest better color
   */
  suggestBetterColor(background, foreground, targetRatio) {
    // Simple algorithm: lighten or darken foreground to meet ratio
    const bgLuminance = this.getLuminance(background);
    const fgRgb = this.hexToRgb(foreground);

    if (!fgRgb) return null;

    // If bg is light, darken fg
    if (bgLuminance > 0.5) {
      const factor = 0.7; // Darken by 30%
      const newRgb = fgRgb.map(v => Math.floor(v * factor));
      return this.rgbToHex(`rgb(${newRgb.join(', ')})`);
    } else {
      // If bg is dark, lighten fg
      const factor = 1.3; // Lighten by 30%
      const newRgb = fgRgb.map(v => Math.min(255, Math.floor(v * factor)));
      return this.rgbToHex(`rgb(${newRgb.join(', ')})`);
    }
  }

  /**
   * Format validation results for console output
   */
  formatResults(results) {
    if (results.status === 'ok') {
      console.log(chalk.green(`✅ All colors meet WCAG ${results.wcagLevel} contrast requirements`));
      return;
    }

    console.log(chalk.yellow(`\n⚠️  Found ${results.filesWithIssues} file(s) with contrast issues (WCAG ${results.wcagLevel}):\n`));

    for (const fileResult of results.results) {
      console.log(chalk.cyan(`📄 ${fileResult.file}`));
      console.log(chalk.gray(`  Colors found: ${fileResult.colors.length}`));

      if (fileResult.issues.length > 0) {
        console.log(chalk.yellow(`  ⚠️  ${fileResult.issues.length} contrast issue(s):`));

        fileResult.issues.forEach(issue => {
          console.log(chalk.yellow(`\n    ${issue.foreground} on ${issue.background}`));
          console.log(chalk.gray(`      Ratio: ${issue.ratio}:1 (need ${issue.required}:1)`));
          console.log(chalk.gray(`      Colors: ${issue.foregroundHex} on ${issue.backgroundHex}`));

          // Suggest fix
          const suggestion = this.suggestBetterColor(issue.backgroundHex, issue.foregroundHex, issue.required);
          if (suggestion) {
            console.log(chalk.green(`      Suggested: ${suggestion}`));
          }
        });
      }

      console.log('');
    }

    console.log(chalk.blue(`\n💡 Tip: Use tools like https://contrast-ratio.com to fine-tune colors\n`));
  }

  /**
   * Get compliance summary
   */
  getComplianceSummary(results) {
    if (results.status === 'ok') {
      return {
        compliant: true,
        wcagLevel: results.wcagLevel,
        filesChecked: results.totalFiles,
        issues: 0
      };
    }

    const totalIssues = results.results.reduce((sum, r) => sum + r.issues.length, 0);

    return {
      compliant: false,
      wcagLevel: results.wcagLevel,
      filesChecked: results.totalFiles,
      filesWithIssues: results.filesWithIssues,
      issues: totalIssues,
      severity: totalIssues > 10 ? 'high' : totalIssues > 5 ? 'medium' : 'low'
    };
  }
}

/**
 * Quick validation function (for imports)
 */
export async function validateContrast(projectPath = '.', options = {}) {
  const wcagLevel = options.wcagLevel || 'AA';
  const validator = new UIContrastValidator(projectPath, wcagLevel);
  const results = await validator.validateAll();

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

  if (options.summary) {
    return {
      ...results,
      summary: validator.getComplianceSummary(results)
    };
  }

  return results;
}
