/**
 * TokenValidator - Validates CSS and HTML for design token compliance
 *
 * Ensures all color, spacing, typography, and other design values use
 * valid CSS custom properties instead of hardcoded values.
 *
 * Usage:
 *   const validator = new TokenValidator();
 *   const report = validator.validateCSS(cssText);
 *   console.log(report.violations); // Array of violations found
 */

class TokenValidator {
  constructor() {
    // Define all valid tokens from the design system
    this.validTokens = {
      colors: [
        // Semantic colors
        '--primary', '--primary-foreground',
        '--secondary', '--secondary-foreground',
        '--accent', '--accent-foreground',
        '--destructive', '--destructive-foreground',
        '--success', '--success-foreground',
        '--warning', '--warning-foreground',
        '--info', '--info-foreground',
        // Functional colors
        '--background', '--foreground',
        '--card', '--card-foreground',
        '--popover', '--popover-foreground',
        '--muted', '--muted-foreground',
        '--border', '--ring',
        '--input'
      ],
      spacing: [
        '--space-0', '--space-1', '--space-2', '--space-3', '--space-4',
        '--space-5', '--space-6', '--space-8', '--space-10', '--space-12',
        '--space-16', '--space-20', '--space-24', '--space-32'
      ],
      typography: [
        // Font families
        '--font-sans', '--font-mono',
        // Font sizes
        '--text-xs', '--text-sm', '--text-base', '--text-lg', '--text-xl',
        '--text-2xl', '--text-3xl', '--text-4xl',
        // Font weights
        '--font-normal', '--font-medium', '--font-semibold', '--font-bold',
        // Line heights
        '--leading-tight', '--leading-normal', '--leading-relaxed',
        // Letter spacing
        '--tracking-tight', '--tracking-normal', '--tracking-wide'
      ],
      radius: [
        '--radius-none', '--radius-sm', '--radius', '--radius-md',
        '--radius-lg', '--radius-xl', '--radius-full'
      ],
      shadows: [
        '--shadow-sm', '--shadow', '--shadow-md', '--shadow-lg', '--shadow-xl'
      ],
      timing: [
        '--duration-fast', '--duration-normal', '--duration-slow',
        '--ease-default', '--ease-in', '--ease-out'
      ],
      zIndex: [
        '--z-base', '--z-dropdown', '--z-sticky', '--z-fixed',
        '--z-modal-background', '--z-modal', '--z-popover', '--z-tooltip',
        '--z-notification', '--z-toast'
      ]
    };

    // Flatten all valid tokens for quick lookup
    this.allValidTokens = new Set();
    Object.values(this.validTokens).forEach(group => {
      group.forEach(token => this.allValidTokens.add(token));
    });

    // Patterns for detecting CSS variable usage
    this.varPattern = /var\(\s*([a-z0-9\-_]+)/gi;
    // Patterns for detecting hardcoded values
    this.colorPattern = /#[0-9a-f]{3,6}|rgb\(|hsl\(|oklch\(/gi;
    this.spacingPattern = /\b\d+(?:px|rem|em|ch|vw|vh)\b/g;

    // Track violations
    this.violations = [];
    this.warnings = [];
    this.stats = {
      totalTokenReferences: 0,
      validTokenReferences: 0,
      invalidTokenReferences: 0,
      hardcodedValues: 0,
      files: {}
    };
  }

  /**
   * Validate CSS text for token compliance
   * @param {string} cssText - CSS content to validate
   * @param {string} [fileName] - Optional filename for reporting
   * @returns {object} Report with violations, warnings, and stats
   */
  validateCSS(cssText, fileName = 'inline') {
    this.violations = [];
    this.warnings = [];

    if (!cssText || typeof cssText !== 'string') {
      return { violations: [], warnings: [], stats: this.stats };
    }

    // Parse CSS and check for token compliance
    this._validateTokenReferences(cssText, fileName);
    this._validateHardcodedColors(cssText, fileName);
    this._validateSpacingValues(cssText, fileName);

    // Store stats for this file
    this.stats.files[fileName] = {
      violations: this.violations.length,
      warnings: this.warnings.length
    };

    return {
      violations: this.violations,
      warnings: this.warnings,
      stats: this.stats,
      isCompliant: this.violations.length === 0
    };
  }

  /**
   * Validate all CSS variable references
   * @private
   */
  _validateTokenReferences(cssText, fileName) {
    let match;
    const varPattern = /var\(\s*([a-z0-9\-_]+)/gi;

    while ((match = varPattern.exec(cssText)) !== null) {
      const tokenName = match[1];
      this.stats.totalTokenReferences++;

      if (this.allValidTokens.has(tokenName)) {
        this.stats.validTokenReferences++;
      } else {
        this.stats.invalidTokenReferences++;
        this.violations.push({
          type: 'INVALID_TOKEN',
          token: tokenName,
          message: `Invalid token: '${tokenName}'`,
          file: fileName,
          suggestion: this._suggestToken(tokenName),
          code: match[0]
        });
      }
    }
  }

  /**
   * Detect hardcoded colors (anti-pattern)
   * @private
   */
  _validateHardcodedColors(cssText, fileName) {
    // Skip comments
    const cleanCSS = cssText.replace(/\/\*[\s\S]*?\*\//g, '');

    // Find hardcoded hex colors
    const hexPattern = /#[0-9a-f]{3,6}(?![a-z0-9\-])/gi;
    let match;

    while ((match = hexPattern.exec(cleanCSS)) !== null) {
      // Check if this is a valid CSS variable fallback (e.g., var(--primary, #fff))
      const context = cleanCSS.substring(Math.max(0, match.index - 20), match.index + 10);
      if (!context.includes('var(')) {
        this.stats.hardcodedValues++;
        this.violations.push({
          type: 'HARDCODED_COLOR',
          value: match[0],
          message: `Hardcoded color detected: '${match[0]}' - Use CSS tokens instead`,
          file: fileName,
          suggestion: `Use var(--primary) or similar color token`,
          code: match[0],
          severity: 'HIGH'
        });
      }
    }

    // Find hardcoded rgb/hsl colors (but not as fallback)
    const rgbPattern = /rgb\([^)]+\)|hsl\([^)]+\)|oklch\([^)]+\)(?![a-z])/gi;
    while ((match = rgbPattern.exec(cleanCSS)) !== null) {
      const context = cleanCSS.substring(Math.max(0, match.index - 20), match.index + 10);
      if (!context.includes('var(')) {
        this.stats.hardcodedValues++;
        this.violations.push({
          type: 'HARDCODED_COLOR_FUNCTION',
          value: match[0],
          message: `Hardcoded color function detected - Use CSS tokens instead`,
          file: fileName,
          suggestion: 'Use var(--color-name) instead of hardcoded rgb/hsl/oklch',
          code: match[0],
          severity: 'HIGH'
        });
      }
    }
  }

  /**
   * Detect hardcoded spacing values
   * @private
   */
  _validateSpacingValues(cssText, fileName) {
    // Look for padding/margin with px values that could be tokens
    const spacingProps = ['padding', 'margin', 'gap', 'width', 'height'];
    const spacingPattern = /(?:padding|margin|gap|width|height):\s*(\d+)px\b/gi;

    let match;
    while ((match = spacingPattern.exec(cssText)) !== null) {
      const value = parseInt(match[1]);
      // Only warn if it's a multiple of 4 (our spacing base unit)
      if (value % 4 === 0 && value <= 128) {
        this.warnings.push({
          type: 'HARDCODED_SPACING',
          value: match[0],
          message: `Hardcoded spacing value: '${match[1]}px' - Could use --space-* token`,
          file: fileName,
          suggestion: `Use var(--space-${Math.log2(value / 4)}) or appropriate token`,
          code: match[0],
          severity: 'MEDIUM'
        });
      }
    }
  }

  /**
   * Suggest the closest valid token based on fuzzy matching
   * @private
   */
  _suggestToken(invalidToken) {
    const lower = invalidToken.toLowerCase();
    let closest = null;
    let closestDistance = Infinity;

    this.allValidTokens.forEach(validToken => {
      const distance = this._levenshteinDistance(lower, validToken.toLowerCase());
      if (distance < closestDistance && distance < 5) {
        closestDistance = distance;
        closest = validToken;
      }
    });

    return closest ? `Did you mean '${closest}'?` : 'Check valid tokens in tokens.css';
  }

  /**
   * Calculate Levenshtein distance for fuzzy matching
   * @private
   */
  _levenshteinDistance(str1, str2) {
    const matrix = [];

    for (let i = 0; i <= str2.length; i++) {
      matrix[i] = [i];
    }

    for (let j = 0; j <= str1.length; j++) {
      matrix[0][j] = j;
    }

    for (let i = 1; i <= str2.length; i++) {
      for (let j = 1; j <= str1.length; j++) {
        if (str2.charAt(i - 1) === str1.charAt(j - 1)) {
          matrix[i][j] = matrix[i - 1][j - 1];
        } else {
          matrix[i][j] = Math.min(
            matrix[i - 1][j - 1] + 1,
            matrix[i][j - 1] + 1,
            matrix[i - 1][j] + 1
          );
        }
      }
    }

    return matrix[str2.length][str1.length];
  }

  /**
   * Validate HTML for inline style violations
   * @param {HTMLElement} element - Element to validate
   * @param {string} [fileName] - Optional filename for reporting
   * @returns {object} Report with violations
   */
  validateHTML(element, fileName = 'html') {
    this.violations = [];
    this.warnings = [];

    if (!element) return { violations: [], warnings: [] };

    // Check style attributes
    const allElements = element.querySelectorAll('[style]');
    allElements.forEach(el => {
      const styleAttr = el.getAttribute('style');
      if (styleAttr) {
        this.validateCSS(styleAttr, `${fileName}:${el.tagName}`);
      }
    });

    // Check shadow DOM styles
    const walkElements = (node) => {
      if (node.shadowRoot) {
        const styleElements = node.shadowRoot.querySelectorAll('style');
        styleElements.forEach(style => {
          this.validateCSS(style.textContent, `${fileName}:${node.tagName}(shadow)`);
        });
      }

      for (let child of node.children) {
        walkElements(child);
      }
    };

    walkElements(element);

    return {
      violations: this.violations,
      warnings: this.warnings,
      stats: this.stats
    };
  }

  /**
   * Generate a compliance report
   * @param {boolean} [includeStats=true] - Include detailed statistics
   * @returns {string} Formatted report
   */
  generateReport(includeStats = true) {
    let report = `
╔══════════════════════════════════════════════════════════════╗
║          Design Token Compliance Report                      ║
╚══════════════════════════════════════════════════════════════╝

`;

    if (this.violations.length === 0) {
      report += `✅ COMPLIANT - No token violations found\n`;
    } else {
      report += `❌ ${this.violations.length} VIOLATIONS FOUND:\n\n`;
      this.violations.forEach((v, i) => {
        report += `${i + 1}. ${v.type}: ${v.message}\n`;
        report += `   File: ${v.file}\n`;
        report += `   Code: ${v.code}\n`;
        if (v.suggestion) report += `   Suggestion: ${v.suggestion}\n`;
        report += `\n`;
      });
    }

    if (this.warnings.length > 0) {
      report += `⚠️  ${this.warnings.length} WARNINGS:\n\n`;
      this.warnings.forEach((w, i) => {
        report += `${i + 1}. ${w.type}: ${w.message}\n`;
        if (w.suggestion) report += `   Suggestion: ${w.suggestion}\n`;
        report += `\n`;
      });
    }

    if (includeStats && this.stats.totalTokenReferences > 0) {
      report += `\n📊 STATISTICS:\n`;
      report += `   Total Token References: ${this.stats.totalTokenReferences}\n`;
      report += `   Valid References: ${this.stats.validTokenReferences}\n`;
      report += `   Invalid References: ${this.stats.invalidTokenReferences}\n`;
      report += `   Hardcoded Values: ${this.stats.hardcodedValues}\n`;
      report += `   Compliance Rate: ${((this.stats.validTokenReferences / this.stats.totalTokenReferences) * 100).toFixed(1)}%\n`;
    }

    return report;
  }

  /**
   * Get list of all valid tokens
   * @returns {object} Tokens organized by category
   */
  getValidTokens() {
    return this.validTokens;
  }

  /**
   * Export validator instance for global access
   */
  static getInstance() {
    if (!window.__dssTokenValidator) {
      window.__dssTokenValidator = new TokenValidator();
    }
    return window.__dssTokenValidator;
  }
}

// Export for module systems
if (typeof module !== 'undefined' && module.exports) {
  module.exports = TokenValidator;
}

// Make available globally for console debugging
if (typeof window !== 'undefined') {
  window.TokenValidator = TokenValidator;
  window.__dssTokenValidator = new TokenValidator();

  // Add to debug interface
  if (window.__DSS_DEBUG) {
    window.__DSS_DEBUG.validateTokens = () => {
      const report = TokenValidator.getInstance().generateReport();
      console.log(report);
      return TokenValidator.getInstance();
    };
  }
}
