"use client";

import { useRef, useState, type PointerEvent as ReactPointerEvent } from "react";
import { cn } from "@/lib/utils";

interface ColorWheelPickerProps {
  value: string;
  onChange: (hex: string) => void;
  size?: number;
}

function hslToHex(h: number, s: number, l: number): string {
  s /= 100;
  l /= 100;
  const k = (n: number) => (n + h / 30) % 12;
  const a = s * Math.min(l, 1 - l);
  const f = (n: number) => l - a * Math.max(-1, Math.min(k(n) - 3, Math.min(9 - k(n), 1)));
  const toHex = (n: number) =>
    Math.round(f(n) * 255)
      .toString(16)
      .padStart(2, "0");
  return `#${toHex(0)}${toHex(8)}${toHex(4)}`;
}

function hexToHsl(hex: string): { h: number; s: number; l: number } {
  const match = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
  if (!match) return { h: 0, s: 0, l: 50 };
  const r = parseInt(match[1], 16) / 255;
  const g = parseInt(match[2], 16) / 255;
  const b = parseInt(match[3], 16) / 255;
  const max = Math.max(r, g, b);
  const min = Math.min(r, g, b);
  let h = 0;
  const l = (max + min) / 2;
  const d = max - min;
  const s = d === 0 ? 0 : d / (1 - Math.abs(2 * l - 1));
  if (d !== 0) {
    if (max === r) h = ((g - b) / d) % 6;
    else if (max === g) h = (b - r) / d + 2;
    else h = (r - g) / d + 4;
    h *= 60;
    if (h < 0) h += 360;
  }
  return { h, s: s * 100, l: l * 100 };
}

// A real hue/saturation wheel (drag anywhere on the circle) plus a lightness
// slider underneath — not just a native OS color swatch button.
//
// The wheel's CSS is a `conic-gradient` whose 0% stop renders at the TOP and
// sweeps clockwise — that's a different zero-point/direction than the usual
// math angle (0° = pointing right, measured counter-clockwise in screen
// space). Every angle computed from the pointer, and every angle used to
// place the marker, has to use that same top-clockwise convention or the
// spot you click stops matching the color actually under the cursor.
export function ColorWheelPicker({ value, onChange, size = 140 }: ColorWheelPickerProps) {
  const wheelRef = useRef<HTMLDivElement>(null);
  const { h, s, l } = hexToHsl(value);
  const [dragging, setDragging] = useState(false);
  // Holds what's being typed so the field stays editable mid-input (e.g.
  // "#1a2"); cleared on blur or once a full valid hex commits `onChange`,
  // at which point the field just reflects `value` like the wheel does.
  const [hexDraft, setHexDraft] = useState<string | null>(null);
  const displayedHex = (hexDraft ?? value).toUpperCase();
  // At l=0 (black) or l=100 (white) every hue/saturation renders identically,
  // so the wheel would look like it "does nothing" — fall back to a usable
  // mid lightness whenever the current color is one of those extremes.
  const lightness = l <= 0 || l >= 100 ? 50 : l;

  const radius = size / 2;
  const satRatio = Math.min(1, s / 100);
  const hueRad = (h * Math.PI) / 180;
  const markerX = radius + Math.sin(hueRad) * satRatio * radius;
  const markerY = radius - Math.cos(hueRad) * satRatio * radius;

  function pickFromPointer(clientX: number, clientY: number) {
    const el = wheelRef.current;
    if (!el) return;
    const rect = el.getBoundingClientRect();
    const cx = rect.left + rect.width / 2;
    const cy = rect.top + rect.height / 2;
    const dx = clientX - cx;
    const dy = clientY - cy;
    const dist = Math.min(1, Math.sqrt(dx * dx + dy * dy) / (rect.width / 2));
    // atan2(dx, -dy): 0° straight up, 90° pointing right — top-clockwise,
    // matching the conic-gradient's own stop placement.
    let angle = (Math.atan2(dx, -dy) * 180) / Math.PI;
    if (angle < 0) angle += 360;
    onChange(hslToHex(angle, dist * 100, lightness));
  }

  function handlePointerDown(e: ReactPointerEvent<HTMLDivElement>) {
    e.preventDefault();
    setDragging(true);
    pickFromPointer(e.clientX, e.clientY);
    e.currentTarget.setPointerCapture(e.pointerId);
  }

  function handlePointerMove(e: ReactPointerEvent<HTMLDivElement>) {
    if (!dragging) return;
    pickFromPointer(e.clientX, e.clientY);
  }

  function stopDragging() {
    setDragging(false);
  }

  function handleHexInput(raw: string) {
    setHexDraft(raw);
    const cleaned = raw.trim().replace(/^#/, "");
    if (/^[0-9a-fA-F]{6}$/.test(cleaned)) {
      onChange(`#${cleaned.toLowerCase()}`);
    }
  }

  return (
    <div className="flex flex-col gap-3">
      <div className="flex items-start gap-3">
        <div
          ref={wheelRef}
          onPointerDown={handlePointerDown}
          onPointerMove={handlePointerMove}
          onPointerUp={stopDragging}
          onPointerCancel={stopDragging}
          className="relative shrink-0 cursor-crosshair rounded-full"
          style={{
            width: size,
            height: size,
            touchAction: "none",
            backgroundImage:
              "radial-gradient(circle, #fff 0%, rgba(255,255,255,0) 70%), conic-gradient(red, yellow, lime, cyan, blue, magenta, red)",
          }}
        >
          <div
            className="pointer-events-none absolute h-4 w-4 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 border-white shadow-[0_0_0_1px_rgba(0,0,0,0.3)]"
            style={{ left: markerX, top: markerY, backgroundColor: value }}
          />
        </div>

        {/* Hex code, to the side of the wheel — typing/pasting a color works
            just as well as picking one visually. */}
        <div className="flex flex-col gap-1.5 pt-1">
          <span className="text-[11px] font-medium text-muted-foreground">Hex</span>
          <div className="flex items-center gap-1.5">
            <span
              className="h-7 w-7 shrink-0 rounded-md border border-border"
              style={{ backgroundColor: value }}
            />
            <input
              type="text"
              value={displayedHex}
              onChange={(e) => handleHexInput(e.target.value)}
              onBlur={() => setHexDraft(null)}
              maxLength={7}
              spellCheck={false}
              placeholder="#000000"
              className="w-24 rounded-md border border-border bg-background px-2 py-1.5 font-mono text-[12.5px] uppercase outline-none focus:border-ring"
            />
          </div>
        </div>
      </div>

      <input
        type="range"
        min={0}
        max={100}
        value={lightness}
        onChange={(e) => onChange(hslToHex(h, s, Number(e.target.value)))}
        className={cn(
          "h-2 w-full cursor-pointer appearance-none rounded-full",
          "[&::-webkit-slider-thumb]:h-4 [&::-webkit-slider-thumb]:w-4 [&::-webkit-slider-thumb]:appearance-none",
          "[&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:border-2 [&::-webkit-slider-thumb]:border-white",
          "[&::-webkit-slider-thumb]:shadow-[0_0_0_1px_rgba(0,0,0,0.3)]",
          "[&::-moz-range-thumb]:h-4 [&::-moz-range-thumb]:w-4 [&::-moz-range-thumb]:rounded-full",
          "[&::-moz-range-thumb]:border-2 [&::-moz-range-thumb]:border-white [&::-moz-range-thumb]:shadow-[0_0_0_1px_rgba(0,0,0,0.3)]",
        )}
        style={{
          background: `linear-gradient(to right, #000, hsl(${h} ${s}% 50%), #fff)`,
        }}
      />
    </div>
  );
}
