import {
  init,
  parse_color,
  lighten_color,
  darken_color,
  saturate_color,
  desaturate_color,
  rotate_hue,
  complement_color,
  mix_colors,
  get_text_color,
  calculate_contrast,
  simulate_protanopia,
  simulate_deuteranopia,
  simulate_tritanopia,
  color_distance,
  generate_random_colors,
  generate_gradient,
  generate_palette,
  get_all_named_colors,
  search_named_colors,
  version,
} from '@valknarthing/pastel-wasm';
import type {
  ApiResponse,
  ColorInfoRequest,
  ColorInfoData,
  ConvertFormatRequest,
  ConvertFormatData,
  ColorManipulationRequest,
  ColorManipulationData,
  ColorMixRequest,
  ColorMixData,
  RandomColorsRequest,
  RandomColorsData,
  DistinctColorsRequest,
  DistinctColorsData,
  GradientRequest,
  GradientData,
  ColorDistanceRequest,
  ColorDistanceData,
  ColorSortRequest,
  ColorSortData,
  ColorBlindnessRequest,
  ColorBlindnessData,
  TextColorRequest,
  TextColorData,
  NamedColorsData,
  NamedColorSearchRequest,
  NamedColorSearchData,
  HealthData,
  CapabilitiesData,
  PaletteGenerateRequest,
  PaletteGenerateData,
} from './types';

// Initialize WASM module
let wasmInitialized = false;

async function ensureWasmInit() {
  if (!wasmInitialized) {
    init(); // Initialize panic hook
    wasmInitialized = true;
  }
}

/**
 * WASM-based Pastel client
 * Provides the same interface as PastelAPIClient but uses WebAssembly
 * Zero network latency, works offline!
 */
export class PastelWASMClient {
  constructor() {
    // Initialize WASM eagerly
    ensureWasmInit().catch(console.error);
  }

  private async request<T>(fn: () => T): Promise<ApiResponse<T>> {
    try {
      await ensureWasmInit();
      const data = fn();
      return {
        success: true,
        data,
      };
    } catch (error) {
      return {
        success: false,
        error: {
          code: 'WASM_ERROR',
          message: error instanceof Error ? error.message : 'Unknown error',
        },
      };
    }
  }

  // Color Information
  async getColorInfo(request: ColorInfoRequest): Promise<ApiResponse<ColorInfoData>> {
    return this.request(() => {
      const colors = request.colors.map((colorStr) => {
        const info = parse_color(colorStr);
        return {
          input: info.input,
          hex: info.hex,
          rgb: {
            r: info.rgb[0],
            g: info.rgb[1],
            b: info.rgb[2],
          },
          hsl: {
            h: info.hsl[0],
            s: info.hsl[1],
            l: info.hsl[2],
          },
          hsv: {
            h: info.hsv[0],
            s: info.hsv[1],
            v: info.hsv[2],
          },
          lab: {
            l: info.lab[0],
            a: info.lab[1],
            b: info.lab[2],
          },
          oklab: {
            l: info.lab[0] / 100.0,
            a: info.lab[1] / 100.0,
            b: info.lab[2] / 100.0,
          },
          lch: {
            l: info.lch[0],
            c: info.lch[1],
            h: info.lch[2],
          },
          oklch: {
            l: info.lch[0] / 100.0,
            c: info.lch[1] / 100.0,
            h: info.lch[2],
          },
          cmyk: {
            c: 0,
            m: 0,
            y: 0,
            k: 0,
          },
          brightness: info.brightness,
          luminance: info.luminance,
          is_light: info.is_light,
        };
      });
      return { colors };
    });
  }

  // Format Conversion
  async convertFormat(request: ConvertFormatRequest): Promise<ApiResponse<ConvertFormatData>> {
    return this.request(() => {
      const conversions = request.colors.map((colorStr) => {
        const parsed = parse_color(colorStr);
        let output: string;

        switch (request.format) {
          case 'hex':
            output = parsed.hex;
            break;
          case 'rgb':
            output = `rgb(${parsed.rgb[0]}, ${parsed.rgb[1]}, ${parsed.rgb[2]})`;
            break;
          case 'hsl':
            output = `hsl(${parsed.hsl[0].toFixed(1)}, ${(parsed.hsl[1] * 100).toFixed(1)}%, ${(parsed.hsl[2] * 100).toFixed(1)}%)`;
            break;
          case 'hsv':
            output = `hsv(${parsed.hsv[0].toFixed(1)}, ${(parsed.hsv[1] * 100).toFixed(1)}%, ${(parsed.hsv[2] * 100).toFixed(1)}%)`;
            break;
          case 'lab':
            output = `lab(${parsed.lab[0].toFixed(2)}, ${parsed.lab[1].toFixed(2)}, ${parsed.lab[2].toFixed(2)})`;
            break;
          case 'lch':
            output = `lch(${parsed.lch[0].toFixed(2)}, ${parsed.lch[1].toFixed(2)}, ${parsed.lch[2].toFixed(2)})`;
            break;
          default:
            output = parsed.hex;
        }

        return {
          input: colorStr,
          output,
        };
      });
      return { conversions };
    });
  }

  // Color Manipulation
  async lighten(request: ColorManipulationRequest): Promise<ApiResponse<ColorManipulationData>> {
    return this.request(() => {
      const colors = request.colors.map((colorStr) => ({
        input: colorStr,
        output: lighten_color(colorStr, request.amount),
      }));
      return { operation: 'lighten', amount: request.amount, colors };
    });
  }

  async darken(request: ColorManipulationRequest): Promise<ApiResponse<ColorManipulationData>> {
    return this.request(() => {
      const colors = request.colors.map((colorStr) => ({
        input: colorStr,
        output: darken_color(colorStr, request.amount),
      }));
      return { operation: 'darken', amount: request.amount, colors };
    });
  }

  async saturate(request: ColorManipulationRequest): Promise<ApiResponse<ColorManipulationData>> {
    return this.request(() => {
      const colors = request.colors.map((colorStr) => ({
        input: colorStr,
        output: saturate_color(colorStr, request.amount),
      }));
      return { operation: 'saturate', amount: request.amount, colors };
    });
  }

  async desaturate(request: ColorManipulationRequest): Promise<ApiResponse<ColorManipulationData>> {
    return this.request(() => {
      const colors = request.colors.map((colorStr) => ({
        input: colorStr,
        output: desaturate_color(colorStr, request.amount),
      }));
      return { operation: 'desaturate', amount: request.amount, colors };
    });
  }

  async rotate(request: ColorManipulationRequest): Promise<ApiResponse<ColorManipulationData>> {
    return this.request(() => {
      const colors = request.colors.map((colorStr) => ({
        input: colorStr,
        output: rotate_hue(colorStr, request.amount),
      }));
      return { operation: 'rotate', amount: request.amount, colors };
    });
  }

  async complement(colors: string[]): Promise<ApiResponse<ColorManipulationData>> {
    return this.request(() => {
      const results = colors.map((colorStr) => ({
        input: colorStr,
        output: complement_color(colorStr),
      }));
      return { operation: 'complement', colors: results };
    });
  }

  async grayscale(colors: string[]): Promise<ApiResponse<ColorManipulationData>> {
    return this.request(() => {
      const results = colors.map((colorStr) => ({
        input: colorStr,
        output: desaturate_color(colorStr, 1.0),
      }));
      return { operation: 'grayscale', colors: results };
    });
  }

  async mix(request: ColorMixRequest): Promise<ApiResponse<ColorMixData>> {
    return this.request(() => {
      // Mix pairs of colors
      const results = [];
      for (let i = 0; i < request.colors.length - 1; i += 2) {
        const color1 = request.colors[i];
        const color2 = request.colors[i + 1];
        const mixed = mix_colors(color1, color2, request.fraction);
        results.push({ color1, color2, mixed });
      }
      return { results };
    });
  }

  // Color Generation
  async generateRandom(request: RandomColorsRequest): Promise<ApiResponse<RandomColorsData>> {
    return this.request(() => {
      const vivid = request.strategy === 'vivid' || request.strategy === 'lch';
      const colors = generate_random_colors(request.count, vivid);
      return { colors };
    });
  }

  async generateDistinct(request: DistinctColorsRequest): Promise<ApiResponse<DistinctColorsData>> {
    return this.request(() => {
      // Note: WASM version doesn't support distinct colors with simulated annealing yet
      // Fall back to vivid random colors
      const colors = generate_random_colors(request.count, true);
      return {
        colors,
      };
    });
  }

  async generateGradient(request: GradientRequest): Promise<ApiResponse<GradientData>> {
    return this.request(() => {
      if (request.stops.length < 2) {
        throw new Error('At least 2 color stops are required');
      }

      // For 2 stops, use the WASM gradient function
      if (request.stops.length === 2) {
        const gradient = generate_gradient(request.stops[0], request.stops[1], request.count);
        return {
          stops: request.stops,
          count: request.count,
          gradient,
        };
      }

      // For multiple stops, interpolate segments
      const segments = request.stops.length - 1;
      const colorsPerSegment = Math.floor(request.count / segments);
      const gradient: string[] = [];

      for (let i = 0; i < segments; i++) {
        const segmentColors = generate_gradient(
          request.stops[i],
          request.stops[i + 1],
          i === segments - 1 ? request.count - gradient.length : colorsPerSegment
        );
        gradient.push(...segmentColors.slice(0, -1)); // Avoid duplicates
      }
      gradient.push(request.stops[request.stops.length - 1]);

      return {
        stops: request.stops,
        count: request.count,
        gradient,
      };
    });
  }

  // Color Analysis
  async calculateDistance(request: ColorDistanceRequest): Promise<ApiResponse<ColorDistanceData>> {
    return this.request(() => {
      const useCiede2000 = request.metric === 'ciede2000';
      const distance = color_distance(request.color1, request.color2, useCiede2000);
      return {
        color1: request.color1,
        color2: request.color2,
        distance,
        metric: request.metric,
      };
    });
  }

  async sortColors(request: ColorSortRequest): Promise<ApiResponse<ColorSortData>> {
    return this.request(() => {
      // Note: WASM version doesn't support sorting yet
      // Return colors as-is for now
      return { sorted: request.colors };
    });
  }

  // Accessibility
  async simulateColorBlindness(request: ColorBlindnessRequest): Promise<ApiResponse<ColorBlindnessData>> {
    return this.request(() => {
      const colors = request.colors.map((colorStr) => {
        let output: string;
        switch (request.type) {
          case 'protanopia':
            output = simulate_protanopia(colorStr);
            break;
          case 'deuteranopia':
            output = simulate_deuteranopia(colorStr);
            break;
          case 'tritanopia':
            output = simulate_tritanopia(colorStr);
            break;
          default:
            output = colorStr;
        }

        const distance = color_distance(colorStr, output, true);
        return {
          input: colorStr,
          output,
          difference_percentage: (distance / 100.0) * 100.0,
        };
      });

      return { type: request.type, colors };
    });
  }

  async getTextColor(request: TextColorRequest): Promise<ApiResponse<TextColorData>> {
    return this.request(() => {
      const colors = request.backgrounds.map((bg) => {
        const textColor = get_text_color(bg);
        const contrastRatio = calculate_contrast(bg, textColor);

        return {
          background: bg,
          textcolor: textColor,
          contrast_ratio: contrastRatio,
          wcag_aa: contrastRatio >= 4.5,
          wcag_aaa: contrastRatio >= 7.0,
        };
      });

      return { colors };
    });
  }

  // Named Colors
  async getNamedColors(): Promise<ApiResponse<NamedColorsData>> {
    return this.request(() => {
      const colors = get_all_named_colors();
      return { colors };
    });
  }

  async searchNamedColors(request: NamedColorSearchRequest): Promise<ApiResponse<NamedColorSearchData>> {
    return this.request(() => {
      const results = search_named_colors(request.query);
      return { results };
    });
  }

  // System
  async getHealth(): Promise<ApiResponse<HealthData>> {
    return this.request(() => ({
      status: 'healthy',
      version: version(),
    }));
  }

  async getCapabilities(): Promise<ApiResponse<CapabilitiesData>> {
    return this.request(() => ({
      endpoints: [
        'colors/info',
        'colors/convert',
        'colors/lighten',
        'colors/darken',
        'colors/saturate',
        'colors/desaturate',
        'colors/rotate',
        'colors/complement',
        'colors/grayscale',
        'colors/mix',
        'colors/random',
        'colors/gradient',
        'colors/colorblind',
        'colors/textcolor',
        'colors/distance',
        'colors/names',
      ],
      formats: ['hex', 'rgb', 'hsl', 'hsv', 'lab', 'lch'],
      distance_metrics: ['cie76', 'ciede2000'],
      colorblindness_types: ['protanopia', 'deuteranopia', 'tritanopia'],
    }));
  }

  // Palette Generation
  async generatePalette(request: PaletteGenerateRequest): Promise<ApiResponse<PaletteGenerateData>> {
    return this.request(() => {
      const colors = generate_palette(request.base, request.scheme);
      return {
        base: request.base,
        scheme: request.scheme,
        palette: {
          primary: colors[0],
          secondary: colors.slice(1),
        },
      };
    });
  }
}

// Export singleton instance
export const pastelWASM = new PastelWASMClient();
