import { h } from 'preact';
import { useRef, useEffect, useState } from 'preact/hooks';
import type { Interface, Widget, SignalRef } from './lib/types';
import TextView from './widgets/TextView';
import TextLabel from './widgets/TextLabel';
import Gauge from './widgets/Gauge';
import BarH from './widgets/BarH';
import BarV from './widgets/BarV';
import Led from './widgets/Led';
import MultiLed from './widgets/MultiLed';
import SetValue from './widgets/SetValue';
import Button from './widgets/Button';
import PlotWidget from './widgets/PlotWidget';
import ImageWidget from './widgets/ImageWidget';
import LinkWidget from './widgets/LinkWidget';
import WidgetTypePicker from './WidgetTypePicker';

const COMPONENTS: Record<string, any> = {
  textview: TextView, textlabel: TextLabel, gauge: Gauge,
  barh: BarH, barv: BarV, led: Led, multiled: MultiLed,
  setvalue: SetValue, button: Button, plot: PlotWidget,
  image: ImageWidget, link: LinkWidget,
};

const DEFAULT_SIZES: Record<string, [number, number]> = {
  textview:  [200, 50],
  textlabel: [150, 36],
  gauge:     [120, 120],
  barh:      [200, 60],
  barv:      [60,  160],
  led:       [60,  60],
  multiled:  [200, 80],
  setvalue:  [200, 60],
  button:    [120, 50],
  plot:      [400, 200],
  image:     [200, 150],
  link:      [140, 50],
};

interface PickerState {
  visible: boolean;
  x: number;
  y: number;
  canvasX: number;
  canvasY: number;
  signal: SignalRef | null;
}

interface DragState {
  widgetId: string;
  startMouseX: number;
  startMouseY: number;
  startWidgetX: number;
  startWidgetY: number;
}

interface ResizeState {
  widgetId: string;
  handle: string; // 'se' | 'sw' | 'ne' | 'nw' | 'n' | 's' | 'e' | 'w'
  startMouseX: number;
  startMouseY: number;
  startX: number;
  startY: number;
  startW: number;
  startH: number;
}

interface Props {
  iface: Interface;
  selectedId: string | null;
  onSelect: (id: string | null) => void;
  onChange: (widgets: Widget[]) => void;
}

let nextId = Date.now();
function genId() { return `w${nextId++}`; }

export default function EditCanvas({ iface, selectedId, onSelect, onChange }: Props) {
  const containerRef = useRef<HTMLDivElement>(null);
  const dragRef = useRef<DragState | null>(null);
  const resizeRef = useRef<ResizeState | null>(null);
  const [picker, setPicker] = useState<PickerState>({
    visible: false, x: 0, y: 0, canvasX: 0, canvasY: 0, signal: null,
  });

  // Document-level move/up handlers for drag and resize
  useEffect(() => {
    function onMouseMove(e: MouseEvent) {
      if (dragRef.current) {
        const d = dragRef.current;
        const dx = e.clientX - d.startMouseX;
        const dy = e.clientY - d.startMouseY;
        const widgets = iface.widgets.map(w =>
          w.id === d.widgetId
            ? { ...w, x: Math.round(d.startWidgetX + dx), y: Math.round(d.startWidgetY + dy) }
            : w
        );
        onChange(widgets);
      }
      if (resizeRef.current) {
        const r = resizeRef.current;
        const dx = e.clientX - r.startMouseX;
        const dy = e.clientY - r.startMouseY;
        let { startX: x, startY: y, startW: w, startH: h } = r;
        const handle = r.handle;
        if (handle.includes('e')) w = Math.max(20, w + dx);
        if (handle.includes('s')) h = Math.max(20, h + dy);
        if (handle.includes('w')) { x = x + dx; w = Math.max(20, w - dx); }
        if (handle.includes('n')) { y = y + dy; h = Math.max(20, h - dy); }
        const widgets = iface.widgets.map(widget =>
          widget.id === r.widgetId
            ? { ...widget, x: Math.round(x), y: Math.round(y), w: Math.round(w), h: Math.round(h) }
            : widget
        );
        onChange(widgets);
      }
    }
    function onMouseUp() {
      dragRef.current = null;
      resizeRef.current = null;
    }
    document.addEventListener('mousemove', onMouseMove);
    document.addEventListener('mouseup', onMouseUp);
    return () => {
      document.removeEventListener('mousemove', onMouseMove);
      document.removeEventListener('mouseup', onMouseUp);
    };
  }, [iface.widgets, onChange]);

  function handleCanvasClick(e: MouseEvent) {
    if ((e.target as Element).closest('.widget-overlay')) return;
    onSelect(null);
  }

  function handleDragOver(e: DragEvent) {
    e.preventDefault();
    if (e.dataTransfer) e.dataTransfer.dropEffect = 'copy';
  }

  function handleDrop(e: DragEvent) {
    e.preventDefault();
    const json = e.dataTransfer?.getData('application/json');
    if (!json) return;
    let sig: SignalRef;
    try { sig = JSON.parse(json); } catch { return; }
    const rect = containerRef.current?.getBoundingClientRect();
    if (!rect) return;
    const scrollLeft = containerRef.current?.scrollLeft ?? 0;
    const scrollTop = containerRef.current?.scrollTop ?? 0;
    const cx = e.clientX - rect.left + scrollLeft;
    const cy = e.clientY - rect.top + scrollTop;
    setPicker({ visible: true, x: e.clientX, y: e.clientY, canvasX: cx, canvasY: cy, signal: sig });
  }

  function handlePick(type: string) {
    if (!picker.signal) return;
    const [defW, defH] = DEFAULT_SIZES[type] ?? [150, 60];
    const newWidget: Widget = {
      id: genId(),
      type,
      x: Math.round(picker.canvasX - defW / 2),
      y: Math.round(picker.canvasY - defH / 2),
      w: defW,
      h: defH,
      signals: [picker.signal],
      options: {},
    };
    onChange([...iface.widgets, newWidget]);
    onSelect(newWidget.id);
    setPicker(p => ({ ...p, visible: false }));
  }

  function handleDeleteWidget(id: string) {
    onChange(iface.widgets.filter(w => w.id !== id));
    if (selectedId === id) onSelect(null);
  }

  function startDrag(e: MouseEvent, widget: Widget) {
    e.stopPropagation();
    onSelect(widget.id);
    dragRef.current = {
      widgetId: widget.id,
      startMouseX: e.clientX,
      startMouseY: e.clientY,
      startWidgetX: widget.x,
      startWidgetY: widget.y,
    };
  }

  function startResize(e: MouseEvent, widget: Widget, handle: string) {
    e.stopPropagation();
    e.preventDefault();
    resizeRef.current = {
      widgetId: widget.id,
      handle,
      startMouseX: e.clientX,
      startMouseY: e.clientY,
      startX: widget.x,
      startY: widget.y,
      startW: widget.w,
      startH: widget.h,
    };
  }

  return (
    <div
      class="edit-canvas-container"
      ref={containerRef}
      onClick={handleCanvasClick}
      onDragOver={handleDragOver}
      onDrop={handleDrop}
    >
      <div
        class="canvas-area"
        style={`width:${iface.w}px;height:${iface.h}px;`}
      >
        {/* Render live widget previews */}
        {iface.widgets.map(widget => {
          const Comp = COMPONENTS[widget.type];
          return Comp
            ? <Comp key={widget.id} widget={widget} />
            : (
              <div
                key={widget.id}
                class="unknown-widget"
                style={`left:${widget.x}px;top:${widget.y}px;width:${widget.w}px;height:${widget.h}px;`}
              >
                <span class="unknown-label">{widget.type}</span>
              </div>
            );
        })}

        {/* Overlay divs for interaction — sit above the live widgets */}
        {iface.widgets.map(widget => {
          const isSelected = widget.id === selectedId;
          return (
            <div
              key={`overlay-${widget.id}`}
              class={`widget-overlay${isSelected ? ' selected' : ''}`}
              style={`left:${widget.x}px;top:${widget.y}px;width:${widget.w}px;height:${widget.h}px;`}
              onMouseDown={(e: MouseEvent) => startDrag(e, widget)}
              onClick={(e: MouseEvent) => { e.stopPropagation(); onSelect(widget.id); }}
            >
              {isSelected && (
                <div>
                  {/* Delete button */}
                  <button
                    class="overlay-delete"
                    onMouseDown={(e: MouseEvent) => e.stopPropagation()}
                    onClick={(e: MouseEvent) => { e.stopPropagation(); handleDeleteWidget(widget.id); }}
                    title="Delete widget"
                  >✕</button>
                  {/* Resize handles */}
                  {(['n','s','e','w','ne','nw','se','sw'] as const).map(dir => (
                    <div
                      key={dir}
                      class={`resize-handle resize-${dir}`}
                      onMouseDown={(e: MouseEvent) => startResize(e, widget, dir)}
                    />
                  ))}
                </div>
              )}
            </div>
          );
        })}
      </div>

      {picker.visible && (
        <WidgetTypePicker
          x={picker.x}
          y={picker.y}
          onPick={handlePick}
          onCancel={() => setPicker(p => ({ ...p, visible: false }))}
        />
      )}
    </div>
  );
}
