import { useEffect, useRef, useState, useCallback } from 'react'
import { useStore } from '@nanostores/react'
import { LFO, type LFOWaveform } from '../domain/modulation/LFO'
import { mappingMode } from '../stores/mappingMode'
import { parameterRegistry } from '../domain/modulation/ParameterRegistry'
import { MappingEditor } from './MappingEditor'

interface LFOScopeProps {
  lfoIndex: number
  waveform: LFOWaveform
  frequency: number
  phase: number
  mappings: Array<{ targetParam: string; depth: number }>
  onChange: (frequency: number, phase: number, waveform: LFOWaveform) => void
  onMapClick: () => void
  onUpdateDepth: (paramId: string, depth: number) => void
  onRemoveMapping: (paramId: string) => void
}

const WAVEFORMS: LFOWaveform[] = ['sine', 'triangle', 'square', 'sawtooth', 'random']

const CANVAS_WIDTH = 340
const CANVAS_HEIGHT = 60
const MIN_FREQ = 0.01
const MAX_FREQ = 20

export function LFOScope({ lfoIndex, waveform, frequency, phase, mappings, onChange, onMapClick, onUpdateDepth, onRemoveMapping }: LFOScopeProps) {
  const canvasRef = useRef<HTMLCanvasElement>(null)
  const lfoRef = useRef<LFO | null>(null)
  const animationRef = useRef<number | null>(null)
  const [isDragging, setIsDragging] = useState(false)
  const [showMappings, setShowMappings] = useState(false)
  const [showEditor, setShowEditor] = useState(false)
  const dragStartRef = useRef<{ x: number; y: number; freq: number; phase: number; moved: boolean } | null>(null)
  const mappingModeState = useStore(mappingMode)

  const getLFOValueAtPhase = useCallback((phaseVal: number): number => {
    const normalizedPhase = phaseVal % 1

    switch (waveform) {
      case 'sine':
        return Math.sin(normalizedPhase * 2 * Math.PI)
      case 'triangle':
        return normalizedPhase < 0.5
          ? -1 + 4 * normalizedPhase
          : 3 - 4 * normalizedPhase
      case 'square':
        return normalizedPhase < 0.5 ? 1 : -1
      case 'sawtooth':
        return 2 * normalizedPhase - 1
      case 'random':
        return Math.sin(normalizedPhase * 2 * Math.PI)
      default:
        return 0
    }
  }, [waveform])

  useEffect(() => {
    if (!lfoRef.current) {
      lfoRef.current = new LFO(new AudioContext(), frequency, phase, waveform)
    } else {
      lfoRef.current.setFrequency(frequency)
      lfoRef.current.setPhase(phase)
      lfoRef.current.setWaveform(waveform)
    }
  }, [frequency, phase, waveform])

  useEffect(() => {
    const canvas = canvasRef.current
    if (!canvas) return

    const ctx = canvas.getContext('2d')
    if (!ctx) return

    let time = 0

    const render = () => {
      if (!lfoRef.current) return

      ctx.fillStyle = '#000000'
      ctx.fillRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT)

      ctx.strokeStyle = '#ffffff'
      ctx.lineWidth = 2
      ctx.beginPath()

      const samples = CANVAS_WIDTH
      const centerY = CANVAS_HEIGHT / 2

      for (let x = 0; x < samples; x++) {
        const t = (x / samples) + time
        const phase = t % 1
        const value = getLFOValueAtPhase(phase)
        const y = centerY - (value * (centerY - 4))

        if (x === 0) {
          ctx.moveTo(x, y)
        } else {
          ctx.lineTo(x, y)
        }
      }

      ctx.stroke()

      ctx.fillStyle = '#ffffff'
      ctx.font = '9px monospace'
      ctx.textAlign = 'left'
      ctx.fillText(`${frequency.toFixed(2)}Hz`, 4, 12)
      ctx.fillText(`${phase.toFixed(0)}°`, 4, 24)

      time += frequency * 0.016
      animationRef.current = requestAnimationFrame(render)
    }

    render()

    return () => {
      if (animationRef.current) {
        cancelAnimationFrame(animationRef.current)
      }
    }
  }, [frequency, waveform, phase, getLFOValueAtPhase])

  const handleMouseDown = (e: React.MouseEvent<HTMLCanvasElement>) => {
    if (e.button === 2) return

    setIsDragging(true)
    dragStartRef.current = {
      x: e.clientX,
      y: e.clientY,
      freq: frequency,
      phase: phase,
      moved: false
    }
  }

  const handleMouseMove = (e: React.MouseEvent<HTMLCanvasElement>) => {
    if (!isDragging || !dragStartRef.current) return

    const deltaY = dragStartRef.current.y - e.clientY
    const deltaX = e.clientX - dragStartRef.current.x

    const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY)
    if (distance > 3) {
      dragStartRef.current.moved = true
    }

    if (!dragStartRef.current.moved) return

    const freqSensitivity = 0.05
    let newFreq = dragStartRef.current.freq * Math.exp(deltaY * freqSensitivity)
    newFreq = Math.max(MIN_FREQ, Math.min(MAX_FREQ, newFreq))

    const phaseSensitivity = 2
    let newPhase = (dragStartRef.current.phase + deltaX * phaseSensitivity) % 360
    if (newPhase < 0) newPhase += 360

    onChange(newFreq, newPhase, waveform)
  }

  const handleMouseUp = () => {
    if (dragStartRef.current && !dragStartRef.current.moved) {
      onMapClick()
    }
    setIsDragging(false)
    dragStartRef.current = null
  }

  const handleContextMenu = (e: React.MouseEvent<HTMLCanvasElement>) => {
    e.preventDefault()
    setShowEditor(true)
  }

  const handleDoubleClick = () => {
    const currentIndex = WAVEFORMS.indexOf(waveform)
    const nextIndex = (currentIndex + 1) % WAVEFORMS.length
    const nextWaveform = WAVEFORMS[nextIndex]
    onChange(frequency, phase, nextWaveform)
  }

  const isActive = mappingModeState.isActive && mappingModeState.activeLFO === lfoIndex
  const hasMappings = mappings.length > 0

  return (
    <div className="flex items-center gap-1 w-full relative">
      <canvas
        ref={canvasRef}
        width={CANVAS_WIDTH}
        height={CANVAS_HEIGHT}
        className={`border-2 border-white cursor-move flex-1 ${
          isActive ? 'animate-pulse' : ''
        }`}
        style={{ maxWidth: CANVAS_WIDTH }}
        onMouseDown={handleMouseDown}
        onMouseMove={handleMouseMove}
        onMouseUp={handleMouseUp}
        onMouseLeave={handleMouseUp}
        onContextMenu={handleContextMenu}
        onDoubleClick={handleDoubleClick}
        onMouseEnter={() => setShowMappings(true)}
        onMouseOut={() => setShowMappings(false)}
      />

      {isActive && (
        <div className="absolute top-2 right-2 bg-black border border-white px-2 py-1 pointer-events-none">
          <div className="font-mono text-[9px] text-white tracking-wider">
            CLICK PARAM TO MAP | ESC TO EXIT
          </div>
        </div>
      )}

      {showMappings && hasMappings && (
        <div className="absolute left-2 bottom-2 bg-black border border-white p-1 z-10 pointer-events-none">
          <div className="font-mono text-[8px] text-white">
            {mappings.map((m, i) => {
              const meta = parameterRegistry.getMetadata(m.targetParam)
              return (
                <div key={i} className="whitespace-nowrap">
                  {meta?.label ?? m.targetParam} ({m.depth}%)
                </div>
              )
            })}
          </div>
        </div>
      )}

      {showEditor && (
        <MappingEditor
          lfoIndex={lfoIndex}
          mappings={mappings}
          onUpdateDepth={onUpdateDepth}
          onRemoveMapping={onRemoveMapping}
          onClose={() => setShowEditor(false)}
        />
      )}
    </div>
  )
}
