import { useState, useRef, useCallback, useEffect } from "react";
import { Upload, Download, X, ImageIcon } from "lucide-react";
import { cn } from "../lib/utils";

// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------

const CHECKERBOARD: React.CSSProperties = {
  backgroundImage: `
    linear-gradient(45deg, #444 25%, transparent 25%),
    linear-gradient(-45deg, #444 25%, transparent 25%),
    linear-gradient(45deg, transparent 75%, #444 75%),
    linear-gradient(-45deg, transparent 75%, #444 75%)
  `,
  backgroundSize: "16px 16px",
  backgroundPosition: "0 0, 0 8px, 8px -8px, -8px 0px",
  backgroundColor: "#2a2a2a",
};

// ---------------------------------------------------------------------------
// WebGL — shaders + init
// ---------------------------------------------------------------------------

const VERT = `
attribute vec2 aPos;
varying vec2 vUV;
void main() {
  vUV = aPos * 0.5 + 0.5;
  vUV.y = 1.0 - vUV.y;
  gl_Position = vec4(aPos, 0.0, 1.0);
}`;

// RGB↔HSL math in GLSL — runs massively parallel on GPU
const FRAG = `
precision mediump float;
uniform sampler2D uImage;
uniform float uHueShift;
uniform float uSaturation;
uniform float uLightness;
varying vec2 vUV;

vec3 rgb2hsl(vec3 c) {
  float maxC = max(c.r, max(c.g, c.b));
  float minC = min(c.r, min(c.g, c.b));
  float l = (maxC + minC) * 0.5;
  float d = maxC - minC;
  if (d < 0.001) return vec3(0.0, 0.0, l);
  float s = l > 0.5 ? d / (2.0 - maxC - minC) : d / (maxC + minC);
  float h;
  if (maxC == c.r)      h = (c.g - c.b) / d + (c.g < c.b ? 6.0 : 0.0);
  else if (maxC == c.g) h = (c.b - c.r) / d + 2.0;
  else                  h = (c.r - c.g) / d + 4.0;
  return vec3(h / 6.0, s, l);
}

float hue2rgb(float p, float q, float t) {
  if (t < 0.0) t += 1.0;
  if (t > 1.0) t -= 1.0;
  if (t < 1.0 / 6.0) return p + (q - p) * 6.0 * t;
  if (t < 0.5)        return q;
  if (t < 2.0 / 3.0)  return p + (q - p) * (2.0 / 3.0 - t) * 6.0;
  return p;
}

vec3 hsl2rgb(vec3 hsl) {
  float h = hsl.x, s = hsl.y, l = hsl.z;
  if (s < 0.001) return vec3(l);
  float q = l < 0.5 ? l * (1.0 + s) : l + s - l * s;
  float p = 2.0 * l - q;
  return vec3(
    hue2rgb(p, q, h + 1.0 / 3.0),
    hue2rgb(p, q, h),
    hue2rgb(p, q, h - 1.0 / 3.0)
  );
}

void main() {
  vec4 c = texture2D(uImage, vUV);
  if (c.a < 0.001) { gl_FragColor = c; return; }
  vec3 hsl = rgb2hsl(c.rgb);
  hsl.x = fract(hsl.x + uHueShift + 1.0);
  hsl.y = clamp(hsl.y + uSaturation, 0.0, 1.0);
  hsl.z = clamp(hsl.z + uLightness,  0.0, 1.0);
  gl_FragColor = vec4(hsl2rgb(hsl), c.a);
}`;

type GlState = {
  gl: WebGLRenderingContext;
  tex: WebGLTexture;
  uHueShift:   WebGLUniformLocation;
  uSaturation: WebGLUniformLocation;
  uLightness:  WebGLUniformLocation;
};

function initGl(canvas: HTMLCanvasElement): GlState | null {
  const gl = canvas.getContext("webgl", {
    premultipliedAlpha: false,
    preserveDrawingBuffer: true,
  }) as WebGLRenderingContext | null;
  if (!gl) return null;

  const vert = gl.createShader(gl.VERTEX_SHADER)!;
  gl.shaderSource(vert, VERT);
  gl.compileShader(vert);

  const frag = gl.createShader(gl.FRAGMENT_SHADER)!;
  gl.shaderSource(frag, FRAG);
  gl.compileShader(frag);

  const prog = gl.createProgram()!;
  gl.attachShader(prog, vert);
  gl.attachShader(prog, frag);
  gl.linkProgram(prog);
  gl.useProgram(prog);

  const buf = gl.createBuffer()!;
  gl.bindBuffer(gl.ARRAY_BUFFER, buf);
  gl.bufferData(
    gl.ARRAY_BUFFER,
    new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]),
    gl.STATIC_DRAW,
  );
  const aPos = gl.getAttribLocation(prog, "aPos");
  gl.enableVertexAttribArray(aPos);
  gl.vertexAttribPointer(aPos, 2, gl.FLOAT, false, 0, 0);

  const tex = gl.createTexture()!;
  gl.bindTexture(gl.TEXTURE_2D, tex);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);

  return {
    gl,
    tex,
    uHueShift:   gl.getUniformLocation(prog, "uHueShift")!,
    uSaturation: gl.getUniformLocation(prog, "uSaturation")!,
    uLightness:  gl.getUniformLocation(prog, "uLightness")!,
  };
}

// ---------------------------------------------------------------------------
// Slider
// ---------------------------------------------------------------------------

function Slider({
  label, value, min, max, unit, onChange, gradient,
}: {
  label: string;
  value: number;
  min: number;
  max: number;
  unit: string;
  onChange: (v: number) => void;
  gradient?: string;
}) {
  return (
    <div className="space-y-2">
      <div className="flex justify-between items-baseline">
        <p className="text-xs font-medium text-text-secondary">{label}</p>
        <span className="text-xs font-mono text-text-tertiary tabular-nums w-16 text-right">
          {`${value > 0 ? "+" : ""}${value}${unit}`}
        </span>
      </div>
      <div className="relative h-5 flex items-center">
        {gradient && (
          <div
            className="absolute inset-x-0 h-2 rounded-full pointer-events-none"
            style={{ background: gradient }}
          />
        )}
        <input
          type="range"
          min={min}
          max={max}
          value={value}
          onChange={(e) => onChange(Number(e.target.value))}
          className={cn(
            "relative z-10 w-full appearance-none bg-transparent cursor-pointer",
            "[&::-webkit-slider-thumb]:appearance-none",
            "[&::-webkit-slider-thumb]:w-4 [&::-webkit-slider-thumb]:h-4",
            "[&::-webkit-slider-thumb]:-mt-1",
            "[&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:bg-white",
            "[&::-webkit-slider-thumb]:border-2 [&::-webkit-slider-thumb]:border-border",
            "[&::-webkit-slider-thumb]:shadow-sm",
            gradient
              ? "[&::-webkit-slider-runnable-track]:h-2 [&::-webkit-slider-runnable-track]:rounded-full [&::-webkit-slider-runnable-track]:bg-transparent"
              : "accent-primary",
          )}
        />
      </div>
    </div>
  );
}

// ---------------------------------------------------------------------------
// HueShifter
// ---------------------------------------------------------------------------

export function HueShifter() {
  const [imageSrc,   setImageSrc]   = useState<string | null>(null);
  const [imageFile,  setImageFile]  = useState<File | null>(null);
  const [imageReady, setImageReady] = useState(false);
  const [dragOver,   setDragOver]   = useState(false);

  const [hueShift,   setHueShift]   = useState(0);
  const [saturation, setSaturation] = useState(0);
  const [lightness,  setLightness]  = useState(0);

  const fileInputRef = useRef<HTMLInputElement>(null);
  // glCanvasRef — WebGL result canvas (also the preview)
  const glCanvasRef  = useRef<HTMLCanvasElement>(null);
  const glRef        = useRef<GlState | null>(null);

  // ── Load image → upload texture once ──────────────────────────────────────
  useEffect(() => {
    if (!imageSrc) return;
    setImageReady(false);

    const img = new Image();
    img.onload = () => {
      const glCanvas = glCanvasRef.current;
      if (!glCanvas) return;
      glCanvas.width  = img.naturalWidth;
      glCanvas.height = img.naturalHeight;

      if (!glRef.current) {
        glRef.current = initGl(glCanvas);
        if (!glRef.current) {
          console.error("HueShifter: WebGL not supported");
          return;
        }
      }

      const { gl, tex } = glRef.current;
      gl.bindTexture(gl.TEXTURE_2D, tex);
      gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
      gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE);
      gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);
      gl.viewport(0, 0, img.naturalWidth, img.naturalHeight);

      setImageReady(true);
    };
    img.src = imageSrc;
  }, [imageSrc]);

  // ── Re-render on GPU whenever sliders change ───────────────────────────────
  useEffect(() => {
    const state = glRef.current;
    if (!state || !imageReady) return;

    const { gl, uHueShift, uSaturation, uLightness } = state;
    gl.uniform1f(uHueShift,   hueShift   / 360);
    gl.uniform1f(uSaturation, saturation / 100);
    gl.uniform1f(uLightness,  lightness  / 100);
    gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
  }, [imageReady, hueShift, saturation, lightness]);

  // ── Helpers ────────────────────────────────────────────────────────────────
  const loadFile = useCallback((file: File) => {
    if (imageSrc) URL.revokeObjectURL(imageSrc);
    setImageSrc(URL.createObjectURL(file));
    setImageFile(file);
    setHueShift(0);
    setSaturation(0);
    setLightness(0);
  }, [imageSrc]);

  const clearAll = useCallback(() => {
    if (imageSrc) URL.revokeObjectURL(imageSrc);
    setImageSrc(null);
    setImageFile(null);
    setImageReady(false);
    glRef.current = null;
    if (fileInputRef.current) fileInputRef.current.value = "";
  }, [imageSrc]);

  const handleDrop = (e: React.DragEvent) => {
    e.preventDefault();
    setDragOver(false);
    const file = e.dataTransfer.files[0];
    if (file?.type.startsWith("image/")) loadFile(file);
  };

  const handleDownload = () => {
    const glCanvas = glCanvasRef.current;
    if (!glCanvas || !imageFile || !imageReady) return;
    glCanvas.toBlob((blob) => {
      if (!blob) return;
      const url = URL.createObjectURL(blob);
      const a   = document.createElement("a");
      a.download = imageFile.name.replace(/\.[^.]+$/, "") + "_recolored.png";
      a.href = url;
      a.click();
      URL.revokeObjectURL(url);
    }, "image/png");
  };

  const handleReset = () => {
    setHueShift(0);
    setSaturation(0);
    setLightness(0);
  };

  const isDefault = hueShift === 0 && saturation === 0 && lightness === 0;

  // ── Upload screen ──────────────────────────────────────────────────────────
  if (!imageSrc) {
    return (
      <div className="max-w-2xl mx-auto py-8 space-y-4">
        <div>
          <h2 className="text-lg font-semibold text-foreground mb-1">Hue Shifter</h2>
          <p className="text-sm text-text-secondary">
            Upload an image and shift its hue, saturation, and lightness to create colour
            variants. Fully transparent pixels are preserved.
          </p>
        </div>

        <div
          onDrop={handleDrop}
          onDragOver={(e) => { e.preventDefault(); setDragOver(true); }}
          onDragLeave={() => setDragOver(false)}
          onClick={() => fileInputRef.current?.click()}
          className={cn(
            "border-2 border-dashed rounded-xl p-16 flex flex-col items-center gap-4 cursor-pointer transition-all select-none",
            dragOver
              ? "border-primary bg-primary/5"
              : "border-border hover:border-primary/50 hover:bg-primary/3",
          )}
        >
          <Upload
            className={cn(
              "w-10 h-10 transition-colors",
              dragOver ? "text-primary" : "text-text-tertiary",
            )}
          />
          <div className="text-center">
            <p className="text-sm font-medium text-text-secondary">
              {dragOver ? "Drop to upload" : "Drop an image here"}
            </p>
            <p className="text-xs text-text-tertiary mt-1">
              or click to browse · PNG, JPEG, WebP · max 15 MB
            </p>
          </div>
        </div>

        <input
          ref={fileInputRef}
          type="file"
          accept="image/png,image/jpeg,image/webp"
          onChange={(e) => { const f = e.target.files?.[0]; if (f) loadFile(f); }}
          className="hidden"
        />
      </div>
    );
  }

  // ── Editor screen ──────────────────────────────────────────────────────────
  return (
    <div className="space-y-4 pb-8">
      {/* Toolbar */}
      <div className="flex items-center gap-3 flex-wrap">
        <h2 className="text-lg font-semibold text-foreground flex-1">Hue Shifter</h2>
        <span className="text-xs text-text-tertiary font-mono truncate max-w-[200px]">
          {imageFile?.name}
        </span>
        <button
          onClick={handleReset}
          disabled={isDefault}
          className={cn(
            "px-3 py-1.5 rounded-md border border-border text-xs text-text-tertiary transition-colors",
            isDefault
              ? "opacity-40 cursor-not-allowed"
              : "hover:text-foreground hover:border-primary/40",
          )}
        >
          Reset
        </button>
        <button
          onClick={clearAll}
          className={cn(
            "flex items-center gap-1.5 px-3 py-1.5 rounded-md border border-border text-xs text-text-tertiary",
            "hover:text-destructive hover:border-destructive transition-colors",
          )}
        >
          <X className="w-3.5 h-3.5" /> Clear
        </button>
        <button
          onClick={handleDownload}
          disabled={!imageReady || isDefault}
          className={cn(
            "flex items-center gap-1.5 px-4 py-1.5 rounded-md text-xs font-semibold transition-colors",
            imageReady && !isDefault
              ? "bg-primary text-white hover:bg-primary/90"
              : "bg-raised border border-border text-text-tertiary cursor-not-allowed",
          )}
        >
          <Download className="w-3.5 h-3.5" /> Download PNG
        </button>
      </div>

      {/* Controls */}
      <div className="bg-card border border-border rounded-xl p-5 space-y-5">
        <Slider
          label="Hue Shift"
          value={hueShift}
          min={-180}
          max={180}
          unit="°"
          onChange={setHueShift}
          gradient="linear-gradient(to right, hsl(0,80%,55%), hsl(60,80%,55%), hsl(120,80%,55%), hsl(180,80%,55%), hsl(240,80%,55%), hsl(300,80%,55%), hsl(360,80%,55%))"
        />
        <Slider
          label="Saturation"
          value={saturation}
          min={-100}
          max={100}
          unit="%"
          onChange={setSaturation}
          gradient="linear-gradient(to right, hsl(210,0%,50%), hsl(210,0%,55%) 50%, hsl(210,90%,55%))"
        />
        <Slider
          label="Lightness"
          value={lightness}
          min={-100}
          max={100}
          unit="%"
          onChange={setLightness}
          gradient="linear-gradient(to right, hsl(0,0%,10%), hsl(0,0%,50%) 50%, hsl(0,0%,92%))"
        />
      </div>

      {/* Side-by-side */}
      <div className="grid grid-cols-2 gap-4">
        <div className="space-y-2">
          <p className="text-xs font-semibold text-text-tertiary uppercase tracking-wider">
            Original
          </p>
          <div className="bg-card border border-border rounded-lg overflow-hidden">
            <div style={CHECKERBOARD}>
              <img src={imageSrc} alt="Original" className="w-full block" />
            </div>
          </div>
        </div>

        <div className="space-y-2">
          <p className="text-xs font-semibold text-text-tertiary uppercase tracking-wider">
            Result
          </p>
          <div className="bg-card border border-border rounded-lg overflow-hidden">
            {/* WebGL canvas always in DOM; hidden until image is ready */}
            <div style={CHECKERBOARD}>
              <canvas
                ref={glCanvasRef}
                className={cn("w-full block", !imageReady && "hidden")}
              />
              {!imageReady && (
                <div className="aspect-square flex items-center justify-center">
                  <ImageIcon className="w-10 h-10 opacity-20 text-white" />
                </div>
              )}
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}
