/**
 * ==========================================================================
 * PALETTE GENERATOR
 * ==========================================================================
 * Generate Material Design 3 compliant color palettes using HCL color space
 * Creates perceptually uniform tonal variations with proper contrast ratios
 * ==========================================================================
 */

import { HCLConverter } from './hcl-converter';
import { TonalPalette, MaterialPalette, BrandColors, HCL } from '../models/hcl.models';

export class PaletteGenerator {

  // ==========================================================================
  // MATERIAL DESIGN 3 TONE LEVELS
  // ==========================================================================
  
  private static readonly TONE_LEVELS = [0, 4, 5, 6, 10, 12, 15, 17, 20, 22, 25, 30, 35, 40, 50, 60, 70, 80, 87, 90, 92, 94, 95, 96, 98, 99, 100] as const;

  // ==========================================================================
  // MAIN PALETTE GENERATION
  // ==========================================================================

  /**
   * Generate complete Material Design 3 palette from brand colors
   * @param brandColors Primary, secondary, tertiary seed colors
   * @param mode Light or dark mode
   * @returns Complete Material Design 3 palette
   */
  static generateMaterialPalette(brandColors: BrandColors, mode: 'light' | 'dark' = 'light'): MaterialPalette {
    return {
      primary: this.generateTonalPalette(brandColors.primary),
      secondary: this.generateTonalPalette(brandColors.secondary),
      tertiary: this.generateTonalPalette(brandColors.tertiary),
      neutral: this.generateNeutralPalette(brandColors.primary),
      neutralVariant: this.generateNeutralVariantPalette(brandColors.primary),
      error: this.generateTonalPalette(brandColors.error || '#B3261E') // Material Design default error
    };
  }

  /**
   * Generate tonal palette from seed color
   * @param seedColor Hex color string
   * @returns Tonal palette with all tone levels
   */
  static generateTonalPalette(seedColor: string): TonalPalette {
    const seedHcl = HCLConverter.hexToHcl(seedColor);
    
    // Create palette object with all tone levels
    const palette = {} as TonalPalette;
    
    this.TONE_LEVELS.forEach(tone => {
      palette[tone as keyof TonalPalette] = this.generateTone(seedHcl, tone);
    });
    
    return palette;
  }

  /**
   * Generate neutral palette from primary color
   * @param primaryColor Primary seed color
   * @returns Neutral tonal palette
   */
  static generateNeutralPalette(primaryColor: string): TonalPalette {
    const primaryHcl = HCLConverter.hexToHcl(primaryColor);
    
    // Create neutral variation with reduced chroma and adjusted hue
    const neutralHcl: HCL = {
      h: primaryHcl.h,
      c: Math.min(primaryHcl.c * 0.08, 8), // Very low chroma for neutral
      l: primaryHcl.l
    };
    
    const palette = {} as TonalPalette;
    
    this.TONE_LEVELS.forEach(tone => {
      palette[tone as keyof TonalPalette] = this.generateTone(neutralHcl, tone);
    });
    
    return palette;
  }

  /**
   * Generate neutral variant palette from primary color
   * @param primaryColor Primary seed color
   * @returns Neutral variant tonal palette
   */
  static generateNeutralVariantPalette(primaryColor: string): TonalPalette {
    const primaryHcl = HCLConverter.hexToHcl(primaryColor);
    
    // Create neutral variant with slightly more chroma than neutral
    const neutralVariantHcl: HCL = {
      h: primaryHcl.h,
      c: Math.min(primaryHcl.c * 0.16, 16), // Low chroma but higher than neutral
      l: primaryHcl.l
    };
    
    const palette = {} as TonalPalette;
    
    this.TONE_LEVELS.forEach(tone => {
      palette[tone as keyof TonalPalette] = this.generateTone(neutralVariantHcl, tone);
    });
    
    return palette;
  }

  // ==========================================================================
  // TONE GENERATION
  // ==========================================================================

  /**
   * Generate specific tone from HCL seed
   * @param seedHcl Seed color in HCL space
   * @param tone Target tone level (0-100)
   * @returns Hex color string
   */
  private static generateTone(seedHcl: HCL, tone: number): string {
    // Apply tone-specific chroma adjustments for better visual consistency
    const chromaMultiplier = this.getChromaMultiplier(tone);
    const adjustedChroma = seedHcl.c * chromaMultiplier;
    
    // Create tone with adjusted lightness and chroma
    const toneHcl: HCL = {
      h: seedHcl.h,
      c: Math.max(0, adjustedChroma),
      l: tone
    };
    
    return HCLConverter.hclToHex(toneHcl.h, toneHcl.c, toneHcl.l);
  }

  /**
   * Get chroma multiplier based on tone level
   * Reduces chroma at very light and very dark tones for better visual balance
   * @param tone Tone level (0-100)
   * @returns Chroma multiplier (0-1)
   */
  private static getChromaMultiplier(tone: number): number {
    // Reduce chroma at extreme lightness values
    if (tone <= 10) return 0.6 + (tone / 10) * 0.3; // 0.6 to 0.9
    if (tone >= 90) return 1.1 - ((tone - 90) / 10) * 0.5; // 1.1 to 0.6
    if (tone >= 80) return 1.0 + ((tone - 80) / 10) * 0.1; // 1.0 to 1.1
    
    // Full chroma in middle ranges
    return 1.0;
  }

  // ==========================================================================
  // COLOR HARMONIES
  // ==========================================================================

  /**
   * Generate complementary color from seed
   * @param seedColor Seed color
   * @returns Complementary color
   */
  static generateComplementary(seedColor: string): string {
    const hcl = HCLConverter.hexToHcl(seedColor);
    const compHue = (hcl.h + 180) % 360;
    return HCLConverter.hclToHex(compHue, hcl.c, hcl.l);
  }

  /**
   * Generate analogous colors from seed
   * @param seedColor Seed color
   * @param count Number of analogous colors to generate
   * @returns Array of analogous colors
   */
  static generateAnalogous(seedColor: string, count: number = 3): string[] {
    const hcl = HCLConverter.hexToHcl(seedColor);
    const colors: string[] = [];
    const step = 30; // 30-degree steps
    
    for (let i = 0; i < count; i++) {
      const hue = (hcl.h + (i - Math.floor(count / 2)) * step) % 360;
      const adjustedHue = hue < 0 ? hue + 360 : hue;
      colors.push(HCLConverter.hclToHex(adjustedHue, hcl.c, hcl.l));
    }
    
    return colors;
  }

  /**
   * Generate triadic colors from seed
   * @param seedColor Seed color
   * @returns Array of triadic colors (including original)
   */
  static generateTriadic(seedColor: string): string[] {
    const hcl = HCLConverter.hexToHcl(seedColor);
    return [
      seedColor,
      HCLConverter.hclToHex((hcl.h + 120) % 360, hcl.c, hcl.l),
      HCLConverter.hclToHex((hcl.h + 240) % 360, hcl.c, hcl.l)
    ];
  }

  /**
   * Generate tetradic colors from seed
   * @param seedColor Seed color
   * @returns Array of tetradic colors
   */
  static generateTetradic(seedColor: string): string[] {
    const hcl = HCLConverter.hexToHcl(seedColor);
    return [
      seedColor,
      HCLConverter.hclToHex((hcl.h + 90) % 360, hcl.c, hcl.l),
      HCLConverter.hclToHex((hcl.h + 180) % 360, hcl.c, hcl.l),
      HCLConverter.hclToHex((hcl.h + 270) % 360, hcl.c, hcl.l)
    ];
  }

  // ==========================================================================
  // ACCESSIBILITY HELPERS
  // ==========================================================================

  /**
   * Ensure minimum contrast ratio for text readability
   * @param backgroundColor Background color
   * @param textColor Text color
   * @param minRatio Minimum contrast ratio (default: 4.5 for WCAG AA)
   * @returns Adjusted text color
   */
  static ensureContrast(backgroundColor: string, textColor: string, minRatio: number = 4.5): string {
    const currentRatio = HCLConverter.getContrastRatio(backgroundColor, textColor);
    
    if (currentRatio >= minRatio) {
      return textColor; // Already meets contrast requirements
    }
    
    const textHcl = HCLConverter.hexToHcl(textColor);
    const backgroundLuminance = HCLConverter.getRelativeLuminance(backgroundColor);
    
    // Adjust lightness to meet contrast requirements
    let adjustedL = textHcl.l;
    let bestColor = textColor;
    let bestRatio = currentRatio;
    
    // Try both lighter and darker variations
    for (let lightness = 0; lightness <= 100; lightness += 5) {
      const testColor = HCLConverter.hclToHex(textHcl.h, textHcl.c, lightness);
      const testRatio = HCLConverter.getContrastRatio(backgroundColor, testColor);
      
      if (testRatio >= minRatio && testRatio > bestRatio) {
        bestColor = testColor;
        bestRatio = testRatio;
        adjustedL = lightness;
      }
    }
    
    return bestColor;
  }

  /**
   * Get optimal text color (black or white) for background
   * @param backgroundColor Background color
   * @returns Optimal text color (#000000 or #FFFFFF)
   */
  static getOptimalTextColor(backgroundColor: string): string {
    const blackRatio = HCLConverter.getContrastRatio(backgroundColor, '#000000');
    const whiteRatio = HCLConverter.getContrastRatio(backgroundColor, '#FFFFFF');
    
    return blackRatio > whiteRatio ? '#000000' : '#FFFFFF';
  }

  // ==========================================================================
  // PALETTE UTILITIES
  // ==========================================================================

  /**
   * Get primary tone for specific use case
   * @param palette Tonal palette
   * @param usage Usage context ('main', 'container', 'on-container', etc.)
   * @param mode Light or dark mode
   * @returns Appropriate tone
   */
  static getPrimaryTone(palette: TonalPalette, usage: 'main' | 'container' | 'on-container' | 'surface', mode: 'light' | 'dark' = 'light'): string {
    if (mode === 'light') {
      switch (usage) {
        case 'main': return palette[40];
        case 'container': return palette[90];
        case 'on-container': return palette[10];
        case 'surface': return palette[98];
        default: return palette[40];
      }
    } else {
      switch (usage) {
        case 'main': return palette[80];
        case 'container': return palette[30];
        case 'on-container': return palette[90];
        case 'surface': return palette[10];
        default: return palette[80];
      }
    }
  }

  /**
   * Validate palette for accessibility and visual consistency
   * @param palette Material palette to validate
   * @returns Validation results
   */
  static validatePalette(palette: MaterialPalette): { isValid: boolean; issues: string[] } {
    const issues: string[] = [];

    // Check contrast ratios for common combinations
    const primaryMain = palette.primary[40];
    const primaryContainer = palette.primary[90];
    const onPrimary = palette.primary[100];
    const onPrimaryContainer = palette.primary[10];

    // Validate primary combinations
    const primaryContrastRatio = HCLConverter.getContrastRatio(primaryMain, onPrimary);
    if (primaryContrastRatio < 4.5) {
      issues.push(`Primary contrast ratio too low: ${primaryContrastRatio.toFixed(2)}`);
    }

    const containerContrastRatio = HCLConverter.getContrastRatio(primaryContainer, onPrimaryContainer);
    if (containerContrastRatio < 4.5) {
      issues.push(`Container contrast ratio too low: ${containerContrastRatio.toFixed(2)}`);
    }

    // Additional checks could be added here (color difference, chroma consistency, etc.)

    return {
      isValid: issues.length === 0,
      issues
    };
  }

  // ==========================================================================
  // PREVIEW GENERATION
  // ==========================================================================

  /**
   * Generate color preview for theme selection UI
   * @param brandColors Brand colors
   * @returns Preview color object
   */
  static generateColorPreview(brandColors: BrandColors): { [key: string]: string } {
    const palette = this.generateMaterialPalette(brandColors);
    
    return {
      'Primary': palette.primary[40],
      'Primary Container': palette.primary[90],
      'Secondary': palette.secondary[40],
      'Secondary Container': palette.secondary[90],
      'Tertiary': palette.tertiary[40],
      'Tertiary Container': palette.tertiary[90],
      'Surface': palette.neutral[98],
      'Surface Container': palette.neutral[94]
    };
  }
}