
import React, { useRef, useEffect, useState } from 'react';
import Webcam from 'react-webcam';
import { ProgressIndicator } from './ProgressIndicator';
import type { HeadMovement } from '../types/faceLandmarks';
import { MovementInstruction } from './MovementInstruction';
import * as facemesh from "@tensorflow-models/face-landmarks-detection";
import { drawMesh, type Prediction, type ScaledMesh } from '../utils/drawMesh';
import type { FaceRecognitionProps } from '../types';

import * as tf from '@tensorflow/tfjs';
import '@tensorflow/tfjs-backend-webgl';
import { useTranslation } from '../i18n/useTranslation';

type CapturedStep = {
  step: string;
  img: string;
};

const movementSteps = [
  "faceClose150",
  "faceClose200",
  "faceClose250",
  "blinkLeft",
  "blinkRight",
  "center",
  "left",
  "right",
  "up",
  "down",
];

export const DetectionFrame: React.FC<FaceRecognitionProps> = ({onComplete, lang}) => {
  const videoConstraints = {
    width: 640,
    height: 480,
    facingMode: "user"
  };

  const describ = useTranslation(lang)

  const webcamRef = useRef<Webcam>(null);
  const canvasRef = useRef(null);

  const faceWidth: React.RefObject<number> = useRef(150);

  const currentStep = useRef(0);
  const cooldownRef = useRef(false);

  const [capturedImages, setCapturedImages] = useState<CapturedStep[]>([]);

  const movements: HeadMovement[] = ["faceClose150",
  "faceClose200",
  "faceClose250",
  "blinkLeft",
  "blinkRight",
  "center",
  "left",
  "right",
  "up",
  "down",];
  const totalSteps = movements.length;
  const [completedMovements, setCompletedMovements] = useState<HeadMovement[]>([]);
  const progress = (completedMovements.length / totalSteps) * 100;
  const [currentMovement, setCurrentMovement] = useState<HeadMovement>('center');
  const [detectionError, setErrorDetect] = useState<string | null>(null)
  const [isLoading, setIsLoading] = useState(true);
  const [cameraError, setCameraError] = useState<string | null>(null);


  const isFaceClose = (landmarks: ScaledMesh) => {
    const leftCheek = landmarks[234];
    const rightCheek = landmarks[454];
    const dist = Math.hypot(
      rightCheek[0] - leftCheek[0],
      rightCheek[1] - leftCheek[1]
    );
    return dist > faceWidth.current;
  };

  const calculateEAR = (eye: [number, number, number][]) => {
    const vertical1 = Math.hypot(eye[1][0] - eye[5][0], eye[1][1] - eye[5][1]);
    const vertical2 = Math.hypot(eye[2][0] - eye[4][0], eye[2][1] - eye[4][1]);
    const horizontal = Math.hypot(eye[0][0] - eye[3][0], eye[0][1] - eye[3][1]);
    return (vertical1 + vertical2) / (2.0 * horizontal);
  };

  const detectLeftEyeBlink = (landmarks: ScaledMesh) => {
    const leftEyeIndices = [33, 160, 158, 133, 153, 144];
    const leftEye = leftEyeIndices.map((i) => landmarks[i]);
    const leftEAR = calculateEAR(leftEye);
    return leftEAR < 0.2;
  };

  const detectRightEyeBlink = (landmarks: ScaledMesh) => {
    const rightEyeIndices = [362, 385, 387, 263, 373, 380];
    const rightEye = rightEyeIndices.map((i) => landmarks[i]);
    const rightEAR = calculateEAR(rightEye);
    return rightEAR < 0.2;
  };

  const detectMovement = (landmarks: ScaledMesh, expectedDirection:string) => {
    const noseTip = landmarks[1];
    const leftEye = landmarks[33];
    const rightEye = landmarks[263];

    const noseX = noseTip[0];
    const noseY = noseTip[1];

    const eyeCenterX = (leftEye[0] + rightEye[0]) / 2;
    const eyeCenterY = (leftEye[1] + rightEye[1]) / 2;

    const dx = noseX - eyeCenterX;
    const dy = noseY - eyeCenterY;

    let direction = "";

    const horizontalThreshold = 15;
    const verticalThreshold = 5;
    // console.log({dy})

    if (dx < -horizontalThreshold) direction = "left";
    else if (dx > horizontalThreshold) direction = "right";
    else if (dy < -verticalThreshold) direction = "up";
    else if (dy > verticalThreshold) direction = "down";

    return direction === expectedDirection;
  };


  const handleUserMedia = () => {
    setIsLoading(false)
    setCameraError(null);
  };

  const handleUserMediaError = () => {
    setCameraError('Acesso à câmera foi negado. Por favor, permita o uso da câmera nas configurações do navegador e recarregue a página.');
  };
  const captureImage = (): string | null => {
    const webcam = webcamRef.current;
  
    if (!webcam || !webcam.video) return null;
  
    const video = webcam.video as HTMLVideoElement;
  
    const tempCanvas = document.createElement("canvas");
    tempCanvas.width = video.videoWidth;
    tempCanvas.height = video.videoHeight;
  
    const ctx = tempCanvas.getContext("2d");
    if (!ctx) return null;
  
    ctx.drawImage(video, 0, 0, tempCanvas.width, tempCanvas.height);
  
    return tempCanvas.toDataURL("image/jpeg");
  };

  const detect = async (net: facemesh.FaceLandmarksDetector) => {

    if(progress >= 100){

      console.log("Face Detect Complet")

      return
    }

    const video = webcamRef.current?.video;
    const canvas = canvasRef.current;

    if (video && video.readyState === 4) {

      const videoWidth = video.videoWidth;
      const videoHeight = video.videoHeight;

      video.width = videoWidth;
      video.height = videoHeight;
      // canvas.width = videoWidth;
      // canvas.height = videoHeight;


      const face = await net.estimateFaces({ input: video }) as Prediction[]

      // const ctx = canvasRef?.current?.getContext("2d");
      // if(ctx == undefined){
      //   // requestAnimationFrame(() => drawMesh(face, ctx));
      // }

      if (face.length == 1 && !cooldownRef.current ) {
        const landmarks = face[0].scaledMesh;
        const expected = movementSteps[currentStep.current];

        let matched = false;
        
        switch (expected) {
          case "faceClose150":
            matched = isFaceClose(landmarks);
            break;
          case "faceClose200":
            matched = isFaceClose(landmarks);
            break;
          case "faceClose250":
            matched = isFaceClose(landmarks);
            break;
          case "center":
            matched = isFaceClose(landmarks);
            break;
          case "blinkLeft":
            matched = detectLeftEyeBlink(landmarks);
            break;
          case "blinkRight":
            matched = detectRightEyeBlink(landmarks);
            break;
          default:
            faceWidth.current = 100
            matched = detectMovement(landmarks, expected);
        }

        // console.log({matched, faceWidth:faceWidth.current, expected})
        if (matched) {
          cooldownRef.current = true;
          const img = captureImage();

          if (img) {
            setCapturedImages((prev) => [...prev, { step: expected, img }]);
            console.log(`Imagem capturada para: ${expected}`);

            console.log(`✔️ Movimento correto: ${expected}`);

            if(expected == 'faceClose150'){

              faceWidth.current = 200
            }

            if(expected == 'faceClose200'){

              faceWidth.current = 250
            }

            setCompletedMovements(prev => [...prev, currentMovement]);
            setCurrentMovement(movements[currentStep.current + 1])

            currentStep.current += 1;
            
            if (currentStep.current >= movementSteps.length) {

              console.log("✅ Todos os movimentos concluídos!")
            }
            
          }

          setTimeout(() => {
            cooldownRef.current = false;
          }, 1500);       
        }

       }
    
    }
   
  };

  const handleEndProcess =()=>{

    if (onComplete && capturedImages.length > 0) {
      onComplete({ images: capturedImages });
    }
    
  }

  async function ActiveBackend(){
    await tf.setBackend('webgl');
await tf.ready();
  }
  

  useEffect(() => {

    ActiveBackend()
    let animationId: number;
    let net: facemesh.FaceLandmarksDetector;
  
    const startDetection = async () => {
      net = await facemesh.load(facemesh.SupportedPackages.mediapipeFacemesh);
  
      const detectLoop = async () => {
        await detect(net);
        animationId = requestAnimationFrame(detectLoop);
      };
  
      detectLoop();
    };
  
    startDetection();
  
    return () => {
      cancelAnimationFrame(animationId);
    };
  }, []);
  

  return(
    <div className="text-center">
      <div className="relative w-full">

        <div className="relative">
          <Webcam
            ref={webcamRef}
            audio={false}
            videoConstraints={videoConstraints}
            onUserMedia={handleUserMedia}
            onUserMediaError={handleUserMediaError}
            className="w-full rounded-lg shadow-md"
            mirrored={true}
          />

          {/* <canvas
            ref={canvasRef}
            style={{
              position: "absolute",
              marginLeft: "auto",
              marginRight: "auto",
              top:-10,
              left: 0,
              right: 0,
              textAlign: "center",
              width: videoConstraints.width,
              height: videoConstraints.height,
              zIndex:10
            }}
          /> */}
        </div>

      </div>
      
      <div className="mt-6">

        <ProgressIndicator progress={progress} lang={lang} />
        
        <div className="mt-4">

          <MovementInstruction movement={currentMovement}  progress={progress} onEnd={()=> handleEndProcess()} lang={lang}/>

        </div>
        
        {detectionError && (
          <div className="bg-red-50 border border-red-200 rounded-md p-3 mt-4">
            <p className="text-red-700 text-sm">{detectionError}</p>
          </div>
        )}

      </div>
    </div>
  )
}