import { useRef, useState } from "react";
import {
  clientPointToMap,
  getObjectKey,
  itemsForZone,
} from "../../lib/locationMapUtils";

const DRAG_CHANGE_THRESHOLD = 2;

export default function LocationMapCanvas({
  mode,
  editorTool,
  objects,
  filters,
  mapSize,
  zoom,
  selectedObjectKey,
  mapItems,
  username,
  svgRef,
  dragState,
  setDragState,
  setSelectedObjectKey,
  remember,
  updateObjects,
}) {
  const canEditObjects = mode === "edit" && editorTool === "edit";
  const canDragPan = mode === "view" || editorTool === "pan";
  const scrollRef = useRef(null);
  const panDragRef = useRef(null);
  const objectDragHistoryCapturedRef = useRef(false);
  const suppressPanClickRef = useRef(false);
  const [isPanning, setIsPanning] = useState(false);

  const startDrag = (event, object, type) => {
    if (!canEditObjects || object.locked || !svgRef.current) return;
    event.stopPropagation();
    event.preventDefault();
    objectDragHistoryCapturedRef.current = false;
    const point = clientPointToMap(event, svgRef.current, mapSize);
    event.currentTarget.setPointerCapture?.(event.pointerId);
    setSelectedObjectKey(getObjectKey(object));
    setDragState({
      type,
      key: getObjectKey(object),
      startPoint: point,
      startObject: { ...object },
    });
  };

  const handlePointerMove = (event) => {
    if (!dragState || !svgRef.current) return;
    event.preventDefault();
    const point = clientPointToMap(event, svgRef.current, mapSize);
    const deltaX = point.x - dragState.startPoint.x;
    const deltaY = point.y - dragState.startPoint.y;
    const hasMeaningfulChange =
      Math.abs(deltaX) >= DRAG_CHANGE_THRESHOLD ||
      Math.abs(deltaY) >= DRAG_CHANGE_THRESHOLD;

    if (!hasMeaningfulChange) return;
    if (!objectDragHistoryCapturedRef.current) {
      remember();
      objectDragHistoryCapturedRef.current = true;
    }

    updateObjects((currentObjects) =>
      currentObjects.map((object) => {
        if (getObjectKey(object) !== dragState.key) return object;
        if (dragState.type === "resize") {
          return {
            ...object,
            width: Math.max(40, dragState.startObject.width + deltaX),
            height: Math.max(40, dragState.startObject.height + deltaY),
          };
        }
        return {
          ...object,
          x: dragState.startObject.x + deltaX,
          y: dragState.startObject.y + deltaY,
        };
      })
    );
  };

  const handlePointerUp = () => {
    objectDragHistoryCapturedRef.current = false;
    setDragState(null);
  };

  const handleObjectClick = (event, object) => {
    event.stopPropagation();
    if (suppressPanClickRef.current) {
      suppressPanClickRef.current = false;
      return;
    }
    if (mode === "view" || canEditObjects) {
      setSelectedObjectKey(getObjectKey(object));
    }
  };

  const clearSelection = () => {
    if (suppressPanClickRef.current) {
      suppressPanClickRef.current = false;
      return;
    }
    if (!dragState) {
      setSelectedObjectKey(null);
    }
  };

  const startPan = (event) => {
    if (!canDragPan || !scrollRef.current || event.button !== 0) return;
    const objectElement = event.target.closest?.(".location-map-object");
    event.currentTarget.setPointerCapture?.(event.pointerId);
    panDragRef.current = {
      startX: event.clientX,
      startY: event.clientY,
      scrollLeft: scrollRef.current.scrollLeft,
      scrollTop: scrollRef.current.scrollTop,
      objectKey: objectElement?.getAttribute("data-object-key") || null,
      moved: false,
    };
    setIsPanning(true);
  };

  const movePan = (event) => {
    if (!panDragRef.current || !scrollRef.current) return;
    const deltaX = event.clientX - panDragRef.current.startX;
    const deltaY = event.clientY - panDragRef.current.startY;
    if (Math.abs(deltaX) > 4 || Math.abs(deltaY) > 4) {
      panDragRef.current.moved = true;
      event.preventDefault();
    }
    scrollRef.current.scrollLeft = panDragRef.current.scrollLeft - deltaX;
    scrollRef.current.scrollTop = panDragRef.current.scrollTop - deltaY;
  };

  const endPan = (event) => {
    if (!panDragRef.current) return;
    const wasMoved = panDragRef.current.moved;
    const objectKey = panDragRef.current.objectKey;
    if (!wasMoved && mode === "view" && objectKey) {
      setSelectedObjectKey(objectKey);
      suppressPanClickRef.current = true;
    } else {
      suppressPanClickRef.current = wasMoved;
    }
    panDragRef.current = null;
    setIsPanning(false);
    event.currentTarget.releasePointerCapture?.(event.pointerId);
  };

  const truncateLabel = (value, maxCharacters) => {
    if (value.length <= maxCharacters) return value;
    return `${value.slice(0, Math.max(5, maxCharacters - 3)).trimEnd()}...`;
  };

  const fittedLabelLines = (object, fallback) => {
    const rawLabel = String(object.label || object.zone_name || fallback).trim();
    const maxCharacters = Math.max(8, Math.floor((Number(object.width) - 28) / 13));
    const canUseTwoLines = Number(object.height) >= 92 && rawLabel.length > maxCharacters;
    if (!canUseTwoLines) return [truncateLabel(rawLabel, maxCharacters)];

    const words = rawLabel.split(/\s+/);
    let firstLine = "";
    let splitIndex = 0;
    for (let index = 0; index < words.length; index += 1) {
      const candidate = firstLine ? `${firstLine} ${words[index]}` : words[index];
      if (candidate.length > maxCharacters && firstLine) break;
      firstLine = candidate;
      splitIndex = index + 1;
      if (candidate.length >= maxCharacters) break;
    }

    const secondLine = words.slice(splitIndex).join(" ");
    if (!secondLine) return [truncateLabel(firstLine, maxCharacters)];
    return [
      truncateLabel(firstLine, maxCharacters),
      truncateLabel(secondLine, maxCharacters),
    ];
  };

  const renderMapObject = (object, index) => {
    if (!object.visible || !filters.showZones) return null;
    const key = getObjectKey(object);
    const isSelected = key === selectedObjectKey;
    const zoneItems = itemsForZone(mapItems, object.zone_id, filters, username);
    const count = zoneItems.length;
    const countLabel = `${count} item${count === 1 ? "" : "s"}`;
    const labelLines = fittedLabelLines(object, "Area");
    const countY = filters.showLabels && labelLines.length > 1 ? object.y + 76 : object.y + 52;
    const resizeHandleSize = Math.min(96, Math.max(44, Math.ceil(48 / zoom)));
    const resizeHandleInset = 6;
    const resizeHandleX = Math.min(
      Math.max(0, mapSize.width - resizeHandleSize),
      Math.max(
        object.x + resizeHandleInset,
        object.x + object.width - resizeHandleSize - resizeHandleInset
      )
    );
    const resizeHandleY = Math.min(
      Math.max(0, mapSize.height - resizeHandleSize),
      Math.max(
        object.y + resizeHandleInset,
        object.y + object.height - resizeHandleSize - resizeHandleInset
      )
    );
    const pinDots = filters.showPins
      ? zoneItems.slice(0, 12).map((item, itemIndex) => {
        const column = itemIndex % 4;
        const row = Math.floor(itemIndex / 4);
        return (
          <circle
            key={item.id}
            className="location-map-pin"
            cx={object.x + 24 + column * 18}
            cy={object.y + 30 + row * 18}
            r="5"
          />
        );
      })
      : null;

    return (
      <g
        key={key}
        data-object-key={key}
        className={[
          "location-map-object",
          isSelected ? "is-selected" : "",
          canEditObjects ? "is-editable" : "",
        ].filter(Boolean).join(" ")}
        transform={`rotate(${object.rotation || 0} ${object.x + object.width / 2} ${object.y + object.height / 2})`}
      >
        <rect
          x={object.x}
          y={object.y}
          width={object.width}
          height={object.height}
          rx="12"
          role="button"
          aria-label={`Map area ${object.label || index + 1}`}
          onPointerDown={(event) => startDrag(event, object, "move")}
          onClick={(event) => handleObjectClick(event, object)}
        />
        {filters.showLabels ? (
          <text x={object.x + 16} y={object.y + 28} className="location-map-label">
            {labelLines.map((line, lineIndex) => (
              <tspan key={`${line}-${lineIndex}`} x={object.x + 16} dy={lineIndex === 0 ? 0 : 24}>
                {line}
              </tspan>
            ))}
          </text>
        ) : null}
        {filters.showCounts ? (
          <text x={object.x + 16} y={countY} className="location-map-count">
            {countLabel}
          </text>
        ) : null}
        {pinDots}
        {canEditObjects && isSelected ? (
          <rect
            className="location-map-resize-handle"
            x={resizeHandleX}
            y={resizeHandleY}
            width={resizeHandleSize}
            height={resizeHandleSize}
            rx="8"
            onPointerDown={(event) => startDrag(event, object, "resize")}
            onClick={(event) => event.stopPropagation()}
          />
        ) : null}
      </g>
    );
  };

  return (
    <section
      className={[
        "location-map-canvas-shell",
        mode === "edit" ? "is-edit-mode" : "is-view-mode",
        editorTool === "edit" ? "is-object-tool" : "is-pan-tool",
        isPanning ? "is-panning" : "",
      ].filter(Boolean).join(" ")}
    >
      <div
        ref={scrollRef}
        className="location-map-scroll"
        onPointerDown={startPan}
        onPointerMove={movePan}
        onPointerUp={endPan}
        onPointerCancel={endPan}
        onPointerLeave={endPan}
      >
        <svg
          ref={svgRef}
          className={[
            "location-map-svg",
            canEditObjects ? "is-editable" : "",
          ].filter(Boolean).join(" ")}
          style={{
            width: `${mapSize.width * zoom}px`,
            height: `${mapSize.height * zoom}px`,
          }}
          viewBox={`0 0 ${mapSize.width} ${mapSize.height}`}
          role="img"
          aria-label="Store location map"
          onPointerMove={handlePointerMove}
          onPointerUp={handlePointerUp}
          onPointerLeave={handlePointerUp}
          onClick={clearSelection}
        >
          <defs>
            <pattern id="location-map-grid" width="40" height="40" patternUnits="userSpaceOnUse">
              <path d="M 40 0 L 0 0 0 40" fill="none" />
            </pattern>
          </defs>
          <rect className="location-map-background" width={mapSize.width} height={mapSize.height} />
          {mode === "edit" ? (
            <rect className="location-map-grid" width={mapSize.width} height={mapSize.height} />
          ) : null}
          {objects.map(renderMapObject)}
        </svg>
        {objects.length === 0 ? (
          <div className="location-map-empty-canvas">
            No map areas yet
          </div>
        ) : null}
      </div>
    </section>
  );
}
