import { useEffect, useRef } from "react";
import { Group, Rect, Circle, RegularPolygon, Star } from "react-konva";
import Konva from "konva";
import type { EditorObject } from "../../store/editorStore";
import {
    RectangleShape,
    CircleShape,
    TriangleShape,
    AbstractShape,
    TextShape,
    LineShape,
    ArrowShape,
    ImageObject,
    LinkShape,
    VideoShape,
} from "../tools";
import Table from "@/components/Table";

type ObjectRendererProps = {
    obj: EditorObject;
    isSelected: boolean;
    transformerRef: React.RefObject<Konva.Transformer | null>;
    layerRef: React.RefObject<Konva.Layer | null>;
    onSelect: (id: string, node: Konva.Node) => void;
    onUpdate: (id: string, updates: Partial<EditorObject>) => void;
    draggable: boolean;
    onCellClick?: (tableId: string, cellId: string) => void;
    onCellDblClick?: (tableId: string, cellId: string, x: number, y: number, width: number, height: number, text: string) => void;
    selectedCellId?: string | null;
    allObjects?: EditorObject[];
};

const ObjectRenderer = ({
    obj,
    isSelected,
    transformerRef,
    layerRef,
    onSelect,
    onUpdate,
    draggable,
    onCellClick,
    onCellDblClick,
    selectedCellId,
    allObjects = [],
}: ObjectRendererProps) => {
    const groupRef = useRef<Konva.Group>(null);

    // Find mask for this object
    const maskShape = obj.maskId ? allObjects.find((m) => m.id === obj.maskId) : null;
    const shouldApplyMask = maskShape && !obj.isMask;

    // Apply caching for masked objects - REQUIRED for destination-in to work
    useEffect(() => {
        if (!groupRef.current || !shouldApplyMask) return;

        // Use requestAnimationFrame to ensure shapes are rendered before caching
        const rafId = requestAnimationFrame(() => {
            if (!groupRef.current) return;
            
            // Clear cache first
            groupRef.current.clearCache();
            
            // Apply cache - Konva will automatically calculate bounds
            groupRef.current.cache();
            groupRef.current.getLayer()?.batchDraw();
        });

        return () => {
            cancelAnimationFrame(rafId);
            groupRef.current?.clearCache();
        };
    }, [shouldApplyMask, maskShape?.x, maskShape?.y, maskShape?.width, maskShape?.height, maskShape?.rotation, obj.x, obj.y, obj.width, obj.height, obj.rotation]);

    if (obj.visible === false) {
        return null;
    }

    // برای objectهای با mask، یک obj جدید با موقعیت (0, 0) ایجاد کن
    const shapeObj = shouldApplyMask 
        ? { ...obj, x: 0, y: 0 }
        : obj;

    const commonProps = {
        obj: shapeObj,
        isSelected,
        transformerRef,
        onSelect,
        onUpdate,
        // اگر mask اعمال شده، shape را غیر draggable کن (Group drag می‌شود)
        draggable: shouldApplyMask ? false : draggable,
    };

    const renderMaskShape = (mask: EditorObject) => {
        const maskProps = {
            listening: false,
            perfectDrawEnabled: false,
            fill: "black",
        };

        // موقعیت mask نسبت به object - برای overlap صحیح
        // mask در موقعیت مطلق است، پس باید آن را relative به object کنیم
        // چون shape در موقعیت (0, 0) است، باید موقعیت mask را نسبت به shape محاسبه کنم
        const relativeX = (mask.x || 0) - (obj.x || 0);
        const relativeY = (mask.y || 0) - (obj.y || 0);

        if (mask.type === "rectangle" && mask.shapeType === "circle") {
            return (
                <Circle
                    {...maskProps}
                    x={relativeX}
                    y={relativeY}
                    radius={(mask.width || 50) / 2}
                    rotation={mask.rotation || 0}
                />
            );
        }

        if (mask.type === "rectangle" && mask.shapeType === "triangle") {
            const radius = Math.min((mask.width || 100) / 2, (mask.height || 100) / 2);
            return (
                <RegularPolygon
                    {...maskProps}
                    x={relativeX + (mask.width || 100) / 2}
                    y={relativeY + (mask.height || 100) / 2}
                    sides={3}
                    radius={radius}
                    rotation={mask.rotation || 0}
                />
            );
        }

        if (mask.type === "rectangle" && mask.shapeType === "abstract") {
            const baseRadius = Math.min((mask.width || 100) / 2, (mask.height || 100) / 2);
            const abstractScale = 0.85;
            const radius = baseRadius * abstractScale;
            const innerRadius = radius * 0.5;

            return (
                <Star
                    {...maskProps}
                    x={relativeX + (mask.width || 100) / 2}
                    y={relativeY + (mask.height || 100) / 2}
                    numPoints={5}
                    innerRadius={innerRadius}
                    outerRadius={radius}
                    rotation={mask.rotation || 0}
                />
            );
        }

        return (
            <Rect
                {...maskProps}
                x={relativeX}
                y={relativeY}
                width={mask.width || 100}
                height={mask.height || 100}
                rotation={mask.rotation || 0}
            />
        );
    };

    let shapeElement: React.ReactNode = null;

    switch (obj.type) {
        case "rectangle":
            if (obj.shapeType === "circle") {
                shapeElement = <CircleShape key={obj.id} {...commonProps} />;
            } else if (obj.shapeType === "triangle") {
                shapeElement = <TriangleShape key={obj.id} {...commonProps} />;
            } else if (obj.shapeType === "abstract") {
                shapeElement = <AbstractShape key={obj.id} {...commonProps} />;
            } else {
                shapeElement = <RectangleShape key={obj.id} {...commonProps} />;
            }
            break;
        case "text":
            shapeElement = <TextShape key={obj.id} {...commonProps} layerRef={layerRef} />;
            break;
        case "line":
            shapeElement = <LineShape key={obj.id} {...commonProps} />;
            break;
        case "arrow":
            shapeElement = <ArrowShape key={obj.id} {...commonProps} />;
            break;
        case "image":
            shapeElement = <ImageObject key={obj.id} {...commonProps} />;
            break;
        case "link":
            shapeElement = <LinkShape key={obj.id} {...commonProps} />;
            break;
        case "video":
            shapeElement = <VideoShape key={obj.id} {...commonProps} />;
            break;
        case "document":
            shapeElement = <ImageObject key={obj.id} {...commonProps} />;
            break;
        case "grid":
            shapeElement = (
                <Table
                    key={obj.id}
                    obj={obj}
                    selectedCellId={selectedCellId || null}
                    onCellClick={(tableId, cellId, node) => {
                        onSelect(tableId, node);
                        onCellClick?.(tableId, cellId);
                    }}
                    onCellDblClick={(tableId, cellId, x, y, width, height, text) => {
                        onCellDblClick?.(tableId, cellId, x, y, width, height, text);
                    }}
                    onDragEnd={(tableId, x, y) => {
                        onUpdate(tableId, { x, y });
                    }}
                    draggable={draggable}
                />
            );
            break;
        default:
            return null;
    }

    // If no mask, render normally
    if (!shouldApplyMask || !maskShape) {
        return shapeElement;
    }

    // Apply masking with Group + destination-in or destination-out
    // IMPORTANT: The shape inside keeps its normal event handlers
    // The mask layer has listening={false} to not interfere with selection  
    // Default: destination-out (overlap مخفی می‌شود)
    const compositeOp = obj.maskInvert === false ? "destination-in" : "destination-out";
    
    const handleGroupDragEnd = (e: Konva.KonvaEventObject<DragEvent>) => {
        const node = e.target;
        onUpdate(obj.id, {
            x: node.x(),
            y: node.y(),
        });
    };

    const handleGroupDragMove = (e: Konva.KonvaEventObject<DragEvent>) => {
        const node = e.target;
        onUpdate(obj.id, {
            x: node.x(),
            y: node.y(),
        });
        // به‌روزرسانی transformer در حین drag
        if (transformerRef.current && isSelected) {
            transformerRef.current.nodes([node]);
            transformerRef.current.getLayer()?.batchDraw();
        }
    };

    const handleGroupTransformEnd = () => {
        if (!groupRef.current) return;
        const node = groupRef.current;
        const scaleX = node.scaleX();
        const scaleY = node.scaleY();

        node.scaleX(1);
        node.scaleY(1);

        onUpdate(obj.id, {
            x: node.x(),
            y: node.y(),
            rotation: node.rotation(),
            width: Math.max(5, (obj.width || 100) * scaleX),
            height: Math.max(5, (obj.height || 100) * scaleY),
        });
    };
    
    return (
        <Group 
            ref={groupRef}
            id={`masked-${obj.id}`}
            name="masked-group"
            x={obj.x}
            y={obj.y}
            draggable={draggable}
            onClick={() => {
                if (groupRef.current) {
                    onSelect(obj.id, groupRef.current);
                }
            }}
            onDragEnd={handleGroupDragEnd}
            onDragMove={handleGroupDragMove}
            onTransformEnd={handleGroupTransformEnd}
        >
            {shapeElement}
            <Group globalCompositeOperation={compositeOp} listening={false}>
                {renderMaskShape(maskShape)}
            </Group>
        </Group>
    );
};

export default ObjectRenderer;

