/**
 * Design System Validator
 *
 * Validates that CSS and Razor files only use design system tokens
 * instead of hardcoded colors, fonts, or spacing values.
 *
 * MORPH-SPEC 3.0 - Phase 3: Design System Enforcement
 */

import { existsSync, readFileSync, readdirSync, statSync } from 'fs';
import { join, extname } from 'path';
import { parseColors, parseTypography, parseSpacing } from '../../generators/design-system-generator.js';
import { detectDesignSystem } from '../../detectors/design-system-detector.js';

/**
 * Validate design system compliance
 * @param {string} projectPath - Root path of the project
 * @param {string} featureName - Feature name (optional)
 * @param {Object} options - Validation options
 * @returns {Object} { errors, warnings, issues }
 */
export async function validateDesignSystem(projectPath, featureName = null, options = {}) {
  const result = {
    errors: 0,
    warnings: 0,
    issues: []
  };

  // Detect design system
  const dsDetection = detectDesignSystem(projectPath, featureName);

  if (!dsDetection.hasDesignSystem) {
    result.errors++;
    result.issues.push({
      level: 'error',
      message: 'No design system found',
      solution: 'Create a design system at .morph/project/design-system.md or run design system generator'
    });
    return result;
  }

  // Parse design system
  let designSystem;
  try {
    const markdown = readFileSync(dsDetection.path, 'utf8');
    designSystem = {
      colors: parseColors(markdown),
      typography: parseTypography(markdown),
      spacing: parseSpacing(markdown)
    };
  } catch (err) {
    result.errors++;
    result.issues.push({
      level: 'error',
      message: `Failed to parse design system: ${err.message}`,
      file: dsDetection.path
    });
    return result;
  }

  // Build allowed color set
  const allowedColors = new Set();
  Object.values(designSystem.colors.primary).forEach(hex => allowedColors.add(hex.toLowerCase()));
  Object.values(designSystem.colors.secondary).forEach(hex => allowedColors.add(hex.toLowerCase()));
  Object.values(designSystem.colors.neutral).forEach(hex => allowedColors.add(hex.toLowerCase()));
  Object.values(designSystem.colors.semantic).forEach(hex => allowedColors.add(hex.toLowerCase()));

  // Common system colors (allowed)
  const systemColors = new Set([
    '#ffffff', '#fff', '#000000', '#000', 'transparent', 'inherit', 'currentcolor'
  ]);

  // Scan CSS files
  const cssFiles = findFiles(projectPath, ['.css'], options.scanPaths || ['wwwroot/css', 'styles']);
  for (const cssFile of cssFiles) {
    const cssResult = validateCSSFile(cssFile, allowedColors, systemColors);
    result.warnings += cssResult.warnings;
    result.issues.push(...cssResult.issues);
  }

  // Scan Razor files
  const razorFiles = findFiles(projectPath, ['.razor'], options.scanPaths || ['Components', 'Pages']);
  for (const razorFile of razorFiles) {
    const razorResult = validateRazorFile(razorFile, allowedColors, systemColors);
    result.warnings += razorResult.warnings;
    result.issues.push(...razorResult.issues);
  }

  return result;
}

/**
 * Find files with specific extensions in directories
 */
function findFiles(projectPath, extensions, scanPaths) {
  const files = [];

  for (const scanPath of scanPaths) {
    const fullPath = join(projectPath, scanPath);
    if (!existsSync(fullPath)) continue;

    try {
      walkDirectory(fullPath, extensions, files);
    } catch (err) {
      // Ignore permission errors
    }
  }

  return files;
}

/**
 * Recursively walk directory
 */
function walkDirectory(dir, extensions, files) {
  const items = readdirSync(dir);

  for (const item of items) {
    const fullPath = join(dir, item);
    const stat = statSync(fullPath);

    if (stat.isDirectory()) {
      walkDirectory(fullPath, extensions, files);
    } else if (stat.isFile()) {
      const ext = extname(fullPath);
      if (extensions.includes(ext)) {
        files.push(fullPath);
      }
    }
  }
}

/**
 * Validate CSS file for hardcoded colors
 */
function validateCSSFile(filePath, allowedColors, systemColors) {
  const result = {
    warnings: 0,
    issues: []
  };

  try {
    const content = readFileSync(filePath, 'utf8');

    // Match hex colors: #RGB or #RRGGBB
    const hexRegex = /#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})\b/g;

    let match;
    let lineNumber = 1;
    let lastIndex = 0;

    while ((match = hexRegex.exec(content)) !== null) {
      const hex = match[0].toLowerCase();

      // Count line number
      const upToMatch = content.substring(lastIndex, match.index);
      lineNumber += (upToMatch.match(/\n/g) || []).length;
      lastIndex = match.index;

      // Skip if in allowed or system colors
      if (allowedColors.has(hex) || systemColors.has(hex)) {
        continue;
      }

      result.warnings++;
      result.issues.push({
        level: 'warning',
        message: `Hardcoded color ${hex} not in design system palette`,
        file: filePath,
        line: lineNumber,
        solution: 'Use CSS variable like var(--color-primary-500) instead'
      });
    }
  } catch (err) {
    // Ignore read errors
  }

  return result;
}

/**
 * Validate Razor file for inline styles with hardcoded colors
 */
function validateRazorFile(filePath, allowedColors, systemColors) {
  const result = {
    warnings: 0,
    issues: []
  };

  try {
    const content = readFileSync(filePath, 'utf8');

    // Match style="" attributes with hex colors
    const styleRegex = /style\s*=\s*"([^"]*)"/gi;

    let match;
    while ((match = styleRegex.exec(content)) !== null) {
      const styleContent = match[1];

      // Find hex colors in style attribute
      const hexRegex = /#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})\b/g;
      let hexMatch;

      while ((hexMatch = hexRegex.exec(styleContent)) !== null) {
        const hex = hexMatch[0].toLowerCase();

        // Skip if in allowed or system colors
        if (allowedColors.has(hex) || systemColors.has(hex)) {
          continue;
        }

        // Count line number
        const upToMatch = content.substring(0, match.index);
        const lineNumber = (upToMatch.match(/\n/g) || []).length + 1;

        result.warnings++;
        result.issues.push({
          level: 'warning',
          message: `Inline style with hardcoded color ${hex} not in design system`,
          file: filePath,
          line: lineNumber,
          solution: 'Use CSS class with design system variables or component props instead of inline styles'
        });
      }
    }
  } catch (err) {
    // Ignore read errors
  }

  return result;
}
