import * as React from 'react'
import { cn } from './utils'

export type ColorFormat = 'hex' | 'rgb' | 'hsl'

export interface ColorPickerProps {
  /** Current color value (hex string) */
  value?: string
  /** Output format */
  format?: ColorFormat
  /** Show alpha/opacity slider */
  showAlpha?: boolean
  /** Preset color swatches */
  presets?: string[]
  /** Additional class name */
  className?: string
  /** Callback when color changes */
  onChange?: (color: string) => void
}

interface RGBA {
  r: number
  g: number
  b: number
  a: number
}

function clamp(value: number, min: number, max: number): number {
  return Math.max(min, Math.min(max, value))
}

function hexToRgba(hex: string): RGBA {
  let h = hex.replace(/^#/, '')
  if (h.length === 3 || h.length === 4) {
    h = h
      .split('')
      .map(c => c + c)
      .join('')
  }
  const r = parseInt(h.slice(0, 2), 16)
  const g = parseInt(h.slice(2, 4), 16)
  const b = parseInt(h.slice(4, 6), 16)
  const a = h.length === 8 ? parseInt(h.slice(6, 8), 16) / 255 : 1
  return {
    r: isNaN(r) ? 0 : r,
    g: isNaN(g) ? 0 : g,
    b: isNaN(b) ? 0 : b,
    a: isNaN(a) ? 1 : a,
  }
}

function rgbaToHex(rgba: RGBA, includeAlpha: boolean): string {
  const r = clamp(Math.round(rgba.r), 0, 255).toString(16).padStart(2, '0')
  const g = clamp(Math.round(rgba.g), 0, 255).toString(16).padStart(2, '0')
  const b = clamp(Math.round(rgba.b), 0, 255).toString(16).padStart(2, '0')
  if (includeAlpha && rgba.a < 1) {
    const a = clamp(Math.round(rgba.a * 255), 0, 255)
      .toString(16)
      .padStart(2, '0')
    return `#${r}${g}${b}${a}`
  }
  return `#${r}${g}${b}`
}

function rgbaToHsla(rgba: RGBA): { h: number; s: number; l: number; a: number } {
  const r = rgba.r / 255
  const g = rgba.g / 255
  const b = rgba.b / 255
  const max = Math.max(r, g, b)
  const min = Math.min(r, g, b)
  const l = (max + min) / 2
  let h = 0
  let s = 0
  if (max !== min) {
    const d = max - min
    s = l > 0.5 ? d / (2 - max - min) : d / (max + min)
    if (max === r) h = ((g - b) / d + (g < b ? 6 : 0)) / 6
    else if (max === g) h = ((b - r) / d + 2) / 6
    else h = ((r - g) / d + 4) / 6
  }
  return { h: h * 360, s: s * 100, l: l * 100, a: rgba.a }
}

function rgbaToHsv(rgba: RGBA): { h: number; s: number; v: number } {
  const r = rgba.r / 255
  const g = rgba.g / 255
  const b = rgba.b / 255
  const max = Math.max(r, g, b)
  const min = Math.min(r, g, b)
  const d = max - min
  let h = 0
  if (d !== 0) {
    if (max === r) h = ((g - b) / d + (g < b ? 6 : 0)) * 60
    else if (max === g) h = ((b - r) / d + 2) * 60
    else h = ((r - g) / d + 4) * 60
  }
  const s = max === 0 ? 0 : d / max
  return { h, s, v: max }
}

function hsvToRgba(h: number, s: number, v: number, a: number): RGBA {
  const i = Math.floor(h / 60) % 6
  const f = h / 60 - Math.floor(h / 60)
  const p = v * (1 - s)
  const q = v * (1 - f * s)
  const t = v * (1 - (1 - f) * s)
  let r: number, g: number, b: number
  switch (i) {
    case 0:
      r = v
      g = t
      b = p
      break
    case 1:
      r = q
      g = v
      b = p
      break
    case 2:
      r = p
      g = v
      b = t
      break
    case 3:
      r = p
      g = q
      b = v
      break
    case 4:
      r = t
      g = p
      b = v
      break
    default:
      r = v
      g = p
      b = q
      break
  }
  return { r: Math.round(r * 255), g: Math.round(g * 255), b: Math.round(b * 255), a }
}

function formatOutput(rgba: RGBA, format: ColorFormat, showAlpha: boolean): string {
  switch (format) {
    case 'rgb': {
      if (rgba.a < 1)
        return `rgba(${Math.round(rgba.r)}, ${Math.round(rgba.g)}, ${Math.round(rgba.b)}, ${Math.round(rgba.a * 100) / 100})`
      return `rgb(${Math.round(rgba.r)}, ${Math.round(rgba.g)}, ${Math.round(rgba.b)})`
    }
    case 'hsl': {
      const hsla = rgbaToHsla(rgba)
      if (hsla.a < 1)
        return `hsla(${Math.round(hsla.h)}, ${Math.round(hsla.s)}%, ${Math.round(hsla.l)}%, ${Math.round(hsla.a * 100) / 100})`
      return `hsl(${Math.round(hsla.h)}, ${Math.round(hsla.s)}%, ${Math.round(hsla.l)}%)`
    }
    case 'hex':
    default:
      return rgbaToHex(rgba, showAlpha)
  }
}

function ColorPicker({
  value = '#000000',
  format = 'hex',
  showAlpha = false,
  presets,
  className,
  onChange,
}: ColorPickerProps) {
  const [isOpen, setIsOpen] = React.useState(false)
  const rootRef = React.useRef<HTMLDivElement>(null)

  const initialRgba = React.useMemo(() => hexToRgba(value), [value])
  const initialHsv = React.useMemo(() => rgbaToHsv(initialRgba), [initialRgba])

  const [hue, setHue] = React.useState(initialHsv.h)
  const [saturation, setSaturation] = React.useState(initialHsv.s)
  const [brightness, setBrightness] = React.useState(initialHsv.v)
  const [alpha, setAlpha] = React.useState(initialRgba.a)
  const [hexInput, setHexInput] = React.useState(value)

  // Sync from external value prop
  React.useEffect(() => {
    const rgba = hexToRgba(value)
    const hsv = rgbaToHsv(rgba)
    setHue(hsv.h)
    setSaturation(hsv.s)
    setBrightness(hsv.v)
    setAlpha(rgba.a)
    setHexInput(value)
  }, [value])

  const rgba = React.useMemo(
    () => hsvToRgba(hue, saturation, brightness, alpha),
    [hue, saturation, brightness, alpha]
  )

  const currentHex = React.useMemo(() => rgbaToHex(rgba, showAlpha), [rgba, showAlpha])

  function emitChange(r: RGBA) {
    onChange?.(formatOutput(r, format, showAlpha))
  }

  // Close on outside click
  React.useEffect(() => {
    if (!isOpen) return
    function handleClick(e: MouseEvent) {
      if (rootRef.current && !rootRef.current.contains(e.target as Node)) {
        setIsOpen(false)
      }
    }
    document.addEventListener('mousedown', handleClick)
    return () => document.removeEventListener('mousedown', handleClick)
  }, [isOpen])

  // Hue slider
  const hueRef = React.useRef<HTMLDivElement>(null)
  function handleHueInteraction(clientX: number) {
    if (!hueRef.current) return
    const rect = hueRef.current.getBoundingClientRect()
    const x = clamp((clientX - rect.left) / rect.width, 0, 1)
    const newHue = x * 360
    setHue(newHue)
    const newRgba = hsvToRgba(newHue, saturation, brightness, alpha)
    setHexInput(rgbaToHex(newRgba, showAlpha))
    emitChange(newRgba)
  }

  // Alpha slider
  const alphaRef = React.useRef<HTMLDivElement>(null)
  function handleAlphaInteraction(clientX: number) {
    if (!alphaRef.current) return
    const rect = alphaRef.current.getBoundingClientRect()
    const x = clamp((clientX - rect.left) / rect.width, 0, 1)
    setAlpha(x)
    const newRgba = hsvToRgba(hue, saturation, brightness, x)
    setHexInput(rgbaToHex(newRgba, showAlpha))
    emitChange(newRgba)
  }

  // Saturation/brightness area
  const satRef = React.useRef<HTMLDivElement>(null)
  function handleSatInteraction(clientX: number, clientY: number) {
    if (!satRef.current) return
    const rect = satRef.current.getBoundingClientRect()
    const s = clamp((clientX - rect.left) / rect.width, 0, 1)
    const b = clamp(1 - (clientY - rect.top) / rect.height, 0, 1)
    setSaturation(s)
    setBrightness(b)
    const newRgba = hsvToRgba(hue, s, b, alpha)
    setHexInput(rgbaToHex(newRgba, showAlpha))
    emitChange(newRgba)
  }

  function makeDragHandler(handler: (clientX: number, clientY: number) => void) {
    return (e: React.MouseEvent) => {
      e.preventDefault()
      handler(e.clientX, e.clientY)
      function onMove(ev: MouseEvent) {
        ev.preventDefault()
        handler(ev.clientX, ev.clientY)
      }
      function onUp() {
        document.removeEventListener('mousemove', onMove)
        document.removeEventListener('mouseup', onUp)
      }
      document.addEventListener('mousemove', onMove)
      document.addEventListener('mouseup', onUp)
    }
  }

  function handleHexCommit() {
    const val = hexInput.trim()
    const normalized = val.startsWith('#') ? val : `#${val}`
    if (/^#([0-9a-fA-F]{3,4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/.test(normalized)) {
      const newRgba = hexToRgba(normalized)
      const hsv = rgbaToHsv(newRgba)
      setHue(hsv.h)
      setSaturation(hsv.s)
      setBrightness(hsv.v)
      setAlpha(newRgba.a)
      emitChange(newRgba)
    }
  }

  function handleRgbChange(channel: 'r' | 'g' | 'b', val: number) {
    const newRgba = { ...rgba, [channel]: clamp(val, 0, 255) }
    const hsv = rgbaToHsv(newRgba)
    setHue(hsv.h)
    setSaturation(hsv.s)
    setBrightness(hsv.v)
    setHexInput(rgbaToHex(newRgba, showAlpha))
    emitChange(newRgba)
  }

  function selectPreset(color: string) {
    const newRgba = hexToRgba(color)
    const hsv = rgbaToHsv(newRgba)
    setHue(hsv.h)
    setSaturation(hsv.s)
    setBrightness(hsv.v)
    setAlpha(newRgba.a)
    setHexInput(color)
    emitChange(newRgba)
  }

  const baseHueHex = rgbaToHex(hsvToRgba(hue, 1, 1, 1), false)

  const inputClass = cn(
    'border-border-glass bg-glass h-8 w-full rounded-lg border px-2 text-[12px] text-foreground',
    'outline-none transition-all duration-200 focus:border-primary focus:shadow-glow backdrop-blur-[20px]'
  )

  return (
    <div ref={rootRef} data-slot="color-picker" className={cn('relative inline-block', className)}>
      {/* Swatch trigger */}
      <button
        type="button"
        aria-label="Open color picker"
        aria-expanded={isOpen}
        onClick={() => setIsOpen(o => !o)}
        className={cn(
          'h-10 w-10 rounded-xl border border-border-glass shadow-sm transition-all duration-200',
          'hover:shadow-glow focus:border-primary focus:shadow-glow focus:outline-none'
        )}
        style={{ backgroundColor: currentHex }}
      />

      {/* Popover panel */}
      {isOpen && (
        <div
          className={cn(
            'absolute top-full left-0 z-50 mt-2 w-64 rounded-xl border border-border-glass bg-glass p-3 shadow-lg backdrop-blur-[20px]',
            'animate-in fade-in-0 zoom-in-95 slide-in-from-top-2'
          )}
          onClick={e => e.stopPropagation()}
        >
          {/* Saturation/Brightness area */}
          <div
            ref={satRef}
            className="relative mb-3 h-40 w-full cursor-crosshair overflow-hidden rounded-lg"
            style={{ backgroundColor: baseHueHex }}
            onMouseDown={makeDragHandler((x, y) => handleSatInteraction(x, y))}
          >
            <div className="pointer-events-none absolute inset-0 bg-gradient-to-r from-white to-transparent" />
            <div className="pointer-events-none absolute inset-0 bg-gradient-to-b from-transparent to-black" />
            <div
              className="pointer-events-none absolute h-3 w-3 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 border-white shadow-sm"
              style={{
                left: `${saturation * 100}%`,
                top: `${(1 - brightness) * 100}%`,
              }}
            />
          </div>

          {/* Hue slider */}
          <div
            ref={hueRef}
            className="relative mb-2 h-3 w-full cursor-pointer rounded-full"
            style={{
              background:
                'linear-gradient(to right, #f00 0%, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%)',
            }}
            onMouseDown={makeDragHandler(x => handleHueInteraction(x))}
          >
            <div
              className="pointer-events-none absolute top-1/2 h-4 w-4 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 border-white shadow-sm"
              style={{ left: `${(hue / 360) * 100}%` }}
            />
          </div>

          {/* Alpha slider */}
          {showAlpha && (
            <div
              ref={alphaRef}
              className="relative mb-2 h-3 w-full cursor-pointer rounded-full"
              style={{
                backgroundImage: `linear-gradient(to right, transparent, ${rgbaToHex({ ...rgba, a: 1 }, false)}), repeating-conic-gradient(#ccc 0% 25%, #fff 0% 50%) 0 0 / 8px 8px`,
              }}
              onMouseDown={makeDragHandler(x => handleAlphaInteraction(x))}
            >
              <div
                className="pointer-events-none absolute top-1/2 h-4 w-4 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 border-white shadow-sm"
                style={{ left: `${alpha * 100}%` }}
              />
            </div>
          )}

          {/* Input fields */}
          <div className="mt-3 grid grid-cols-4 gap-2">
            <div className="col-span-1">
              <label className="mb-1 block text-[10px] font-medium text-foreground-muted">
                Hex
              </label>
              <input
                type="text"
                className={inputClass}
                value={hexInput}
                maxLength={9}
                onChange={e => setHexInput(e.target.value)}
                onBlur={handleHexCommit}
                onKeyDown={e => e.key === 'Enter' && handleHexCommit()}
              />
            </div>
            <div>
              <label className="mb-1 block text-[10px] font-medium text-foreground-muted">R</label>
              <input
                type="number"
                className={inputClass}
                min={0}
                max={255}
                value={Math.round(rgba.r)}
                onChange={e => handleRgbChange('r', parseInt(e.target.value, 10) || 0)}
              />
            </div>
            <div>
              <label className="mb-1 block text-[10px] font-medium text-foreground-muted">G</label>
              <input
                type="number"
                className={inputClass}
                min={0}
                max={255}
                value={Math.round(rgba.g)}
                onChange={e => handleRgbChange('g', parseInt(e.target.value, 10) || 0)}
              />
            </div>
            <div>
              <label className="mb-1 block text-[10px] font-medium text-foreground-muted">B</label>
              <input
                type="number"
                className={inputClass}
                min={0}
                max={255}
                value={Math.round(rgba.b)}
                onChange={e => handleRgbChange('b', parseInt(e.target.value, 10) || 0)}
              />
            </div>
          </div>

          {/* Presets */}
          {presets && presets.length > 0 && (
            <div className="mt-3 flex flex-wrap gap-1.5">
              {presets.map(preset => (
                <button
                  key={preset}
                  type="button"
                  aria-label={`Select color ${preset}`}
                  className="h-6 w-6 rounded-md border border-border-glass transition-all duration-200 hover:scale-110 hover:shadow-glow"
                  style={{ backgroundColor: preset }}
                  onClick={() => selectPreset(preset)}
                />
              ))}
            </div>
          )}
        </div>
      )}
    </div>
  )
}

export { ColorPicker }
