import { useEffect, useRef, useState } from "react";
import { Stage, Layer, Rect } from "react-konva";
import Konva from "konva";
import { useEditorStore } from "../store/editorStore";
import { useDrawingHandlers } from "./canvas/useDrawingHandlers";
import { useStageSize } from "./canvas/useStageSize";
import { useKeyboardMovement } from "./canvas/useKeyboardMovement";
import { useSelectionHandlers } from "./canvas/useSelectionHandlers";
import { useTransformHandlers } from "./canvas/useTransformHandlers";
import { useObjectHandlers } from "./canvas/useObjectHandlers";
import { useMaskGroupState } from "./canvas/useMaskGroupState";
import ObjectsLayer from "./canvas/ObjectsLayer";
import GuidesLayer from "./canvas/GuidesLayer";
import { getSnappedPosition, type Guide } from "./canvas/useSnap";
import CellEditor from "@/components/CellEditor";
import ZoomControls from "./ZoomControls";

type EditorCanvasProps = {
  catalogSize?: string;
};

const EditorCanvas = ({ catalogSize }: EditorCanvasProps) => {
  const stageRef = useRef<Konva.Stage>(null);
  const layerRef = useRef<Konva.Layer>(null);
  const activeGuideIdsRef = useRef<string[]>([]);
  const stageWrapRef = useRef<HTMLDivElement>(null);
  const draggingGuideRef = useRef<{
    id: string;
    orientation: Guide["orientation"];
  } | null>(null);

  const {
    tool,
    objects,
    guides,
    selectedObjectId,
    selectedObjectIds,
    selectedCellId,
    editingCell,
    setStageRef,
    setLayerRef,
    setGuides,
    zoom,
  } = useEditorStore();

  const { transformerRef, handleStageMouseDown, handleStageMouseMove, handleStageMouseUp } = useDrawingHandlers();
  const stageSize = useStageSize(catalogSize);
  useKeyboardMovement();

  const { handleSelect, handleCellClick, handleCellDblClick } = useSelectionHandlers();
  const { handleObjectUpdate, handleGroupWithMask, handleUngroupFromMask, handleCellEditorSave, handleCellEditorCancel } =
    useObjectHandlers();

  const { isGroupedWithMask, maskObject, canGroup, canUngroup, selectedObject } = useMaskGroupState(selectedObjectId);

  useTransformHandlers(transformerRef, layerRef);

  const [activeGuideIds, setActiveGuideIds] = useState<string[]>([]);
  const [selectedGuideId, setSelectedGuideId] = useState<string | null>(null);
  const [draftGuide, setDraftGuide] = useState<{
    orientation: Guide["orientation"];
    position: number;
  } | null>(null);
  const cleanupRulerDragRef = useRef<(() => void) | null>(null);

  useEffect(() => {
    if (stageRef.current) {
      setStageRef(stageRef);
    }
    if (layerRef.current) {
      setLayerRef(layerRef);
    }
  }, [setStageRef, setLayerRef]);


  const finalScale = stageSize.scale * zoom;
  const updateActiveGuides = (nextActiveGuideIds: string[]) => {
    const prev = activeGuideIdsRef.current;
    const changed =
      prev.length !== nextActiveGuideIds.length ||
      prev.some((id, index) => id !== nextActiveGuideIds[index]);

    if (changed) {
      activeGuideIdsRef.current = nextActiveGuideIds;
      setActiveGuideIds(nextActiveGuideIds);
    }
  };

  const handleGuidePositionChange = (id: string, position: number) => {
    setGuides(
      guides.map((guide) =>
        guide.id === id
          ? {
            ...guide,
            position,
          }
          : guide,
      ),
    );
  };

  const addGuide = (orientation: Guide["orientation"], position: number) => {
    const id = `guide-${orientation}-${crypto.randomUUID()}`;
    setGuides([...guides, { id, orientation, position }]);
    setSelectedGuideId(id);
  };

  const removeGuide = (id: string) => {
    setGuides(guides.filter((guide) => guide.id !== id));
    setActiveGuideIds((prev) => prev.filter((activeId) => activeId !== id));
    setSelectedGuideId((prev) => (prev === id ? null : prev));
  };

  const handleGuideDragEnd = (id: string, position: number, shouldRemove: boolean) => {
    draggingGuideRef.current = null;
    if (shouldRemove) {
      removeGuide(id);
      return;
    }
    handleGuidePositionChange(id, position);
  };

  useEffect(() => {
    const forceFinishGuideDrag = () => {
      const draggingGuide = draggingGuideRef.current;
      if (!draggingGuide) return;

      const stage = stageRef.current;
      if (!stage) return;

      const node = stage.findOne(`#${draggingGuide.id}`) as Konva.Node | null;
      if (!node) {
        draggingGuideRef.current = null;
        return;
      }

      if (node.isDragging()) {
        node.stopDrag();
      }

      const nextPosition =
        draggingGuide.orientation === "vertical"
          ? node.x()
          : node.y();
      const shouldRemove =
        draggingGuide.orientation === "vertical"
          ? nextPosition < 0 || nextPosition > stageSize.width
          : nextPosition < 0 || nextPosition > stageSize.height;

      handleGuideDragEnd(draggingGuide.id, nextPosition, shouldRemove);
    };

    window.addEventListener("pointerup", forceFinishGuideDrag);
    window.addEventListener("mouseup", forceFinishGuideDrag);
    window.addEventListener("touchend", forceFinishGuideDrag);
    window.addEventListener("pointercancel", forceFinishGuideDrag);
    window.addEventListener("blur", forceFinishGuideDrag);

    return () => {
      window.removeEventListener("pointerup", forceFinishGuideDrag);
      window.removeEventListener("mouseup", forceFinishGuideDrag);
      window.removeEventListener("touchend", forceFinishGuideDrag);
      window.removeEventListener("pointercancel", forceFinishGuideDrag);
      window.removeEventListener("blur", forceFinishGuideDrag);
    };
  }, [stageSize.height, stageSize.width]);

  useEffect(() => {
    const handleDeleteGuide = (e: KeyboardEvent) => {
      if (!selectedGuideId) return;
      const target = e.target as HTMLElement;
      if (
        target?.tagName === "INPUT" ||
        target?.tagName === "TEXTAREA" ||
        target?.isContentEditable
      ) {
        return;
      }

      if (e.key === "Delete" || e.key === "Backspace") {
        e.preventDefault();
        removeGuide(selectedGuideId);
      }
    };

    window.addEventListener("keydown", handleDeleteGuide);
    return () => {
      window.removeEventListener("keydown", handleDeleteGuide);
    };
  }, [selectedGuideId]);

  useEffect(() => {
    return () => {
      cleanupRulerDragRef.current?.();
      cleanupRulerDragRef.current = null;
    };
  }, []);

  const startRulerGuideDrag = (
    orientation: Guide["orientation"],
    pointerId: number | null,
  ) => {
    cleanupRulerDragRef.current?.();

    const updateFromPointer = (event: PointerEvent) => {
      if (pointerId !== null && event.pointerId !== pointerId) return null;
      const rect = stageWrapRef.current?.getBoundingClientRect();
      if (!rect) return null;
      const rawPos =
        orientation === "vertical"
          ? (event.clientX - rect.left) / finalScale
          : (event.clientY - rect.top) / finalScale;
      return rawPos;
    };

    const updateDraft = (event: PointerEvent) => {
      const pointerPos = updateFromPointer(event);
      if (pointerPos === null) return;
      const boundedPos =
        orientation === "vertical"
          ? Math.max(0, Math.min(stageSize.width, pointerPos))
          : Math.max(0, Math.min(stageSize.height, pointerPos));
      setDraftGuide({ orientation, position: boundedPos });
    };

    const handlePointerMove = (event: PointerEvent) => {
      updateDraft(event);
    };

    const cleanup = () => {
      window.removeEventListener("pointermove", handlePointerMove);
      window.removeEventListener("pointerup", handlePointerUp);
      window.removeEventListener("pointercancel", handlePointerCancel);
      window.removeEventListener("blur", handleBlur);
      cleanupRulerDragRef.current = null;
    };

    const handlePointerUp = (event: PointerEvent) => {
      if (pointerId !== null && event.pointerId !== pointerId) return;
      const rect = stageWrapRef.current?.getBoundingClientRect();
      if (!rect) {
        setDraftGuide(null);
        cleanup();
        return;
      }

      const isInside =
        event.clientX >= rect.left &&
        event.clientX <= rect.right &&
        event.clientY >= rect.top &&
        event.clientY <= rect.bottom;

      if (isInside) {
        const pointerPos = updateFromPointer(event);
        if (pointerPos !== null) {
          const boundedPos = orientation === "vertical"
            ? Math.max(0, Math.min(stageSize.width, pointerPos))
            : Math.max(0, Math.min(stageSize.height, pointerPos));
          addGuide(orientation, boundedPos);
        }
      }

      setDraftGuide(null);
      cleanup();
    };

    const handlePointerCancel = (event: PointerEvent) => {
      if (pointerId !== null && event.pointerId !== pointerId) return;
      setDraftGuide(null);
      cleanup();
    };

    const handleBlur = () => {
      setDraftGuide(null);
      cleanup();
    };

    window.addEventListener("pointermove", handlePointerMove);
    window.addEventListener("pointerup", handlePointerUp);
    window.addEventListener("pointercancel", handlePointerCancel);
    window.addEventListener("blur", handleBlur);

    cleanupRulerDragRef.current = cleanup;
  };

  const handleDragMove = (e: Konva.KonvaEventObject<DragEvent>) => {
    const node = e.target;
    if (!node || node.name() === "guide-line") return;

    const snapped = getSnappedPosition(node, guides);

    if (node.x() !== snapped.position.x || node.y() !== snapped.position.y) {
      node.position(snapped.position);
      node.getLayer()?.batchDraw();
    }

    updateActiveGuides(snapped.activeGuideIds);

    // گروه فقط باکس انتخاب را درگ می‌کند؛ اعضا با مختصات مطلق جدا رسم می‌شوند — در حین drag باید استور هم‌گام شود
    if (node.name() === "group-container") {
      handleObjectUpdate(node.id(), {
        x: snapped.position.x,
        y: snapped.position.y,
      });
    }
  };

  const handleDragEnd = () => {
    updateActiveGuides([]);
  };

  const handleStageMouseDownWithGuideReset = (e: Konva.KonvaEventObject<MouseEvent>) => {
    if (e.target?.name() !== "guide-line") {
      setSelectedGuideId(null);
    }
    handleStageMouseDown(e);
  };

  return (
    <div className="flex-1 flex items-center justify-center pb-8 overflow-auto relative xl:pl-6">
      <div className="mt-8">
        <div
          className="relative select-none"
          style={{
            width: stageSize.width * finalScale,
            height: stageSize.height * finalScale,
          }}
        >
          <div
            className="absolute -top-6 left-0 h-6 bg-slate-100 border-b border-slate-300 cursor-col-resize"
            style={{ width: stageSize.width * finalScale }}
            onPointerDown={(e) => {
              e.preventDefault();
              startRulerGuideDrag("vertical", e.pointerId);
            }}
            title="Drag to create vertical guide"
          />
          <div
            className="absolute top-0 -left-6 w-6 bg-slate-100 border-r border-slate-300 cursor-row-resize"
            style={{ height: stageSize.height * finalScale }}
            onPointerDown={(e) => {
              e.preventDefault();
              startRulerGuideDrag("horizontal", e.pointerId);
            }}
            title="Drag to create horizontal guide"
          />
          <div className="absolute -top-6 -left-6 w-6 h-6 bg-slate-200 border border-slate-300" />
          <div ref={stageWrapRef} className="shadow-lg">
            <Stage
              ref={stageRef}
              width={stageSize.width * finalScale}
              height={stageSize.height * finalScale}
              scaleX={finalScale}
              scaleY={finalScale}
              onMouseDown={handleStageMouseDownWithGuideReset}
              onMouseMove={handleStageMouseMove}
              onMouseUp={handleStageMouseUp}
              onDragMove={handleDragMove}
              onDragEnd={handleDragEnd}
            >
              <Layer listening={false}>
                <Rect
                  x={0}
                  y={0}
                  width={stageSize.width}
                  height={stageSize.height}
                  fill="#ffffff"
                  listening={false}
                />
              </Layer>

              <ObjectsLayer
                layerRef={layerRef}
                objects={objects}
                selectedObjectIds={selectedObjectIds}
                selectedCellId={selectedCellId}
                tool={tool}
                transformerRef={transformerRef}
                selectedObject={selectedObject}
                isGroupedWithMask={isGroupedWithMask}
                maskObject={maskObject}
                canGroup={canGroup}
                canUngroup={canUngroup}
                onSelect={handleSelect}
                onUpdate={handleObjectUpdate}
                onCellClick={handleCellClick}
                onCellDblClick={handleCellDblClick}
                onGroup={handleGroupWithMask}
                onUngroup={handleUngroupFromMask}
              />
              <GuidesLayer
                guides={guides}
                stageWidth={stageSize.width}
                stageHeight={stageSize.height}
                activeGuideIds={activeGuideIds}
                selectedGuideId={selectedGuideId}
                draftGuide={draftGuide}
                onGuideSelect={setSelectedGuideId}
                onGuideDragStart={(id, orientation) => {
                  draggingGuideRef.current = { id, orientation };
                  setSelectedGuideId(id);
                }}
                onGuideDragEnd={handleGuideDragEnd}
              />
            </Stage>
          </div>
        </div>
      </div>
      {editingCell && (
        <CellEditor
          x={editingCell.x}
          y={editingCell.y}
          width={editingCell.width}
          height={editingCell.height}
          value={editingCell.value}
          onSave={handleCellEditorSave}
          onCancel={handleCellEditorCancel}
        />
      )}
      <ZoomControls />
    </div>
  );
};

export default EditorCanvas;
