import {
    forwardRef,
    useImperativeHandle,
    useRef,
    useState,
    type PointerEvent as ReactPointerEvent,
} from 'react';
import { Button } from '@/components/ui/button';

export type SignaturePadHandle = {
    toFile: () => Promise<File | null>;
    toDataURL: () => string | null;
    clear: () => void;
};

const WIDTH = 500;
const HEIGHT = 200;

function getContext(canvas: HTMLCanvasElement): CanvasRenderingContext2D {
    const ctx = canvas.getContext('2d');
    if (!ctx) {
        throw new Error('Canvas 2D context unavailable');
    }
    return ctx;
}

function fillWhite(ctx: CanvasRenderingContext2D) {
    ctx.fillStyle = '#fff';
    ctx.fillRect(0, 0, WIDTH, HEIGHT);
}

export const SignaturePad = forwardRef<SignaturePadHandle>(
    function SignaturePad(_props, ref) {
        const canvasRef = useRef<HTMLCanvasElement>(null);
        const drawingRef = useRef(false);
        const lastPointRef = useRef<{ x: number; y: number } | null>(null);
        const [hasDrawn, setHasDrawn] = useState(false);

        useImperativeHandle(ref, () => ({
            clear() {
                const canvas = canvasRef.current;
                if (!canvas) {
                    return;
                }
                fillWhite(getContext(canvas));
                setHasDrawn(false);
            },
            toDataURL() {
                const canvas = canvasRef.current;
                if (!canvas || !hasDrawn) {
                    return null;
                }
                return canvas.toDataURL('image/png');
            },
            toFile() {
                return new Promise((resolve) => {
                    const canvas = canvasRef.current;
                    if (!canvas || !hasDrawn) {
                        resolve(null);
                        return;
                    }

                    canvas.toBlob((blob) => {
                        if (!blob) {
                            resolve(null);
                            return;
                        }
                        resolve(
                            new File([blob], 'signature.png', {
                                type: 'image/png',
                            }),
                        );
                    }, 'image/png');
                });
            },
        }));

        function pointFromEvent(event: ReactPointerEvent<HTMLCanvasElement>) {
            const rect = event.currentTarget.getBoundingClientRect();
            return {
                x: ((event.clientX - rect.left) / rect.width) * WIDTH,
                y: ((event.clientY - rect.top) / rect.height) * HEIGHT,
            };
        }

        function handlePointerDown(event: ReactPointerEvent<HTMLCanvasElement>) {
            event.currentTarget.setPointerCapture(event.pointerId);
            drawingRef.current = true;
            lastPointRef.current = pointFromEvent(event);
        }

        function handlePointerMove(event: ReactPointerEvent<HTMLCanvasElement>) {
            if (!drawingRef.current || !canvasRef.current) {
                return;
            }

            const ctx = getContext(canvasRef.current);
            const point = pointFromEvent(event);
            const last = lastPointRef.current ?? point;

            ctx.strokeStyle = '#111';
            ctx.lineWidth = 2.5;
            ctx.lineCap = 'round';
            ctx.lineJoin = 'round';

            ctx.beginPath();
            ctx.moveTo(last.x, last.y);
            ctx.lineTo(point.x, point.y);
            ctx.stroke();

            lastPointRef.current = point;
            setHasDrawn(true);
        }

        function stopDrawing() {
            drawingRef.current = false;
            lastPointRef.current = null;
        }

        return (
            <div className="flex flex-col gap-2">
                <canvas
                    width={WIDTH}
                    height={HEIGHT}
                    className="touch-none rounded-md border bg-white"
                    onPointerDown={handlePointerDown}
                    onPointerMove={handlePointerMove}
                    onPointerUp={stopDrawing}
                    onPointerLeave={stopDrawing}
                    ref={(node) => {
                        canvasRef.current = node;
                        if (node) {
                            fillWhite(getContext(node));
                        }
                    }}
                />
                <div>
                    <Button
                        type="button"
                        variant="outline"
                        size="sm"
                        onClick={() => {
                            const canvas = canvasRef.current;
                            if (canvas) {
                                fillWhite(getContext(canvas));
                                setHasDrawn(false);
                            }
                        }}
                    >
                        Effacer
                    </Button>
                </div>
            </div>
        );
    },
);
