import {
  init,
  parse_color,
  lighten_color,
  darken_color,
  saturate_color,
  desaturate_color,
  rotate_hue,
  complement_color,
  generate_random_colors,
  generate_gradient,
  generate_palette,
  version,
} from '@valknarthing/pastel-wasm';
import type {
  ApiResponse,
  ColorInfoRequest,
  ColorInfoData,
  ConvertFormatRequest,
  ConvertFormatData,
  ColorManipulationRequest,
  ColorManipulationData,
  RandomColorsRequest,
  RandomColorsData,
  GradientRequest,
  GradientData,
  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 Color client
 * Provides the same interface as ColorAPIClient but uses WebAssembly
 * Zero network latency, works offline!
 */
export class ColorWASMClient {
  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) as any;
        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.oklab ? info.oklab[0] : info.lab[0] / 100.0,
            a: info.oklab ? info.oklab[1] : info.lab[1] / 100.0,
            b: info.oklab ? info.oklab[2] : info.lab[2] / 100.0,
          },
          lch: {
            l: info.lch[0],
            c: info.lch[1],
            h: info.lch[2],
          },
          oklch: {
            l: info.oklch ? info.oklch[0] : info.lch[0] / 100.0,
            c: info.oklch ? info.oklch[1] : info.lch[1] / 100.0,
            h: info.oklch ? info.oklch[2] : 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) as any;
        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;
          case 'oklab': {
            const l = parsed.oklab ? parsed.oklab[0] : parsed.lab[0] / 100.0;
            const a = parsed.oklab ? parsed.oklab[1] : parsed.lab[1] / 100.0;
            const b = parsed.oklab ? parsed.oklab[2] : parsed.lab[2] / 100.0;
            output = `oklab(${(l * 100).toFixed(1)}% ${a.toFixed(3)} ${b.toFixed(3)})`;
            break;
          }
          case 'oklch': {
            const l = parsed.oklch ? parsed.oklch[0] : parsed.lch[0] / 100.0;
            const c = parsed.oklch ? parsed.oklch[1] : parsed.lch[1] / 100.0;
            const h = parsed.oklch ? parsed.oklch[2] : parsed.lch[2];
            output = `oklch(${(l * 100).toFixed(1)}% ${c.toFixed(3)} ${h.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 };
    });
  }

  // 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 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,
      };
    });
  }

  // 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/random',
        'colors/gradient',
        '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 colorWASM = new ColorWASMClient();
