import React, { useRef, useState } from 'react';

type Point = {
    x: number;
    y: number;
};

type Drawing = {
    path: Path2D;
    offset: Point;
};

const DrawingCanvas: React.FC = () => {
    const canvasRef = useRef<HTMLCanvasElement>(null);
    const [isDrawing, setIsDrawing] = useState(false);
    const [drawings, setDrawings] = useState<Drawing[]>([]);
    const [currentPath, setCurrentPath] = useState<Path2D | null>(null);
    const [drawMode, setDrawMode] = useState(true); // true for drawing, false for selecting
    const [selectMode, setSelectMode] = useState(false);
    const [selectMode2, setSelectMode2] = useState(false);
    const [selectionStart, setSelectionStart] = useState<Point | null>(null);
    const [selectedArea, setSelectedArea] = useState<{ start: Point; end: Point } | null>(null);
    const [selectedDrawings, setSelectedDrawings] = useState<number[]>([]);
    const [mousePos, setMousePos] = useState<Point>();
    const [dragStartPoint, setDragStartPoint] = useState<Point | null>(null);

    const handleTouchStart = (e: React.TouchEvent) => {
        handleMouseDown({
            clientX: e.touches[0].clientX,
            clientY: e.touches[0].clientY,
        } as any);
    };

    const handleTouchMove = (e: React.TouchEvent) => {
        handleMouseMove({
            clientX: e.touches[0].clientX,
            clientY: e.touches[0].clientY,
        } as any);
    };

    const handleTouchEnd = (e: React.TouchEvent) => {
        handleMouseUp({
            clientX: e.changedTouches[0].clientX,
            clientY: e.changedTouches[0].clientY,
        } as any);
    };

    const getCanvasRelativePosition = (e: React.MouseEvent): Point => {
        if (!canvasRef.current) return { x: 0, y: 0 };

        const rect = canvasRef.current.getBoundingClientRect();
        return {
            x: e.clientX - rect.left,
            y: e.clientY - rect.top,
        };
    };

    const redraw = () => {
        if (!canvasRef.current) return;
        const ctx = canvasRef.current.getContext('2d');
        if (ctx) {
            ctx.clearRect(0, 0, canvasRef.current.width, canvasRef.current.height);
            ctx.save();
            drawings.forEach((drawing) => {
                ctx.translate(drawing.offset.x, drawing.offset.y);
                ctx.strokeStyle = 'black';
                ctx.lineWidth = 1;
                ctx.stroke(drawing.path);
                ctx.setTransform(1, 0, 0, 1, 0, 0);
            });
            ctx.restore();

            // 선택 영역이 있으면 항상 그려줍니다.
            if (selectedArea) {
                drawSelectionArea(selectedArea.start, selectedArea.end);
            }
        }
    };

    const handleMouseDown = (e: React.MouseEvent) => {
        const point = getCanvasRelativePosition(e);
        setMousePos(point);

        if (drawMode) {
            setIsDrawing(true);
            const newPath = new Path2D();
            newPath.moveTo(point.x, point.y);
            setCurrentPath(newPath);
            return;
        }

        if (selectMode) {
            if (selectedArea && isPointInsideRect(point, selectedArea)) {
                setDragStartPoint(point);
                return;
            } else {
                setSelectedDrawings([]);
                setDragStartPoint(null);
                setSelectedArea({ start: point, end: point } );
                setSelectionStart(point);
                console.log("out");
                return;
            }
        }
    };

    const handleMouseMove = (e: React.MouseEvent) => {
        const point = getCanvasRelativePosition(e);

        if (drawMode && isDrawing && currentPath) {
            currentPath.lineTo(point.x, point.y);
            const ctx = canvasRef.current?.getContext('2d');
            ctx?.stroke(currentPath);
            return;
        }

        if (selectMode && selectionStart && !dragStartPoint) {
            const normalizedArea = normalizeSelectionArea(selectionStart, point);
            setSelectedArea(normalizedArea);
            redraw();
            drawSelectionArea(normalizedArea.start, normalizedArea.end);
            return;
        }

        if (selectedDrawings.length && mousePos && dragStartPoint) {
            const dx = point.x - mousePos.x;
            const dy = point.y - mousePos.y;
            const newDrawings = [...drawings];

            for (let index of selectedDrawings) {
                newDrawings[index].offset.x += dx;
                newDrawings[index].offset.y += dy;
            }

            if (selectedArea) {
                selectedArea.start.x += dx;
                selectedArea.start.y += dy;
                selectedArea.end.x += dx;
                selectedArea.end.y += dy;
            }

            setDrawings(newDrawings);
            setMousePos(point);
            redraw();
            if (selectedArea) {
                drawSelectionArea(selectedArea.start, selectedArea.end);
            }
            return;
        }
    };

    const isPointInsideRect = (point: Point, rect: { start: Point; end: Point }): boolean => {
        return (
            point.x >= rect.start.x &&
            point.x <= rect.end.x &&
            point.y >= rect.start.y &&
            point.y <= rect.end.y
        );
    };

    const handleMouseUp = (e: React.MouseEvent) => {
        if (drawMode) {
            setIsDrawing(false);
            if (currentPath) {
                setDrawings([...drawings, { path: currentPath, offset: { x: 0, y: 0 } }]);
                setCurrentPath(null);
            }
            return;
        }

        if (selectMode && selectionStart) {
            if (selectedArea) {
                const normalizedArea = selectedArea;
                const selected = drawings.map((drawing, index) =>
                    isDrawingInsideSelection(drawing, normalizedArea) ? index : -1
                ).filter(index => index !== -1);

                setSelectedDrawings(selected);
                redraw();

                if (selected.length === 0) {
                    setSelectedArea(null);
                }
            }
            setSelectionStart(null);
            setDragStartPoint(null);
            return;
        }

        setDragStartPoint(null);
    };

    const isDrawingInsideSelection = (drawing: Drawing, selection: { start: Point; end: Point }): boolean => {
        const ctx = canvasRef.current?.getContext('2d');
        if (!ctx) return false;

        const { start, end } = selection;
        for (let x = start.x; x <= end.x; x += 5) {
            for (let y = start.y; y <= end.y; y += 5) {
                if (ctx.isPointInPath(drawing.path, x - drawing.offset.x, y - drawing.offset.y)) {
                    return true;
                }
            }
        }
        return false;
    };

    const normalizeSelectionArea = (start: Point, end: Point): { start: Point; end: Point } => {
        return {
            start: {
                x: Math.min(start.x, end.x),
                y: Math.min(start.y, end.y)
            },
            end: {
                x: Math.max(start.x, end.x),
                y: Math.max(start.y, end.y)
            }
        };
    };

    const drawSelectionArea = (start: Point, end: Point) => {
        if (!canvasRef.current) return;
        const ctx = canvasRef.current.getContext('2d');
        if (ctx) {
            ctx.setLineDash([5, 5]);
            ctx.strokeStyle = 'blue';
            ctx.strokeRect(start.x, start.y, end.x - start.x, end.y - start.y);
            ctx.setLineDash([]);
        }
    };


    return (
        <div>
            <button onClick={() => {
                setDrawMode(!drawMode);
                setSelectMode(false);
            }}>
                {drawMode ? 'Draw Mode: ON' : 'Draw Mode: OFF'}
            </button>
            <button onClick={() => {
                setSelectMode(!selectMode);
                setDrawMode(false);
            }}>
                {selectMode ? 'Select Mode: ON' : 'Select Mode: OFF'}
            </button>
            <button onClick={() => {
                setSelectMode2(!selectMode2);
                setDrawMode(false);
                setSelectMode(false);
            }}>
                {selectMode2 ? 'Select Mode 2: ON' : 'Select Mode 2: OFF'}
            </button>
            <br/>
            <canvas
                ref={canvasRef}
                width={800}
                height={600}
                style={{ border: '1px solid black' }}
                onMouseDown={handleMouseDown}
                onMouseMove={handleMouseMove}
                onMouseUp={handleMouseUp}
                onTouchStart={handleTouchStart}
                onTouchMove={handleTouchMove}
                onTouchEnd={handleTouchEnd}
            />
        </div>
    );
};

export default DrawingCanvas;
