import { useEffect, useRef, useState } from 'react';
import {
  detectCardShapesFromFrame,
  DETECTION_START_DELAY_MS,
  mergeDetectedShapesIntoTrackedCards,
  selectCardsReadyForVerification,
  SHAPE_DETECTION_INTERVAL_MS,
  VERIFICATION_INTERVAL_MS,
} from './scanner-card-detection.js';

/**
 * Camera stream + OpenCV shape detection loop for the card scanner.
 * Identification callbacks stay in the parent component.
 */
export function useCameraScanner({ onError, onVerifyCard, verificationPausedRef }) {
  const [isStreaming, setIsStreaming] = useState(false);
  const [isDetecting, setIsDetecting] = useState(false);
  const [trackedCards, setTrackedCards] = useState([]);
  const [videoMetrics, setVideoMetrics] = useState({ width: 0, height: 0 });

  const videoRef = useRef(null);
  const canvasRef = useRef(null);
  const detectionCanvasRef = useRef(null);
  const streamRef = useRef(null);
  const detectionIntervalRef = useRef(null);
  const trackingIntervalRef = useRef(null);
  const trackedCardsRef = useRef([]);
  const nextCardIdRef = useRef(1);
  const isStreamingRef = useRef(false);

  useEffect(() => {
    isStreamingRef.current = isStreaming;
  }, [isStreaming]);

  useEffect(() => {
    if (canvasRef.current) {
      canvasRef.current.getContext('2d', { willReadFrequently: true });
    }
    if (detectionCanvasRef.current) {
      detectionCanvasRef.current.getContext('2d', { willReadFrequently: true });
    }
  }, []);

  const detectCardShapes = () => {
    if (!videoRef.current || !detectionCanvasRef.current || !isStreamingRef.current) return [];
    return detectCardShapesFromFrame(videoRef.current, detectionCanvasRef.current);
  };

  const updateTrackedCards = (detectedShapes) => {
    const { cards, nextCardId } = mergeDetectedShapesIntoTrackedCards(
      trackedCardsRef.current,
      detectedShapes,
      nextCardIdRef.current
    );
    nextCardIdRef.current = nextCardId;
    trackedCardsRef.current = cards;
    setTrackedCards(cards);
  };

  const stopDetection = () => {
    console.log('🛑 Stopping card detection...');
    setIsDetecting(false);

    if (detectionIntervalRef.current) {
      clearInterval(detectionIntervalRef.current);
      detectionIntervalRef.current = null;
    }

    if (trackingIntervalRef.current) {
      clearInterval(trackingIntervalRef.current);
      trackingIntervalRef.current = null;
    }

    trackedCardsRef.current = [];
    setTrackedCards([]);
  };

  const startDetection = () => {
    if (detectionIntervalRef.current || !isStreamingRef.current) return;

    console.log('🎯 Starting continuous card detection...');
    setIsDetecting(true);

    detectionIntervalRef.current = setInterval(() => {
      const shapes = detectCardShapes();
      updateTrackedCards(shapes);
    }, SHAPE_DETECTION_INTERVAL_MS);

    trackingIntervalRef.current = setInterval(() => {
      if (verificationPausedRef?.current) return;

      const cardsToVerify = selectCardsReadyForVerification(trackedCardsRef.current);
      cardsToVerify.slice(0, 1).forEach((card) => {
        onVerifyCard?.(card);
      });
    }, VERIFICATION_INTERVAL_MS);
  };

  const stopCamera = () => {
    setIsStreaming(false);
    isStreamingRef.current = false;
    stopDetection();

    if (streamRef.current) {
      streamRef.current.getTracks().forEach((track) => track.stop());
      streamRef.current = null;
    }

    if (videoRef.current) {
      videoRef.current.srcObject = null;
    }
  };

  const startCamera = async () => {
    try {
      console.log('🎥 Starting camera...');
      const stream = await navigator.mediaDevices.getUserMedia({
        video: {
          facingMode: 'environment',
          width: { ideal: 1280 },
          height: { ideal: 720 },
          aspectRatio: { ideal: 16 / 9 },
        },
      });

      console.log('📹 Camera stream obtained:', stream);

      if (!videoRef.current) {
        console.error('❌ Video element not available');
        onError?.('Video element not available');
        return;
      }

      videoRef.current.srcObject = stream;
      streamRef.current = stream;

      videoRef.current.onloadedmetadata = () => {
        console.log('📺 Video metadata loaded, attempting to play...');
        videoRef.current?.play().then(() => {
          console.log('▶️ Video playback started successfully');
          setIsStreaming(true);
          isStreamingRef.current = true;
        }).catch((err) => {
          console.error('❌ Video playback failed:', err);
          onError?.(`Video playback failed: ${err.message}`);
        });
      };

      videoRef.current.onerror = (err) => {
        console.error('❌ Video element error:', err);
        onError?.('Video element error occurred');
      };

      setTimeout(() => {
        if (!isStreamingRef.current && videoRef.current?.readyState >= 2) {
          console.log('🔄 Fallback: Attempting to play video directly...');
          videoRef.current.play().then(() => {
            console.log('▶️ Fallback video playback started');
            setIsStreaming(true);
            isStreamingRef.current = true;
          }).catch(console.error);
        }
      }, 2000);
    } catch (err) {
      console.error('❌ Camera access error:', err);
      onError?.(`Unable to access camera: ${err.message}`);
    }
  };

  useEffect(() => {
    const video = videoRef.current;
    if (!video || !isStreaming) return undefined;

    const syncVideoMetrics = () => {
      setVideoMetrics({
        width: video.videoWidth || 0,
        height: video.videoHeight || 0,
      });
    };

    video.addEventListener('loadedmetadata', syncVideoMetrics);
    video.addEventListener('resize', syncVideoMetrics);
    syncVideoMetrics();

    return () => {
      video.removeEventListener('loadedmetadata', syncVideoMetrics);
      video.removeEventListener('resize', syncVideoMetrics);
    };
  }, [isStreaming]);

  useEffect(() => {
    if (!isStreaming || isDetecting) return undefined;

    const timerId = setTimeout(() => {
      startDetection();
    }, DETECTION_START_DELAY_MS);

    return () => clearTimeout(timerId);
    // eslint-disable-next-line react-hooks/exhaustive-deps -- start detection once per stream session
  }, [isStreaming]);

  useEffect(() => {
    return () => {
      stopCamera();
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps -- run stopCamera only on unmount
  }, []);

  return {
    videoRef,
    canvasRef,
    detectionCanvasRef,
    isStreaming,
    isDetecting,
    trackedCards,
    videoMetrics,
    startCamera,
    stopCamera,
  };
}
