import { useEffect, useRef, useState } from "react";
import { Stage, Layer, Rect, Image as KonvaImage } from "react-konva";
import useImage from "use-image";
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 { useCustomShapeDrawing } from "./canvas/useCustomShapeDrawing";
import { usePencilDrawing } from "./canvas/usePencilDrawing";
import ObjectsLayer from "./canvas/ObjectsLayer";
import GuidesLayer from "./canvas/GuidesLayer";
import CustomShapeDrawingLayer from "./canvas/CustomShapeDrawingLayer";
import PencilDrawingLayer from "./canvas/PencilDrawingLayer";
import {
  clearSmartGuides,
  drawSmartGuides,
  getSnappedPosition,
  type Guide,
} from "./canvas/useSnap";
import CellEditor from "@/components/CellEditor";
import ZoomControls from "./ZoomControls";
import EditorCanvasToolbar from "./EditorCanvasToolbar";
import { createScopedId } from "../store/editorStore.helpers";
import { getKonvaGradientProps } from "../utils/gradient";
import { STICKER_DRAG_MIME } from "../types/IconTypes";
import { GALLERY_DRAG_MIME } from "../types/GalleryTypes";
import {
  createStickerObject,
  scaleStickerDimensions,
} from "../utils/stickerUtils";
import {
  createImageObject,
  scaleImageDimensions,
} from "../utils/imageUtils";

type EditorCanvasProps = {
  catalogSize?: string;
};

const EditorCanvas = ({ catalogSize }: EditorCanvasProps) => {
  const stageRef = useRef<Konva.Stage>(null);
  const layerRef = useRef<Konva.Layer>(null);
  const smartGuidesLayerRef = useRef<Konva.Layer>(null);
  const activeGuideIdsRef = useRef<string[]>([]);
  const stageWrapRef = useRef<HTMLDivElement>(null);

  const {
    tool,
    objects,
    guides,
    documentSettings,
    pages,
    currentPageId,
    selectedObjectId,
    selectedObjectIds,
    selectedCellId,
    editingCell,
    setStageRef,
    setLayerRef,
    setGuides,
    zoom,
    addObject,
    setSelectedObjectId,
    setTool,
  } = useEditorStore();
  const isSmartGuideEnabled = documentSettings.smartGuide;
  const currentPage = pages.find((page) => page.id === currentPageId);

  const bgColor =
    currentPage?.backgroundType === 'color' || currentPage?.backgroundType === 'gradient'
      ? currentPage.backgroundColor
      : '#ffffff';
  const bgGradient = currentPage?.backgroundGradient;
  const hasVideoBackground =
    currentPage?.backgroundType === "video" &&
    Boolean(currentPage.backgroundVideoUrl);

  const [bgImage] = useImage(
    currentPage?.backgroundType === 'image'
      ? (currentPage.backgroundImageUrl || '')
      : '',
  );
  const stageSize = useStageSize(catalogSize);
  const bgImageCrop = (() => {
    if (!bgImage) return null;

    const containerRatio = stageSize.width / stageSize.height;
    const imageRatio = bgImage.width / bgImage.height;

    if (imageRatio > containerRatio) {
      const cropWidth = bgImage.height * containerRatio;
      const cropX = (bgImage.width - cropWidth) / 2;
      return { x: cropX, y: 0, width: cropWidth, height: bgImage.height };
    }

    const cropHeight = bgImage.width / containerRatio;
    const cropY = (bgImage.height - cropHeight) / 2;
    return { x: 0, y: cropY, width: bgImage.width, height: cropHeight };
  })();

  const { transformerRef, handleStageMouseDown, handleStageMouseMove, handleStageMouseUp } = useDrawingHandlers();
  const { drawingState, handleStageClick: handleCustomShapeClick, handleStageMouseMove: handleCustomShapeMouseMove } =
    useCustomShapeDrawing();
  const {
    drawingState: pencilDrawingState,
    handleStagePointerDown: handlePencilPointerDown,
    handleStagePointerMove: handlePencilPointerMove,
    handleStagePointerUp: handlePencilPointerUp,
  } = usePencilDrawing(stageRef);
  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;

  useEffect(() => {
    let cleanup: (() => void) | undefined;
    let raf = 0;

    const begin = () => useEditorStore.getState().beginObjectGesture();
    const end = () => useEditorStore.getState().endObjectGesture();

    const attach = () => {
      const stage = stageRef.current;
      if (!stage || cleanup) return;
      stage.on("dragstart", begin);
      stage.on("dragend", end);
      cleanup = () => {
        stage.off("dragstart", begin);
        stage.off("dragend", end);
      };
    };

    attach();
    if (!cleanup) {
      raf = window.requestAnimationFrame(attach);
    }

    return () => {
      if (raf) window.cancelAnimationFrame(raf);
      cleanup?.();
    };
  }, [finalScale, stageSize.width, stageSize.height]);

  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 = createScopedId(`guide-${orientation}`);
    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) => {
    if (shouldRemove) {
      removeGuide(id);
      return;
    }
    handleGuidePositionChange(id, position);
  };

  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);
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [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;
    if (!isSmartGuideEnabled) {
      updateActiveGuides([]);
      clearSmartGuides(smartGuidesLayerRef.current);
      return;
    }

    const snapped = getSnappedPosition(node, guides, finalScale);

    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,
      });
    }

    drawSmartGuides(
      smartGuidesLayerRef.current,
      snapped.smartGuides,
      stageSize.width,
      stageSize.height,
    );
  };

  const handleDragEnd = () => {
    updateActiveGuides([]);
    clearSmartGuides(smartGuidesLayerRef.current);
  };

  useEffect(() => {
    if (isSmartGuideEnabled) return;
    updateActiveGuides([]);
    clearSmartGuides(smartGuidesLayerRef.current);
  }, [isSmartGuideEnabled]);

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

  const handleStagePointerDownWithGuideReset = (e: Konva.KonvaEventObject<PointerEvent>) => {
    if (e.target?.name() !== "guide-line") {
      setSelectedGuideId(null);
    }
    if (tool === "pencil") {
      handlePencilPointerDown(e);
    }
  };

  const handleCombinedMouseMove = (e: Konva.KonvaEventObject<MouseEvent>) => {
    handleStageMouseMove(e);
    handleCustomShapeMouseMove(e);
  };

  const handleCombinedPointerMove = (e: Konva.KonvaEventObject<PointerEvent>) => {
    handlePencilPointerMove(e);
  };

  const handleCombinedPointerUp = (e: Konva.KonvaEventObject<PointerEvent>) => {
    handlePencilPointerUp(e);
  };

  const scaledW = stageSize.width * finalScale;
  const scaledH = stageSize.height * finalScale;

  const SIDEBAR_DRAG_MIMES = [STICKER_DRAG_MIME, GALLERY_DRAG_MIME];

  const handleSidebarDragOver = (e: React.DragEvent) => {
    if (!SIDEBAR_DRAG_MIMES.some((mime) => e.dataTransfer.types.includes(mime))) return;
    e.preventDefault();
    e.dataTransfer.dropEffect = "copy";
  };

  const placeObjectAt = (
    imageUrl: string,
    stageX: number,
    stageY: number,
    width: number,
    height: number,
    type: "sticker" | "image",
  ) => {
    const object =
      type === "sticker"
        ? createStickerObject(
            imageUrl,
            stageX,
            stageY,
            width,
            height,
            stageSize.width,
            stageSize.height,
          )
        : createImageObject(
            imageUrl,
            stageX,
            stageY,
            width,
            height,
            stageSize.width,
            stageSize.height,
          );
    addObject(object);
    setSelectedObjectId(object.id);
    setTool("select");
  };

  const handleSidebarDrop = (e: React.DragEvent) => {
    const stickerUrl = e.dataTransfer.getData(STICKER_DRAG_MIME);
    const galleryUrl = e.dataTransfer.getData(GALLERY_DRAG_MIME);
    const imageUrl = stickerUrl || galleryUrl;
    const dropType = stickerUrl ? "sticker" : galleryUrl ? "image" : null;
    if (!imageUrl || !dropType || !stageWrapRef.current) return;
    e.preventDefault();

    const rect = stageWrapRef.current.getBoundingClientRect();
    const stageX = (e.clientX - rect.left) / finalScale;
    const stageY = (e.clientY - rect.top) / finalScale;

    const scaleFn =
      dropType === "sticker" ? scaleStickerDimensions : scaleImageDimensions;

    const img = new Image();
    img.onload = () => {
      const { width, height } = scaleFn(img.naturalWidth, img.naturalHeight);
      placeObjectAt(imageUrl, stageX, stageY, width, height, dropType);
    };
    img.onerror = () => {
      placeObjectAt(imageUrl, stageX, stageY, 100, 100, dropType);
    };
    img.src = imageUrl;
  };
  const backgroundGradientProps = getKonvaGradientProps(
    currentPage?.backgroundType === "gradient" ? "gradient" : "solid",
    bgGradient,
    stageSize.width,
    stageSize.height,
    "rect",
  );

  return (
    <div className="relative z-10 flex min-h-0 flex-1 flex-col overflow-auto pb-8 xl:pl-6">
      <div
        className="flex w-full justify-center mx-auto"
        style={{ maxWidth: scaledW }}
      >
        <div
          className="overflow-hidden"
          style={{ width: scaledW }}
        >
          <EditorCanvasToolbar barWidthPx={scaledW} />
        </div>
      </div>
      <div className="mx-auto mt-4 flex w-full min-w-0 flex-col items-center">

        <div
          className="relative z-40 -mt-px select-none rounded-b-[14px] border border-t-0 border-[#d8dde5] bg-white shadow-[0_2px_8px_rgba(15,23,42,0.07)]"
          style={{
            width: scaledW,
            height: scaledH,
          }}
        >

          <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="relative shadow-lg"
            onDragOver={handleSidebarDragOver}
            onDrop={handleSidebarDrop}
          >
            {hasVideoBackground && (
              <video
                src={currentPage?.backgroundVideoUrl}
                autoPlay
                loop
                muted
                playsInline
                className="absolute inset-0 w-full h-full object-cover pointer-events-none"
              />
            )}
            <Stage
              ref={stageRef}
              width={stageSize.width * finalScale}
              height={stageSize.height * finalScale}
              scaleX={finalScale}
              scaleY={finalScale}
              style={{ cursor: tool === "custom-shape" || tool === "pencil" ? "crosshair" : undefined }}
              onMouseDown={handleStageMouseDownWithGuideReset}
              onMouseMove={handleCombinedMouseMove}
              onMouseUp={handleStageMouseUp}
              onPointerDown={handleStagePointerDownWithGuideReset}
              onPointerMove={handleCombinedPointerMove}
              onPointerUp={handleCombinedPointerUp}
              onClick={handleCustomShapeClick}
              onDragMove={handleDragMove}
              onDragEnd={handleDragEnd}
            >
              <Layer listening={false}>
                {!hasVideoBackground && (
                  <Rect
                    x={0}
                    y={0}
                    width={stageSize.width}
                    height={stageSize.height}
                    fill={bgColor}
                    {...backgroundGradientProps}
                    listening={false}
                  />
                )}
                {!hasVideoBackground && currentPage?.backgroundType === 'image' && bgImage && (
                  <KonvaImage
                    image={bgImage}
                    x={0}
                    y={0}
                    width={stageSize.width}
                    height={stageSize.height}
                    crop={bgImageCrop ?? undefined}
                    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}
                stageWidth={stageSize.width}
                stageHeight={stageSize.height}
              />
              <GuidesLayer
                guides={guides}
                stageWidth={stageSize.width}
                stageHeight={stageSize.height}
                activeGuideIds={activeGuideIds}
                selectedGuideId={selectedGuideId}
                draftGuide={draftGuide}
                onGuideSelect={setSelectedGuideId}
                onGuideDragEnd={handleGuideDragEnd}
              />
              <Layer ref={smartGuidesLayerRef} listening={false} visible={false} />
              <CustomShapeDrawingLayer drawingState={drawingState} />
              <PencilDrawingLayer drawingState={pencilDrawingState} />
            </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;
