import React, { useCallback, useMemo, useRef, useState } from 'react'
import type { Frame, WObject } from '@/lib/wysiwyg/schema'

function snap8(v: number) {
  return Math.round(v / 8) * 8
}

export type CanvasProps = {
  frame: Frame
  onChange?: (next: Frame) => void
}

export function Canvas({ frame, onChange }: CanvasProps) {
  const [model, setModel] = useState<Frame>(frame)
  const [selectedId, setSelectedId] = useState<string | null>(model.layers[0]?.objects[0]?.id ?? null)
  const draggingRef = useRef<{ id: string; startX: number; startY: number; origX: number; origY: number } | null>(null)

  const selected = useMemo(() => {
    for (const layer of model.layers) {
      const obj = layer.objects.find((o) => o.id === selectedId)
      if (obj) return obj
    }
    return null
  }, [model, selectedId])

  const updateObject = useCallback((id: string, updater: (o: WObject) => WObject) => {
    setModel((prev) => {
      const next: Frame = { ...prev, layers: prev.layers.map((ly) => ({ ...ly, objects: ly.objects.map((o) => (o.id === id ? updater(o) : o)) })) }
      onChange?.(next)
      return next
    })
  }, [onChange])

  const onKeyDown = useCallback((e: React.KeyboardEvent) => {
    if (!selected) return
    let dx = 0, dy = 0
    if (e.key === 'ArrowLeft') dx = -1
    else if (e.key === 'ArrowRight') dx = 1
    else if (e.key === 'ArrowUp') dy = -1
    else if (e.key === 'ArrowDown') dy = 1
    else return
    e.preventDefault()
    updateObject(selected.id, (o) => {
      const nx = snap8(o.x + dx)
      const ny = snap8(o.y + dy)
      return { ...o, x: nx, y: ny }
    })
  }, [selected, updateObject])

  const onMouseDownObj = useCallback((e: React.MouseEvent, o: WObject) => {
    setSelectedId(o.id)
    draggingRef.current = { id: o.id, startX: e.clientX, startY: e.clientY, origX: o.x, origY: o.y }
  }, [])

  const onMouseMoveCanvas = useCallback((e: React.MouseEvent) => {
    const d = draggingRef.current
    if (!d) return
    const dx = e.clientX - d.startX
    const dy = e.clientY - d.startY
    const nx = snap8(d.origX + dx)
    const ny = snap8(d.origY + dy)
    updateObject(d.id, (o) => ({ ...o, x: nx, y: ny }))
  }, [updateObject])

  const onMouseUpCanvas = useCallback(() => {
    draggingRef.current = null
  }, [])

  return (
    <div
      role="application"
      aria-label="WYSIWYG Canvas"
      tabIndex={0}
      onKeyDown={onKeyDown}
      onMouseMove={onMouseMoveCanvas}
      onMouseUp={onMouseUpCanvas}
      style={{ position: 'relative', width: model.width, height: model.height, outline: '0' }}
      data-testid="wysiwyg-canvas"
    >
      {model.layers.map((layer) => layer.visible !== false && layer.objects.map((o) => (
        <div
          key={o.id}
          data-testid={`obj-${o.id}`}
          aria-label={`object ${o.id}`}
          onMouseDown={(e) => onMouseDownObj(e, o)}
          style={{ position: 'absolute', left: o.x, top: o.y, width: o.width, height: o.height, transform: `rotate(${o.rotate}deg)`, outline: selectedId===o.id? '2px solid #0ea5e9':'none' }}
        />
      )))}
    </div>
  )
}
