import { getWidget } from "../widgets/registry";
import { createSignal, Show, onMount } from "solid-js";
import { Dynamic } from "solid-js/web";
import { createDraggable } from "@neodrag/solid";
import type { GridCell } from "../types/grid";
import type { WidgetConfig } from "../types/widget";

type DragEventData = {
  offsetX: number;
  offsetY: number;
  event: MouseEvent | TouchEvent;
  rootNode: HTMLElement;
  currentNode: HTMLElement;
};

interface WidgetRendererProps {
  config: WidgetConfig;
  onPositionUpdate?: (id: string, position: { x: number; y: number }) => void;
  onSizeUpdate?: (id: string, size: { width: number; height: number }) => void;
  onRemove?: (id: string) => void;
  onSnapToCell?: (widgetId: string, cellId: string | null) => void;
  onCellHover?: (cellId: string | null) => void;
  onDragStateChange?: (isDragging: boolean) => void;
  gridCells?: GridCell[];
  locked?: boolean;
}

interface Point {
  x: number;
  translate?: string;
  y: number;
}

interface Rect {
  x: number;
  y: number;
  width: number;
  height: number;
}

interface SnapResult {
  snapped: boolean;
  targetCell: GridCell | null;
  position: Point;
  size?: { width: number; height: number };
}

const SNAP_CONFIG = {
  threshold: 500,

  minOverlap: 0.15,

  magneticSnap: true,

  resizeOnSnap: true,
};

function getRectCenter(rect: Rect): Point {
  return {
    x: rect.x + rect.width / 2,
    y: rect.y + rect.height / 2,
  };
}

function getDistance(p1: Point, p2: Point): number {
  const dx = p2.x - p1.x;
  const dy = p2.y - p1.y;
  return Math.sqrt(dx * dx + dy * dy);
}

function getOverlapRatio(rect1: Rect, rect2: Rect): number {
  const xOverlap = Math.max(
    0,
    Math.min(rect1.x + rect1.width, rect2.x + rect2.width) -
      Math.max(rect1.x, rect2.x),
  );
  const yOverlap = Math.max(
    0,
    Math.min(rect1.y + rect1.height, rect2.y + rect2.height) -
      Math.max(rect1.y, rect2.y),
  );

  const overlapArea = xOverlap * yOverlap;
  const rect1Area = rect1.width * rect1.height;

  return rect1Area > 0 ? overlapArea / rect1Area : 0;
}

function findSnapTarget(
  widgetRect: Rect,
  cells: GridCell[],
  threshold: number,
): GridCell | null {
  if (cells.length === 0) return null;

  const widgetCenter = getRectCenter(widgetRect);
  let bestCell: GridCell | null = null;
  let bestOverlap = 0;

  console.log("[Snap Debug] Finding target:", {
    widgetRect,
    widgetCenter,
    cellsCount: cells.length,
    threshold,
    minOverlap: SNAP_CONFIG.minOverlap,
  });

  for (const cell of cells) {
    const cellCenter = getRectCenter(cell);
    const distance = getDistance(widgetCenter, cellCenter);

    if (distance > threshold) continue;

    const overlap = getOverlapRatio(widgetRect, cell);

    console.log("[Snap Debug] Checking cell:", {
      cellId: cell.id,
      cellCenter,
      distance: distance.toFixed(1),
      overlap: overlap.toFixed(3),
      withinThreshold: distance <= threshold,
      meetsMinOverlap: overlap >= SNAP_CONFIG.minOverlap,
    });

    if (overlap < SNAP_CONFIG.minOverlap) continue;

    if (
      overlap > bestOverlap ||
      (overlap === bestOverlap &&
        bestCell &&
        distance < getDistance(widgetCenter, getRectCenter(bestCell)))
    ) {
      bestOverlap = overlap;
      bestCell = cell;

      console.log("[Snap Debug] New best cell:", {
        cellId: cell.id,
        overlap: overlap.toFixed(3),
        distance: distance.toFixed(1),
      });
    }
  }

  console.log("[Snap Debug] Final best cell:", {
    cellId: bestCell?.id || "none",
    overlap: bestOverlap.toFixed(3),
  });

  return bestCell;
}

function calculateSnap(
  widgetPosition: Point,
  widgetSize: { width: number; height: number },
  cells: GridCell[],
  enableSnap: boolean,
): SnapResult {
  if (!enableSnap || cells.length === 0) {
    return {
      snapped: false,
      targetCell: null,
      position: widgetPosition,
    };
  }

  const widgetRect: Rect = {
    x: widgetPosition.x,
    y: widgetPosition.y,
    width: widgetSize.width,
    height: widgetSize.height,
  };

  const targetCell = findSnapTarget(widgetRect, cells, SNAP_CONFIG.threshold);

  if (targetCell) {
    return {
      snapped: true,
      targetCell,
      position: { x: targetCell.x, y: targetCell.y },
      size: SNAP_CONFIG.resizeOnSnap
        ? { width: targetCell.width, height: targetCell.height }
        : undefined,
    };
  }

  return {
    snapped: false,
    targetCell: null,
    position: widgetPosition,
  };
}

export function WidgetRenderer(props: WidgetRendererProps) {
  const widgetDef = getWidget(props.config.type);
  const { draggable } = createDraggable();
  const [isDragging, setIsDragging] = createSignal(false);
  const [isResizing, setIsResizing] = createSignal(false);

  let parentEl: HTMLElement | null = null;
  let widgetEl: HTMLElement | undefined;

  let resizeStartSize: { width: number; height: number } = {
    width: 0,
    height: 0,
  };
  let resizeStartPos: { x: number; y: number } = { x: 0, y: 0 };

  let dragStartWidgetPos: Point | null = null;
  let dragStartPointerPos: Point | null = null;

  const position = () => props.config.position || { x: 10, y: 10 };
  const size = () => props.config.size || { width: 200, height: 150 };

  onMount(() => {
    parentEl = document.querySelector("[data-grid-container]") as HTMLElement;
  });

  const getContainerScale = (): number => {
    if (!parentEl) return 1;
    const transform = window.getComputedStyle(parentEl).transform;
    if (!transform || transform === "none") return 1;

    try {
      const matrixMatch = transform.match(/matrix\(([^)]+)\)/);
      if (matrixMatch) {
        const values = matrixMatch[1]
          .split(",")
          .map((v) => parseFloat(v.trim()));
        return values[0] || 1;
      }

      const matrix3dMatch = transform.match(/matrix3d\(([^)]+)\)/);
      if (matrix3dMatch) {
        const values = matrix3dMatch[1]
          .split(",")
          .map((v) => parseFloat(v.trim()));
        return values[0] || 1;
      }
    } catch {
      return 1;
    }
    return 1;
  };

  const screenToGrid = (screenX: number, screenY: number): Point | null => {
    if (!parentEl) return null;

    const rect = parentEl.getBoundingClientRect();
    const scale = getContainerScale();

    return {
      x: (screenX - rect.left) / scale,
      y: (screenY - rect.top) / scale,
    };
  };

  const getWidgetGridPosition = (): Point => {
    if (!widgetEl || !parentEl) return position();

    const widgetRect = widgetEl.getBoundingClientRect();
    const parentRect = parentEl.getBoundingClientRect();
    const scale = getContainerScale();

    return {
      x: (widgetRect.left - parentRect.left) / scale,
      y: (widgetRect.top - parentRect.top) / scale,
    };
  };

  const handleDragStart = (data?: DragEventData) => {
    setIsDragging(true);
    props.onDragStateChange?.(true);

    if (!data || !data.event) return;

    const event = data.event;
    const clientX =
      "clientX" in event
        ? event.clientX
        : (event as TouchEvent).touches[0].clientX;
    const clientY =
      "clientY" in event
        ? event.clientY
        : (event as TouchEvent).touches[0].clientY;

    console.log("[Snap Debug] DRAG START", {
      widgetId: props.config.id,
      clientX,
      clientY,
      gridCellsCount: props.gridCells?.length || 0,
    });

    dragStartPointerPos = screenToGrid(clientX, clientY);
    dragStartWidgetPos = getWidgetGridPosition();

    console.log("[Snap Debug] Drag start positions:", {
      dragStartPointerPos,
      dragStartWidgetPos,
    });
  };

  const handleDrag = (data: DragEventData) => {
    if (!props.gridCells || props.gridCells.length === 0) {
      props.onCellHover?.(null);
      return;
    }

    if (!data.event) return;

    const event = data.event;
    const clientX =
      "clientX" in event
        ? event.clientX
        : (event as TouchEvent).touches[0].clientX;
    const clientY =
      "clientY" in event
        ? event.clientY
        : (event as TouchEvent).touches[0].clientY;

    const pointerPos = screenToGrid(clientX, clientY);
    if (!pointerPos || !dragStartPointerPos || !dragStartWidgetPos) return;

    const offset = {
      x: pointerPos.x - dragStartPointerPos.x,
      y: pointerPos.y - dragStartPointerPos.y,
    };

    const currentPos = {
      x: dragStartWidgetPos.x + offset.x,
      y: dragStartWidgetPos.y + offset.y,
    };

    // Check for snap target
    const snapResult = calculateSnap(
      currentPos,
      size(),
      props.gridCells,
      SNAP_CONFIG.magneticSnap,
    );

    console.log("[Snap Debug] Drag:", {
      widgetId: props.config.id,
      currentPos,
      widgetSize: size(),
      cellsCount: props.gridCells.length,
      snapResult: {
        snapped: snapResult.snapped,
        targetCellId: snapResult.targetCell?.id,
      },
    });

    props.onCellHover?.(snapResult.targetCell?.id || null);
  };

  const handleDragEnd = (data: DragEventData) => {
    setIsDragging(false);
    props.onDragStateChange?.(false);
    props.onCellHover?.(null);

    if (!data.event) {
      dragStartWidgetPos = null;
      dragStartPointerPos = null;
      return;
    }

    const event = data.event;
    const clientX =
      "clientX" in event
        ? event.clientX
        : (event as TouchEvent).touches[0]?.clientX;
    const clientY =
      "clientY" in event
        ? event.clientY
        : (event as TouchEvent).touches[0]?.clientY;

    const pointerPos = screenToGrid(clientX, clientY);
    if (!pointerPos || !dragStartPointerPos || !dragStartWidgetPos) {
      dragStartWidgetPos = null;
      dragStartPointerPos = null;
      return;
    }

    const offset = {
      x: pointerPos.x - dragStartPointerPos.x,
      y: pointerPos.y - dragStartPointerPos.y,
    };

    const finalPos = {
      x: dragStartWidgetPos.x + offset.x,
      y: dragStartWidgetPos.y + offset.y,
    };

    const snapResult = calculateSnap(
      finalPos,
      size(),
      props.gridCells || [],
      true,
    );

    props.onPositionUpdate?.(props.config.id, {
      x: Math.round(snapResult.position.x),
      y: Math.round(snapResult.position.y),
    });

    if (snapResult.snapped && snapResult.size) {
      props.onSizeUpdate?.(props.config.id, {
        width: Math.round(snapResult.size.width),
        height: Math.round(snapResult.size.height),
      });
    }

    props.onSnapToCell?.(props.config.id, snapResult.targetCell?.id || null);

    dragStartWidgetPos = null;
    dragStartPointerPos = null;
  };

  const handleResizeStart = (e: MouseEvent) => {
    e.preventDefault();
    e.stopPropagation();
    setIsResizing(true);
    resizeStartSize = size();
    resizeStartPos = { x: e.clientX, y: e.clientY };

    const handleResizeMove = (moveEvent: MouseEvent) => {
      const deltaX = moveEvent.clientX - resizeStartPos.x;
      const deltaY = moveEvent.clientY - resizeStartPos.y;

      const newWidth = Math.max(150, resizeStartSize.width + deltaX);
      const newHeight = Math.max(100, resizeStartSize.height + deltaY);

      props.onSizeUpdate?.(props.config.id, {
        width: Math.round(newWidth),
        height: Math.round(newHeight),
      });
    };

    const handleResizeEnd = () => {
      setIsResizing(false);
      document.removeEventListener("mousemove", handleResizeMove);
      document.removeEventListener("mouseup", handleResizeEnd);
    };

    document.addEventListener("mousemove", handleResizeMove);
    document.addEventListener("mouseup", handleResizeEnd);
  };

  return (
    <Show when={widgetDef}>
      {(widget) => (
        <div
          ref={widgetEl}
          use:draggable={{
            disabled: props.locked || isResizing(),
            position: position(),
            bounds: "parent",
            cancel: "button, input, select, textarea, .resize-handle",
            onDragStart: handleDragStart,
            onDrag: handleDrag,
            onDragEnd: handleDragEnd,
            gpuAcceleration: true,
            applyUserSelectHack: true,
          }}
          data-widget-id={props.config.id}
          style={{
            position: "absolute",
            width: `${size().width}px`,
            height: `${size().height}px`,
            cursor: props.locked
              ? "default"
              : isDragging()
                ? "grabbing"
                : "grab",
            "user-select": "none",
            transition:
              isDragging() || isResizing() ? "none" : "box-shadow 0.2s",
            "box-shadow":
              isDragging() || isResizing()
                ? "0 8px 16px rgba(0,0,0,0.2)"
                : "0 2px 4px rgba(0,0,0,0.1)",
            "z-index": isDragging() || isResizing() ? "100" : "1",
          }}
        >
          {!props.locked && props.onRemove && (
            <button
              onClick={(e) => {
                e.stopPropagation();
                props.onRemove?.(props.config.id);
              }}
              style={{
                position: "absolute",
                top: "-8px",
                right: "-8px",
                "z-index": "10",
                padding: "0",
                "font-size": "1rem",
                background: "#ff4444",
                color: "white",
                border: "none",
                "border-radius": "50%",
                cursor: "pointer",
                width: "24px",
                height: "24px",
                display: "flex",
                "align-items": "center",
                "justify-content": "center",
                "font-weight": "bold",
              }}
            >
              ×
            </button>
          )}

          {!props.locked &&
            (!props.gridCells || props.gridCells.length === 0) && (
              <div
                class="resize-handle"
                onMouseDown={handleResizeStart}
                style={{
                  position: "absolute",
                  bottom: "0",
                  right: "0",
                  width: "20px",
                  height: "20px",
                  cursor: "nwse-resize",
                  "z-index": "10",
                  background:
                    "linear-gradient(135deg, transparent 50%, rgba(0,0,0,0.2) 50%)",
                  "border-bottom-right-radius": "4px",
                }}
              />
            )}

          <div style={{ width: "100%", height: "100%", overflow: "auto" }}>
            <Dynamic
              component={widget().Component}
              settings={props.config.settings}
            />
          </div>
        </div>
      )}
    </Show>
  );
}
