/**
 * @ai-summary Hook for managing camera permission and media stream
 * @ai-context Handles getUserMedia API, permission state, and camera switching
 */

import { useState, useCallback, useRef, useEffect } from 'react';
import type { PermissionState, FacingMode } from './types';

interface UseCameraPermissionOptions {
  /** Initial facing mode preference */
  initialFacingMode?: FacingMode;
}

interface UseCameraPermissionReturn {
  /** Current permission state */
  permissionState: PermissionState;
  /** Active media stream */
  stream: MediaStream | null;
  /** Error message if any */
  error: string | null;
  /** Current camera facing mode */
  facingMode: FacingMode;
  /** Whether the device has multiple cameras */
  hasMultipleCameras: boolean;
  /** Request camera access */
  requestPermission: () => Promise<void>;
  /** Switch between front and rear camera */
  switchCamera: () => Promise<void>;
  /** Stop the media stream */
  stopStream: () => void;
}

/**
 * Hook for managing camera permissions and media streams
 */
export function useCameraPermission(
  options: UseCameraPermissionOptions = {}
): UseCameraPermissionReturn {
  const { initialFacingMode = 'environment' } = options;

  const [permissionState, setPermissionState] = useState<PermissionState>('prompt');
  const [stream, setStream] = useState<MediaStream | null>(null);
  const [error, setError] = useState<string | null>(null);
  const [facingMode, setFacingMode] = useState<FacingMode>(initialFacingMode);
  const [hasMultipleCameras, setHasMultipleCameras] = useState(false);

  const streamRef = useRef<MediaStream | null>(null);

  // Check for multiple cameras on mount
  useEffect(() => {
    const checkCameras = async () => {
      try {
        if (!navigator.mediaDevices?.enumerateDevices) {
          return;
        }
        const devices = await navigator.mediaDevices.enumerateDevices();
        const videoInputs = devices.filter((d) => d.kind === 'videoinput');
        setHasMultipleCameras(videoInputs.length > 1);
      } catch {
        // Silently fail - camera enumeration is not critical
      }
    };
    checkCameras();
  }, []);

  // Clean up stream on unmount
  useEffect(() => {
    return () => {
      if (streamRef.current) {
        streamRef.current.getTracks().forEach((track) => track.stop());
      }
    };
  }, []);

  const stopStream = useCallback(() => {
    if (streamRef.current) {
      streamRef.current.getTracks().forEach((track) => track.stop());
      streamRef.current = null;
      setStream(null);
    }
  }, []);

  const requestPermission = useCallback(async () => {
    // Check if getUserMedia is available
    if (!navigator.mediaDevices?.getUserMedia) {
      setPermissionState('unavailable');
      setError('Camera access is not supported in this browser');
      return;
    }

    // Stop any existing stream
    stopStream();
    setError(null);

    try {
      const constraints: MediaStreamConstraints = {
        video: {
          facingMode: { ideal: facingMode },
          width: { ideal: 1920 },
          height: { ideal: 1080 },
        },
        audio: false,
      };

      const mediaStream = await navigator.mediaDevices.getUserMedia(constraints);
      streamRef.current = mediaStream;
      setStream(mediaStream);
      setPermissionState('granted');

      // Re-check for multiple cameras after permission granted
      const devices = await navigator.mediaDevices.enumerateDevices();
      const videoInputs = devices.filter((d) => d.kind === 'videoinput');
      setHasMultipleCameras(videoInputs.length > 1);
    } catch (err) {
      const errorMessage = err instanceof Error ? err.message : 'Unknown error';

      if (
        err instanceof DOMException &&
        (err.name === 'NotAllowedError' || err.name === 'PermissionDeniedError')
      ) {
        setPermissionState('denied');
        setError('Camera permission was denied. Please enable camera access in your browser settings.');
      } else if (
        err instanceof DOMException &&
        (err.name === 'NotFoundError' || err.name === 'DevicesNotFoundError')
      ) {
        setPermissionState('unavailable');
        setError('No camera found on this device');
      } else if (
        err instanceof DOMException &&
        (err.name === 'NotReadableError' || err.name === 'TrackStartError')
      ) {
        setPermissionState('unavailable');
        setError('Camera is already in use by another application');
      } else {
        setPermissionState('unavailable');
        setError(`Failed to access camera: ${errorMessage}`);
      }
    }
  }, [facingMode, stopStream]);

  const switchCamera = useCallback(async () => {
    const newFacingMode: FacingMode = facingMode === 'environment' ? 'user' : 'environment';
    setFacingMode(newFacingMode);

    // If we have an active stream, restart with new facing mode
    if (stream) {
      stopStream();

      try {
        const constraints: MediaStreamConstraints = {
          video: {
            facingMode: { exact: newFacingMode },
            width: { ideal: 1920 },
            height: { ideal: 1080 },
          },
          audio: false,
        };

        const mediaStream = await navigator.mediaDevices.getUserMedia(constraints);
        streamRef.current = mediaStream;
        setStream(mediaStream);
      } catch {
        // If exact fails, try ideal
        try {
          const constraints: MediaStreamConstraints = {
            video: {
              facingMode: { ideal: newFacingMode },
              width: { ideal: 1920 },
              height: { ideal: 1080 },
            },
            audio: false,
          };

          const mediaStream = await navigator.mediaDevices.getUserMedia(constraints);
          streamRef.current = mediaStream;
          setStream(mediaStream);
        } catch (err) {
          const errorMessage = err instanceof Error ? err.message : 'Unknown error';
          setError(`Failed to switch camera: ${errorMessage}`);
          // Revert facing mode
          setFacingMode(facingMode);
        }
      }
    }
  }, [facingMode, stream, stopStream]);

  return {
    permissionState,
    stream,
    error,
    facingMode,
    hasMultipleCameras,
    requestPermission,
    switchCamera,
    stopStream,
  };
}

export default useCameraPermission;
