/**
 * MORPH-SPEC CSS Validator Library
 *
 * Validates that CSS classes used in Razor files exist in CSS files.
 * Helps catch missing CSS definitions before runtime.
 *
 * @module css-validator
 */

import { readFileSync, existsSync } from 'fs';
import { glob } from 'glob';
import { join, relative } from 'path';

// ============================================================================
// CSS CLASS EXTRACTION
// ============================================================================

/**
 * Extract CSS class names from a CSS file content
 * @param {string} content - CSS file content
 * @returns {Set<string>} Set of class names defined in the CSS
 */
export function extractCssClasses(content) {
  const classes = new Set();

  // Match class selectors: .class-name
  // Handles: .class, .class:hover, .class::before, .class.other-class
  const classRegex = /\.([a-zA-Z_-][a-zA-Z0-9_-]*)/g;

  let match;
  while ((match = classRegex.exec(content)) !== null) {
    classes.add(match[1]);
  }

  return classes;
}

/**
 * Extract CSS classes used in Razor file content
 * @param {string} content - Razor file content
 * @returns {Array<{className: string, line: number}>} Array of class usages with line numbers
 */
export function extractRazorClasses(content) {
  const usages = [];
  const lines = content.split('\n');

  lines.forEach((line, index) => {
    // Match class="..." or class='...'
    const classAttrRegex = /class\s*=\s*["']([^"']+)["']/gi;

    // Match @class="..." (Blazor conditional)
    const blazorClassRegex = /@class\s*=\s*["']([^"']+)["']/gi;

    // Match Class="..." (component parameter)
    const componentClassRegex = /\bClass\s*=\s*["']([^"']+)["']/gi;

    // Match Style with class-like patterns in CSS modules
    const cssModuleRegex = /styles\.([a-zA-Z_-][a-zA-Z0-9_-]*)/gi;

    for (const regex of [classAttrRegex, blazorClassRegex, componentClassRegex]) {
      let match;
      while ((match = regex.exec(line)) !== null) {
        const classString = match[1];

        // Split by whitespace to get individual classes
        const classes = classString.split(/\s+/).filter(c => c.length > 0);

        for (const className of classes) {
          // Skip Blazor expressions like @(condition ? "class1" : "class2")
          if (className.startsWith('@') || className.includes('(')) continue;

          // Skip interpolated strings
          if (className.includes('{') || className.includes('}')) continue;

          // Skip conditional ternary results (partial matches)
          if (className === '?' || className === ':') continue;

          usages.push({
            className: className.trim(),
            line: index + 1
          });
        }
      }
    }

    // CSS modules
    let cssModuleMatch;
    while ((cssModuleMatch = cssModuleRegex.exec(line)) !== null) {
      usages.push({
        className: cssModuleMatch[1],
        line: index + 1
      });
    }
  });

  return usages;
}

// ============================================================================
// VALIDATION
// ============================================================================

/**
 * Known utility class prefixes from common frameworks
 * These are excluded from validation as they come from external libraries
 */
const UTILITY_PREFIXES = [
  // Tailwind CSS
  'bg-', 'text-', 'font-', 'p-', 'px-', 'py-', 'pt-', 'pb-', 'pl-', 'pr-',
  'm-', 'mx-', 'my-', 'mt-', 'mb-', 'ml-', 'mr-', 'w-', 'h-', 'min-', 'max-',
  'flex', 'grid', 'block', 'inline', 'hidden', 'overflow-', 'rounded-',
  'border-', 'shadow-', 'opacity-', 'z-', 'gap-', 'space-', 'justify-',
  'items-', 'content-', 'self-', 'col-', 'row-', 'order-', 'animate-',
  'transition-', 'duration-', 'ease-', 'delay-', 'scale-', 'rotate-',
  'translate-', 'skew-', 'origin-', 'cursor-', 'select-', 'resize-',
  'list-', 'appearance-', 'pointer-', 'fill-', 'stroke-', 'object-',
  'aspect-', 'columns-', 'break-', 'box-', 'float-', 'clear-', 'isolation-',
  'mix-blend-', 'bg-blend-', 'filter-', 'backdrop-', 'blur-', 'brightness-',
  'contrast-', 'drop-shadow-', 'grayscale-', 'hue-rotate-', 'invert-',
  'saturate-', 'sepia-', 'table-', 'caption-', 'border-collapse-', 'outline-',
  'ring-', 'placeholder-', 'caret-', 'accent-', 'scroll-', 'snap-', 'touch-',
  'will-change-', 'contain-',

  // Bootstrap
  'container', 'row', 'col-', 'btn-', 'alert-', 'badge-', 'card-', 'nav-',
  'navbar-', 'dropdown-', 'modal-', 'form-', 'input-', 'table-', 'list-',
  'd-', 'align-', 'justify-', 'order-', 'offset-', 'g-', 'gy-', 'gx-',

  // Fluent UI Blazor (internal classes)
  'fluent-', 'fui-',

  // MudBlazor (internal classes)
  'mud-', 'MudBlazor',

  // Common framework classes
  'sr-only', 'not-sr-only', 'disabled', 'active', 'show', 'fade', 'collapse'
];

/**
 * Check if a class name is a utility class from a known framework
 * @param {string} className - Class name to check
 * @returns {boolean} True if it's a utility class
 */
function isUtilityClass(className) {
  return UTILITY_PREFIXES.some(prefix => className.startsWith(prefix));
}

/**
 * Validate CSS classes in a project directory
 * @param {string} projectPath - Path to the project directory
 * @param {Object} options - Validation options
 * @param {boolean} options.verbose - Show detailed output
 * @param {boolean} options.includeUtilities - Include utility classes in validation
 * @param {string[]} options.cssGlob - Glob patterns for CSS files
 * @param {string[]} options.razorGlob - Glob patterns for Razor files
 * @returns {Promise<Object>} Validation results
 */
export async function validateCssClasses(projectPath, options = {}) {
  const {
    verbose = false,
    includeUtilities = false,
    cssGlob = ['**/*.css'],
    razorGlob = ['**/*.razor']
  } = options;

  // Find all CSS files
  const cssFiles = [];
  for (const pattern of cssGlob) {
    const files = await glob(pattern, {
      cwd: projectPath,
      ignore: ['**/node_modules/**', '**/bin/**', '**/obj/**', '**/wwwroot/lib/**']
    });
    cssFiles.push(...files);
  }

  // Find all Razor files
  const razorFiles = [];
  for (const pattern of razorGlob) {
    const files = await glob(pattern, {
      cwd: projectPath,
      ignore: ['**/node_modules/**', '**/bin/**', '**/obj/**']
    });
    razorFiles.push(...files);
  }

  // Extract all defined CSS classes
  const definedClasses = new Set();
  const cssFileDetails = [];

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

    const content = readFileSync(fullPath, 'utf8');
    const classes = extractCssClasses(content);

    cssFileDetails.push({
      file,
      classCount: classes.size
    });

    for (const cls of classes) {
      definedClasses.add(cls);
    }
  }

  // Extract all used CSS classes from Razor files
  const usedClasses = new Map(); // className -> [{file, line}]
  const razorFileDetails = [];

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

    const content = readFileSync(fullPath, 'utf8');
    const usages = extractRazorClasses(content);

    razorFileDetails.push({
      file,
      usageCount: usages.length
    });

    for (const usage of usages) {
      if (!usedClasses.has(usage.className)) {
        usedClasses.set(usage.className, []);
      }
      usedClasses.get(usage.className).push({
        file,
        line: usage.line
      });
    }
  }

  // Find missing classes
  const missingClasses = [];

  for (const [className, locations] of usedClasses) {
    // Skip utility classes unless explicitly included
    if (!includeUtilities && isUtilityClass(className)) continue;

    // Skip if class is defined
    if (definedClasses.has(className)) continue;

    missingClasses.push({
      className,
      locations
    });
  }

  // Sort missing classes by number of usages (most used first)
  missingClasses.sort((a, b) => b.locations.length - a.locations.length);

  return {
    summary: {
      cssFilesScanned: cssFiles.length,
      razorFilesScanned: razorFiles.length,
      definedClasses: definedClasses.size,
      usedClasses: usedClasses.size,
      missingClasses: missingClasses.length
    },
    cssFiles: cssFileDetails,
    razorFiles: razorFileDetails,
    definedClasses: Array.from(definedClasses).sort(),
    missingClasses,
    valid: missingClasses.length === 0
  };
}

/**
 * Format validation results for CLI output
 * @param {Object} results - Validation results from validateCssClasses
 * @param {boolean} verbose - Show detailed output
 * @returns {string} Formatted output
 */
export function formatValidationResults(results, verbose = false) {
  const lines = [];

  // Header
  lines.push('');
  lines.push('CSS Validation Results');
  lines.push('='.repeat(50));
  lines.push('');

  // Summary
  lines.push(`CSS files scanned:   ${results.summary.cssFilesScanned}`);
  lines.push(`Razor files scanned: ${results.summary.razorFilesScanned}`);
  lines.push(`Classes defined:     ${results.summary.definedClasses}`);
  lines.push(`Classes used:        ${results.summary.usedClasses}`);
  lines.push(`Missing classes:     ${results.summary.missingClasses}`);
  lines.push('');

  if (results.valid) {
    lines.push('All CSS classes are defined.');
    return lines.join('\n');
  }

  // Missing classes
  lines.push('Missing CSS Classes:');
  lines.push('-'.repeat(50));

  for (const missing of results.missingClasses) {
    lines.push(`\n  .${missing.className}`);

    if (verbose) {
      for (const loc of missing.locations) {
        lines.push(`    - ${loc.file}:${loc.line}`);
      }
    } else {
      // Show first 3 locations
      const shown = missing.locations.slice(0, 3);
      for (const loc of shown) {
        lines.push(`    - ${loc.file}:${loc.line}`);
      }
      if (missing.locations.length > 3) {
        lines.push(`    ... and ${missing.locations.length - 3} more`);
      }
    }
  }

  lines.push('');

  return lines.join('\n');
}

/**
 * Generate a CSS stub file with missing class definitions
 * @param {Object} results - Validation results
 * @returns {string} CSS file content with stub classes
 */
export function generateCssStub(results) {
  if (results.valid) {
    return '/* No missing classes */';
  }

  const lines = [
    '/* ==========================================================================',
    '   MISSING CSS CLASSES - Generated by MORPH-SPEC validate-css',
    '   Add these classes to your design-system.css or appropriate CSS file',
    '   ========================================================================== */',
    ''
  ];

  for (const missing of results.missingClasses) {
    lines.push(`/* Used in: ${missing.locations.map(l => `${l.file}:${l.line}`).slice(0, 3).join(', ')}${missing.locations.length > 3 ? '...' : ''} */`);
    lines.push(`.${missing.className} {`);
    lines.push('  /* TODO: Add styles */');
    lines.push('}');
    lines.push('');
  }

  return lines.join('\n');
}
