import { forwardRef, useCallback, useEffect, useImperativeHandle, useRef } from 'react';
import type { PointerEvent as ReactPointerEvent } from 'react';

import { cls } from '../../utils/cls';

export interface SignaturePadHandle {
    clear: () => void;
    toDataURL: (type?: string, quality?: number) => string | undefined;
}

interface SignaturePadProps {
    className?: string;
    lineWidth?: number;
    strokeStyle?: string;
    onDrawEnd?: (dataUrl: string) => void;
}

export const SignaturePad = forwardRef<SignaturePadHandle, SignaturePadProps>(
    ({ className = '', lineWidth = 5, strokeStyle = '#111827', onDrawEnd }, ref) => {
        const canvasRef = useRef<HTMLCanvasElement | null>(null);
        const isDrawingRef = useRef(false);
        const lastPointRef = useRef<{ x: number; y: number } | null>(null);
        const scaleRef = useRef({ scaleX: 1, scaleY: 1 });

        const resizeCanvas = useCallback(() => {
            const canvas = canvasRef.current;
            if (!canvas) return;
            const rect = canvas.getBoundingClientRect();
            const dpr = window.devicePixelRatio || 1;
            canvas.width = rect.width * dpr;
            canvas.height = rect.height * dpr;
            const ctx = canvas.getContext('2d');
            if (!ctx) return;
            ctx.scale(dpr, dpr);
            ctx.lineWidth = lineWidth;
            ctx.lineCap = 'round';
            ctx.strokeStyle = strokeStyle;
            scaleRef.current = { scaleX: dpr, scaleY: dpr };
        }, [lineWidth, strokeStyle]);

        useEffect(() => {
            resizeCanvas();
            const handleResize = () => resizeCanvas();
            window.addEventListener('resize', handleResize);
            return () => window.removeEventListener('resize', handleResize);
        }, [resizeCanvas]);

        const getPoint = useCallback((e: ReactPointerEvent<HTMLCanvasElement>) => {
            const canvas = canvasRef.current;
            if (!canvas) return { x: 0, y: 0 };
            const rect = canvas.getBoundingClientRect();
            return {
                // 使用 CSS 像素坐标，避免与 ctx.scale 的 DPR 缩放重复，防止漂移
                x: e.clientX - rect.left,
                y: e.clientY - rect.top,
            };
        }, []);

        const startDrawing = useCallback(
            (e: ReactPointerEvent<HTMLCanvasElement>) => {
                const canvas = canvasRef.current;
                if (!canvas) return;
                isDrawingRef.current = true;
                lastPointRef.current = getPoint(e);
                canvas.setPointerCapture(e.pointerId);
            },
            [getPoint],
        );

        const draw = useCallback(
            (e: ReactPointerEvent<HTMLCanvasElement>) => {
                if (!isDrawingRef.current) return;
                const canvas = canvasRef.current;
                if (!canvas) return;
                const ctx = canvas.getContext('2d');
                if (!ctx) return;
                const current = getPoint(e);
                const last = lastPointRef.current || current;
                ctx.beginPath();
                ctx.moveTo(last.x, last.y);
                ctx.lineTo(current.x, current.y);
                ctx.stroke();
                lastPointRef.current = current;
            },
            [getPoint],
        );

        const stopDrawing = useCallback(
            (e?: ReactPointerEvent<HTMLCanvasElement>) => {
                if (!isDrawingRef.current) return;
                isDrawingRef.current = false;
                lastPointRef.current = null;
                if (e?.pointerId && canvasRef.current) {
                    canvasRef.current.releasePointerCapture(e.pointerId);
                }
                const dataUrl = canvasRef.current?.toDataURL();
                if (dataUrl && onDrawEnd) {
                    onDrawEnd(dataUrl);
                }
            },
            [onDrawEnd],
        );

        const clear = useCallback(() => {
            const canvas = canvasRef.current;
            const ctx = canvas?.getContext('2d');
            if (canvas && ctx) {
                ctx.clearRect(0, 0, canvas.width, canvas.height);
            }
        }, []);

        useImperativeHandle(
            ref,
            () => ({
                clear,
                toDataURL: (type?: string, quality?: number) => canvasRef.current?.toDataURL(type, quality),
            }),
            [clear],
        );

        return (
            <canvas
                ref={canvasRef}
                className={cls('w-full h-72 bg-white touch-none', className)}
                onPointerDown={startDrawing}
                onPointerMove={draw}
                onPointerUp={stopDrawing}
                onPointerLeave={stopDrawing}
                onPointerCancel={stopDrawing}
            />
        );
    },
);

SignaturePad.displayName = 'SignaturePad';

