/**
 * BaseElement Component
 *
 * Provides common functionality for all canvas elements:
 * - Dragging with snapping
 * - Selection handling
 * - Hover effects
 * - Boundary constraints
 *
 * This is a wrapper that element renderers use for consistent behavior.
 */

import {useRef, useCallback, type ReactNode} from "react";
import {Group, Rect, Label, Tag, Text} from "react-konva";
import type {KonvaEventObject} from "konva/lib/Node";
import type Konva from "konva";
import {useCanvasStore} from "@/stores/canvasStore";
import {findSnapGuides} from "../SnapGuides";
import type {CanvasElement} from "@/types";

interface BaseElementProps {
  element: CanvasElement;
  containerSize: {width: number; height: number};
  children: ReactNode;
}

export function BaseElement({
  element,
  containerSize,
  children,
}: BaseElementProps) {
  const groupRef = useRef<Konva.Group>(null);

  // Use individual selectors to avoid unnecessary re-renders
  const selectedIds = useCanvasStore((state) => state.selectedIds);
  const hoveredId = useCanvasStore((state) => state.hoveredId);
  const elements = useCanvasStore((state) => state.elements);
  const snapToGuides = useCanvasStore((state) => state.snapToGuides);
  const selectElement = useCanvasStore((state) => state.selectElement);
  const setHoveredId = useCanvasStore((state) => state.setHoveredId);
  const moveElement = useCanvasStore((state) => state.moveElement);
  const setActiveGuides = useCanvasStore((state) => state.setActiveGuides);

  const isSelected = selectedIds.includes(element.id);
  const isHovered = hoveredId === element.id;

  // Handle drag start
  const handleDragStart = useCallback(() => {
    if (!selectedIds.includes(element.id)) {
      selectElement(element.id);
    }
  }, [element.id, selectedIds, selectElement]);

  // Handle drag move - apply snapping and boundary constraints
  const handleDragMove = useCallback(
    (e: KonvaEventObject<DragEvent>) => {
      const node = e.target;
      let currentX = node.x();
      let currentY = node.y();

      // Constrain to canvas bounds
      const minX = 0;
      const minY = 0;
      const maxX = containerSize.width - element.size.width;
      const maxY = containerSize.height - element.size.height;

      currentX = Math.max(minX, Math.min(maxX, currentX));
      currentY = Math.max(minY, Math.min(maxY, currentY));

      // Apply boundary constraints
      node.x(currentX);
      node.y(currentY);

      // Apply snapping if enabled
      if (snapToGuides) {
        const tempElement: CanvasElement = {
          ...element,
          position: {x: currentX, y: currentY},
        };

        const {guides, snapPosition} = findSnapGuides(
          tempElement,
          elements,
          containerSize,
          5
        );

        if (snapPosition.x !== undefined) {
          const snappedX = Math.max(minX, Math.min(maxX, snapPosition.x));
          node.x(snappedX);
        }
        if (snapPosition.y !== undefined) {
          const snappedY = Math.max(minY, Math.min(maxY, snapPosition.y));
          node.y(snappedY);
        }

        setActiveGuides(guides);
      }
    },
    [element, elements, containerSize, snapToGuides, setActiveGuides]
  );

  // Handle drag end
  const handleDragEnd = useCallback(
    (e: KonvaEventObject<DragEvent>) => {
      const node = e.target;
      moveElement(element.id, {
        x: node.x(),
        y: node.y(),
      });
      setActiveGuides([]);
    },
    [element.id, moveElement, setActiveGuides]
  );

  // Handle click/select
  const handleClick = useCallback(
    (e: KonvaEventObject<MouseEvent>) => {
      e.cancelBubble = true;
      const isMultiSelect = e.evt.shiftKey || e.evt.ctrlKey || e.evt.metaKey;
      selectElement(element.id, isMultiSelect);
    },
    [element.id, selectElement]
  );

  // Handle hover
  const handleMouseEnter = useCallback(() => {
    setHoveredId(element.id);
    document.body.style.cursor = "move";
  }, [element.id, setHoveredId]);

  const handleMouseLeave = useCallback(() => {
    setHoveredId(null);
    document.body.style.cursor = "default";
  }, [setHoveredId]);

  return (
    <Group
      ref={groupRef}
      id={element.id}
      name={element.id}
      x={element.position.x}
      y={element.position.y}
      width={element.size.width}
      height={element.size.height}
      rotation={element.rotation}
      opacity={element.opacity}
      draggable={!element.locked}
      onDragStart={handleDragStart}
      onDragMove={handleDragMove}
      onDragEnd={handleDragEnd}
      onClick={handleClick}
      onTap={handleClick}
      onMouseEnter={handleMouseEnter}
      onMouseLeave={handleMouseLeave}
    >
      {/* Hit area - ensures the entire element is draggable */}
      <Rect
        width={element.size.width}
        height={element.size.height}
        fill="transparent"
      />

      {/* Background */}
      {element.backgroundColor !== "transparent" && (
        <Rect
          width={element.size.width}
          height={element.size.height}
          fill={element.backgroundColor}
          cornerRadius={element.border.radius}
          listening={false}
        />
      )}

      {/* Border */}
      {element.border.width.desktop > 0 && (
        <Rect
          width={element.size.width}
          height={element.size.height}
          stroke={element.border.color}
          strokeWidth={element.border.width.desktop}
          cornerRadius={element.border.radius}
          listening={false}
        />
      )}

      {/* Hover/Selection highlight */}
      {(isHovered || isSelected) && (
        <Rect
          width={element.size.width}
          height={element.size.height}
          stroke={isSelected ? "#3b82f6" : "#93c5fd"}
          strokeWidth={isSelected ? 2 : 1}
          cornerRadius={element.border.radius}
          dash={isHovered && !isSelected ? [4, 4] : undefined}
          listening={false}
        />
      )}

      {/* Element-specific content */}
      {children}

      {/* Element label (visible when hovered) */}
      {isHovered && !isSelected && (
        <Label x={0} y={-24} listening={false}>
          <Tag
            fill="#3b82f6"
            cornerRadius={4}
            pointerDirection="down"
            pointerWidth={6}
            pointerHeight={4}
          />
          <Text
            text={element.name}
            fill="#ffffff"
            fontSize={11}
            fontFamily="Inter, system-ui, sans-serif"
            padding={4}
          />
        </Label>
      )}
    </Group>
  );
}
