import Webcam from "react-webcam";
import styles from "./styles.module.scss";
import ModalOverlay, { ModalOverlayType } from "@/components/palmistry/modal-overlay/modal-overlay";
import Modal from "@/components/palmistry/modal/modal";
import { useEffect, useRef, useState } from "react";
import { HandLandmarker, DrawingUtils, HandLandmarkerResult } from "@mediapipe/tasks-vision";
import { handLandmarkerSingleton } from "@/utils/handLandmarkerSingleton";

interface ExtendedMediaTrackCapabilities extends MediaTrackCapabilities {
    torch?: boolean;
}

interface TorchConstraints extends MediaTrackConstraintSet {
    torch?: boolean;
}

interface ExtendedMediaTrackConstraints extends MediaTrackConstraints {
    advanced?: TorchConstraints[];
}

interface CameraModalProps {
    onClose: () => void;
    onTakePhoto: (photo: string) => void;
    onError: (error: string | DOMException) => void;
    onVideoReady?: () => void;
    onMediaPipeInitChange?: (status: boolean) => void;
    onIsFacingCameraChange?: (isFacingCamera: boolean) => void;
    reinitializeKey?: number; // for reinitializing the camera (change the key to reinitialize the camera)
    isCameraVisible?: boolean;
    className?: string;
    isShowHand?: boolean;
    isMediaPipe?: boolean;
    mediaPipeRenderingTemplate?: "v2" | "v3" | "v4";
}

function getIsPalmFacingCamera(landmarks: { x: number, y: number, z: number }[], handedness: "Left" | "Right"): boolean {
    const v1 = {
        x: landmarks[5].x - landmarks[0].x,
        y: landmarks[5].y - landmarks[0].y,
        z: landmarks[5].z - landmarks[0].z,
    };
    const v2 = {
        x: landmarks[17].x - landmarks[0].x,
        y: landmarks[17].y - landmarks[0].y,
        z: landmarks[17].z - landmarks[0].z,
    };
    const normal = {
        x: v1.y * v2.z - v1.z * v2.y,
        y: v1.z * v2.x - v1.x * v2.z,
        z: v1.x * v2.y - v1.y * v2.x,
    };
    if (handedness === "Right") {
        return normal.z < 0;
    } else {
        return normal.z > 0;
    }
}


function CameraModal({
    onClose,
    onTakePhoto,
    onError,
    onVideoReady,
    onMediaPipeInitChange,
    onIsFacingCameraChange,
    reinitializeKey = 0,
    isCameraVisible = true,
    className = "",
    isShowHand = true,
    isMediaPipe = false,
    mediaPipeRenderingTemplate = "v2"
}: CameraModalProps) {
    const [isVideoReady, setIsVideoReady] = useState(false);
    const [isTorchOn, setIsTorchOn] = useState(false);
    const [isTorchAvailable, setIsTorchAvailable] = useState(false);
    const [isHandDetected, setIsHandDetected] = useState(false);
    const [isPalmFacingCamera, setIsPalmFacingCamera] = useState(false);
    const [handName, setHandName] = useState<"Left" | "Right">("Left");
    // const {
    //     width: _width, height: _height,
    //     elementRef: containerRef } = useDynamicSize<HTMLDivElement>({});
    // const [width, height] = [720, 1280];
    // const width = _width * 2;
    // const height = _height * 2;

    // const isLandscape = height <= width;
    // const ratio = isLandscape ? width / height : height / width;
    const cameraRef = useRef<Webcam>(null);
    const canvasRef = useRef<HTMLCanvasElement>(null);
    const containerRef = useRef<HTMLDivElement>(null);
    const handLandmarkerRef = useRef<HandLandmarker | null>(null);
    // useEffect(() => {
    //     setIsVideoReady(false);
    // }, [reinitializeKey]);

    const onClickOverlay = (e: React.MouseEvent) => {
        if (e.target === e.currentTarget) {
            onClose();
        }
    };

    const onClickTakePhoto = () => {
        if (!isVideoReady) return onError("Video is not ready");
        const photo = cameraRef.current?.getScreenshot(
            // {
            //     width,
            //     height,
            // }
        );
        if (photo) {
            onTakePhoto(photo);
        }
    };

    const checkTorchAvailability = async (track: MediaStreamTrack) => {
        try {
            const capabilities = track.getCapabilities() as ExtendedMediaTrackCapabilities;
            setIsTorchAvailable(!!capabilities.torch);
        } catch (error) {
            setIsTorchAvailable(false);
            console.error('Ошибка при проверке поддержки фонарика:', error);
        }
    };

    const onUserMedia = (stream: MediaStream) => {
        setIsVideoReady(true);
        onVideoReady?.();
        const track = stream.getVideoTracks()[0];
        if (track) {
            checkTorchAvailability(track);
        }
    };

    const toggleTorch = async () => {
        try {
            const track = cameraRef.current?.video?.srcObject instanceof MediaStream
                ? (cameraRef.current.video.srcObject as MediaStream).getVideoTracks()[0]
                : null;

            if (track) {
                const capabilities = track.getCapabilities() as ExtendedMediaTrackCapabilities;
                if (capabilities.torch) {
                    const constraints: ExtendedMediaTrackConstraints = {
                        advanced: [{ torch: !isTorchOn }]
                    };
                    await track.applyConstraints(constraints);
                    setIsTorchOn(!isTorchOn);
                } else {
                    onError("Вспышка не поддерживается на этом устройстве");
                }
            }
        } catch (error) {
            onError(error instanceof Error ? error.message : "Ошибка при управлении вспышкой");
        }
    };

    useEffect(() => {
        const unsubscribe = handLandmarkerSingleton.subscribe((isLoaded) => {
            onMediaPipeInitChange?.(isLoaded);
            if (isLoaded) {
                handLandmarkerRef.current = handLandmarkerSingleton.getHandLandmarker();
            }
        });

        handLandmarkerSingleton.preload();

        return () => unsubscribe();
    }, []);

    useEffect(() => {
        if (!isCameraVisible || !isVideoReady || !isMediaPipe || !handLandmarkerRef.current) return;

        let running = true;
        let animationFrameId: number;
        let lastVideoTime = -1;
        let results: HandLandmarkerResult | undefined = undefined;

        const predictWebcam = () => {
            const handLandmarker = handLandmarkerRef.current;
            if (!handLandmarker || !cameraRef.current?.video || !canvasRef.current) {
                animationFrameId = requestAnimationFrame(predictWebcam);
                return;
            }
            const video = cameraRef.current.video;
            const canvas = canvasRef.current;
            // if (
            //     canvas.width !== video.videoWidth ||
            //     canvas.height !== video.videoHeight
            // ) {
            //     canvas.width = video.videoWidth;
            //     canvas.height = video.videoHeight;
            // }

            if (
                video.videoWidth === 0 ||
                video.videoHeight === 0
            ) {
                animationFrameId = requestAnimationFrame(predictWebcam);
                return;
            }

            const ctx = canvas.getContext("2d");
            if (!ctx) return;

            const now = performance.now();
            if (lastVideoTime !== video.currentTime) {
                lastVideoTime = video.currentTime;
                results = handLandmarker.detectForVideo(video, now);
            }

            const _isHandDetected = !!(results?.landmarks && results?.landmarks.length > 0);
            setIsHandDetected(_isHandDetected);

            ctx.save();
            ctx.clearRect(0, 0, canvas.width, canvas.height);

            if (results?.landmarks) {
                const drawingUtils = new DrawingUtils(ctx);
                results.landmarks.forEach((landmarks, i) => {
                    const handedness = results?.handedness[i][0].categoryName as "Left" | "Right";
                    const _isPalmFacingCamera = getIsPalmFacingCamera(landmarks, handedness);
                    setIsPalmFacingCamera(_isPalmFacingCamera);
                    if (_isHandDetected) {
                        onIsFacingCameraChange?.(_isPalmFacingCamera);
                    }
                    if (_isHandDetected && _isPalmFacingCamera) {
                        setHandName(handedness);

                        let fingertipIndexes = [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20];

                        if (mediaPipeRenderingTemplate === "v2") {
                            fingertipIndexes = [4, 8, 12, 16, 20];
                        }
                        const fingertipPoints = fingertipIndexes.map(idx => landmarks[idx]);
                        const fingertipConnections = [
                            { start: 0, end: 1 },
                            { start: 1, end: 2 },
                            { start: 3, end: 4 },
                            { start: 4, end: 5 },
                            { start: 5, end: 6 },
                            { start: 7, end: 8 },
                            { start: 8, end: 9 },
                            { start: 9, end: 10 },
                            { start: 11, end: 12 },
                            { start: 12, end: 13 },
                            { start: 13, end: 14 },
                            { start: 15, end: 16 },
                            { start: 16, end: 17 },
                            { start: 17, end: 18 },
                        ];
                        fingertipPoints.forEach(point => {
                            if (mediaPipeRenderingTemplate === "v2") {
                                drawingUtils.drawLandmarks([point], { color: "#ffffff4d", lineWidth: 4, fillColor: "#0b60f1" });
                            }
                            if (mediaPipeRenderingTemplate === "v3") {
                                drawingUtils.drawLandmarks([point], { color: "#0b60f1", lineWidth: 1 });
                            }
                            if (mediaPipeRenderingTemplate === "v4") {
                                drawingUtils.drawLandmarks([point], { color: "#0b60f1", lineWidth: 1 });
                                drawingUtils.drawConnectors(fingertipPoints, fingertipConnections, { color: "#a3a3a303", lineWidth: 2 });
                            }
                        });
                    }
                })
            }
            ctx.restore();

            if (running) {
                animationFrameId = requestAnimationFrame(predictWebcam);
            }
        };

        predictWebcam();

        return () => {
            running = false;
            if (animationFrameId) cancelAnimationFrame(animationFrameId);
        };
    }, [isCameraVisible, reinitializeKey, isVideoReady, isMediaPipe, handLandmarkerRef.current]);

    const getStyleCanvas = () => {
        const containerWidth = containerRef?.current?.clientWidth || 0;
        const containerHeight = containerRef?.current?.clientHeight || 0;
        const videoWidth = cameraRef?.current?.video?.videoWidth || 0;
        const videoHeight = cameraRef?.current?.video?.videoHeight || 0;
        const videoAspect = videoWidth / videoHeight;
        const containerAspect = containerWidth / containerHeight;

        let style = {};

        if (containerAspect > videoAspect) {
            const displayWidth = containerWidth;
            style = {
                position: "absolute",
                width: `${videoWidth}px`,
                height: `${videoHeight}px`,
                left: "50%",
                top: "50%",
                transform: `translate(-50%, -50%) scale(${displayWidth / videoWidth})`,
                pointerEvents: "none",
            };
        } else {
            const displayHeight = containerHeight;
            style = {
                position: "absolute",
                width: `${videoWidth}px`,
                height: `${videoHeight}px`,
                left: "50%",
                top: "50%",
                transform: `translate(-50%, -50%) scale(${displayHeight / videoHeight})`,
                pointerEvents: "none",
            };
        }
        return style;
    }

    return <ModalOverlay
        type={ModalOverlayType.Dark}
        className={`${styles.overlay} ${className}`}
        onClick={onClickOverlay}
    >
        <Modal
            onClose={onClose}
            noCloseButton
            modalClassName={styles.modal}
        >
            <div
                ref={containerRef}
                className={styles.cameraContainer}>
                {isShowHand && (!isHandDetected || !isPalmFacingCamera || !isMediaPipe) && (
                    <svg
                        className={styles.handIcon}
                        width="226"
                        height="435"
                        viewBox="0 0 226 435"
                        fill="none"
                        xmlns="http://www.w3.org/2000/svg"
                        style={{
                            transform: `translate(-50%) scale(${handName === "Right" ? 1 : -1}, 1)`,
                        }}
                    >
                        <path d="M44.9888 101.991C31.3554 80.777 16.4145 88.555 8.56162 99.382C3.99661 105.676 3.45507 113.764 3.41227 121.539C3.06124 185.302 2.66386 298.933 3.47936 322.748C4.34753 348.102 22.1089 380.082 30.881 392.903C47.521 412.857 95.0172 446.752 151.882 422.699C208.748 398.646 222.91 337.033 223 307.148V181.957V181.957C222.663 170.327 216.966 158.307 205.412 156.937C201.087 156.425 197.042 156.699 193.716 157.409C191.581 157.864 189.641 158.915 187.848 160.159V160.159C181.442 164.605 177.583 171.876 177.493 179.673L176.571 259.523L177.251 200.682L176.705 74.0516C176.618 53.7907 170.942 24.5591 151.038 28.3435C144.116 29.6596 138.353 35.2118 135.333 42.942M44.9888 101.991L46.074 202.212M44.9888 101.991L45.9236 62.9801C46.2026 51.3361 47.4303 38.0466 57.4751 32.1503C67.0778 26.5135 80.2774 27.7506 90.8392 42.942M90.8392 42.942V202.212M90.8392 42.942V38.4912C90.8392 26.0387 91.7293 11.455 102.608 5.39458C108.902 1.88803 116.617 1.86364 124.232 7.80872C130.541 12.7337 133.624 20.5445 134.497 28.5001C134.994 33.0286 135.333 38.1796 135.333 42.942M135.333 42.942V202.212" stroke="white" strokeWidth="6" strokeLinecap="round" />
                    </svg>
                )}
                {isCameraVisible && (
                    <>
                        <Webcam
                            key={reinitializeKey}
                            ref={cameraRef}
                            className={styles.camera}
                            muted
                            // width={width}
                            // height={height}
                            // disablePictureInPicture={true}
                            screenshotQuality={2}
                            videoConstraints={{
                                facingMode: { ideal: "environment" },
                                // width,
                                // height,
                                // aspectRatio: ratio,
                            }}
                            onUserMedia={onUserMedia}
                            onUserMediaError={(error) => {
                                setIsVideoReady(false);
                                setIsTorchAvailable(false);
                                console.error(error);
                                onError(error);
                            }}
                        />
                        {isMediaPipe && <canvas
                            ref={canvasRef}
                            className={styles.overlayCanvas}
                            width={cameraRef?.current?.video?.videoWidth || 0}
                            height={cameraRef?.current?.video?.videoHeight || 0}
                            style={getStyleCanvas()}
                        />}
                    </>
                )}
                <div
                    className={styles.shutterButton}
                    onClick={onClickTakePhoto}
                    style={{
                        opacity: isVideoReady ? 1 : 0.5,
                    }}
                />
                {isTorchAvailable && (
                    <button
                        className={`${styles.torchButton} ${isTorchOn ? styles.torchButtonActive : ""}`}
                        onClick={toggleTorch}
                        style={{
                            opacity: isVideoReady ? 1 : 0.5,
                        }}
                    >
                        <div className={styles.torchIconContainer}>
                            <svg
                                className={styles.torchIcon}
                                width="38"
                                height="38"
                                viewBox="0 0 1024 1024"
                                version="1.1"
                                xmlns="http://www.w3.org/2000/svg"
                            >
                                <path fill="#fff" d="M634 64H390c-35.2 0-48 28.8-48 64h340c0-35.2-12.8-64-48-64zM392.2 295c15.2 17.6 23.8 40 23.8 63.4v531.8c0 43.8 35.8 69.8 79.8 69.8h32.6c43.8 0 79.8-25.8 79.8-69.8V358.4c0-23.4 8.6-45.6 23.8-63.4 30.8-35.8 50-69 50-135H342c0 70 19.2 99.2 50.2 135z m63.8 181.6c0-31.2 25.2-56.6 56-56.6s56 25.4 56 56.6v70.8c0 31.2-25.2 56.6-56 56.6s-56-25.4-56-56.6v-70.8z" />
                                <path fill="#fff" d="M512 546m-40 0a40 40 0 1 0 80 0 40 40 0 1 0-80 0Z" />
                            </svg>
                        </div>
                    </button>
                )}
            </div>
        </Modal>
    </ModalOverlay>;
}

export default CameraModal;

