import { useState, useCallback, useRef, useEffect } from "react";

// Remote debug logger - temporary
const remoteLog = async (level, ...args) => {
  const msg = args.map(a => typeof a === 'object' ? JSON.stringify(a) : String(a)).join(' ');
  console[level](...args);
  try {
    await fetch('/api/debug-log', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ level, msg, ts: new Date().toISOString(), src: 'camera' })
    }).catch(() => {});
  } catch {}
};

/**
 * Hook for managing camera stream with swap capability
 * Most mobile devices can't open both cameras simultaneously,
 * so we open one at a time and swap on demand.
 */
export function useDualCamera() {
  const [mainStream, setMainStream] = useState(null);
  const [selfStream, setSelfStream] = useState(null); // Always null on mobile (no simultaneous dual cam)
  const [isSwapped, setIsSwapped] = useState(false); // false = front camera, true = back camera
  const [error, setError] = useState(null);
  const [isStarting, setIsStarting] = useState(false);
  const [isSwapping, setIsSwapping] = useState(false);
  const [permissions, setPermissions] = useState({ video: false, audio: false });
  const [canSwap, setCanSwap] = useState(false); // True if we detected 2 cameras

  // Track available camera device IDs
  const frontDeviceIdRef = useRef(null);
  const backDeviceIdRef = useRef(null);
  const currentStreamRef = useRef(null);
  const audioStreamRef = useRef(null); // Keep audio separate so we don't lose it on swap

  const checkPermissions = useCallback(async () => {
    try {
      const devices = await navigator.mediaDevices.enumerateDevices();
      const hasVideo = devices.some((d) => d.kind === "videoinput");
      const hasAudio = devices.some((d) => d.kind === "audioinput");
      setPermissions({ video: hasVideo, audio: hasAudio });
      return { video: hasVideo, audio: hasAudio };
    } catch (err) {
      console.error("[useDualCamera] Permission check failed:", err);
      return { video: false, audio: false };
    }
  }, []);

  // Track both streams for dual camera mode (when device supports it)
  const frontStreamRef = useRef(null);
  const backStreamRef = useRef(null);

  const startCameras = useCallback(async () => {
    if (isStarting) return;
    setIsStarting(true);
    setError(null);

    try {
      // First request any camera to get permissions (labels are empty until permission granted)
      const tempStream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
      tempStream.getTracks().forEach(t => t.stop());

      // Small delay after releasing temp stream
      await new Promise(r => setTimeout(r, 300));

      // Now list available cameras with labels
      const devices = await navigator.mediaDevices.enumerateDevices();
      const videoDevices = devices.filter((d) => d.kind === "videoinput");
      remoteLog("log", "[useDualCamera] Available cameras:", videoDevices.map(d => ({
        id: d.deviceId?.slice(0,8),
        label: d.label,
      })));

      // Try to identify front vs back by label
      for (const device of videoDevices) {
        const label = (device.label || "").toLowerCase();
        if (label.includes("front") || label.includes("user") || label.includes("selfie")) {
          frontDeviceIdRef.current = device.deviceId;
        } else if (label.includes("back") || label.includes("rear") || label.includes("environment")) {
          backDeviceIdRef.current = device.deviceId;
        }
      }

      // If we couldn't identify by label, use first two devices
      if (!frontDeviceIdRef.current && !backDeviceIdRef.current && videoDevices.length >= 2) {
        frontDeviceIdRef.current = videoDevices[0].deviceId;
        backDeviceIdRef.current = videoDevices[1].deviceId;
      } else if (!frontDeviceIdRef.current && videoDevices.length >= 1) {
        frontDeviceIdRef.current = videoDevices[0].deviceId;
      }

      const hasTwoCameras = !!(frontDeviceIdRef.current && backDeviceIdRef.current);
      setCanSwap(hasTwoCameras);

      remoteLog("log", "[useDualCamera] Detected devices:", {
        front: frontDeviceIdRef.current?.slice(0,8),
        back: backDeviceIdRef.current?.slice(0,8),
        hasTwoCameras
      });

      // Open FRONT camera first (main view with audio)
      remoteLog("log", "[useDualCamera] Opening front camera...");
      const frontStream = await navigator.mediaDevices.getUserMedia({
        video: frontDeviceIdRef.current
          ? { deviceId: { exact: frontDeviceIdRef.current }, width: { ideal: 1280 }, height: { ideal: 720 } }
          : { facingMode: "user", width: { ideal: 1280 }, height: { ideal: 720 } },
        audio: true,
      });
      frontStreamRef.current = frontStream;
      currentStreamRef.current = frontStream;
      audioStreamRef.current = frontStream; // Audio comes from this stream

      const frontTrack = frontStream.getVideoTracks()[0];
      remoteLog("log", "[useDualCamera] Front camera opened:", frontTrack?.label);

      // Try to open BACK camera simultaneously (for PiP on devices that support it)
      let backStream = null;
      if (hasTwoCameras) {
        await new Promise(r => setTimeout(r, 500));

        try {
          remoteLog("log", "[useDualCamera] Trying to open back camera simultaneously...");
          backStream = await navigator.mediaDevices.getUserMedia({
            video: backDeviceIdRef.current
              ? { deviceId: { exact: backDeviceIdRef.current }, width: { ideal: 640 }, height: { ideal: 480 } }
              : { facingMode: { exact: "environment" }, width: { ideal: 640 }, height: { ideal: 480 } },
            audio: false,
          });
          backStreamRef.current = backStream;
          const backTrack = backStream.getVideoTracks()[0];
          remoteLog("log", "[useDualCamera] Back camera opened (dual mode):", backTrack?.label);
        } catch (backErr) {
          remoteLog("warn", "[useDualCamera] Back camera failed (single cam mode):", backErr.name, backErr.message);
          // Device doesn't support dual cameras - that's ok, we'll swap on demand
        }
      }

      setMainStream(frontStream);
      setSelfStream(backStream);
      setIsSwapped(false);
      setPermissions({ video: true, audio: true });

      remoteLog("log", "[useDualCamera] Cameras started:", {
        front: !!frontStream,
        back: !!backStream,
        canSwap: hasTwoCameras
      });
    } catch (err) {
      remoteLog("error", "[useDualCamera] Failed to start cameras:", err.name, err.message);
      setError(err.message || "Failed to access camera");

      if (err.name === "NotAllowedError") {
        setError("Camera permission denied. Please allow camera access.");
      } else if (err.name === "NotFoundError") {
        setError("No camera found on this device.");
      } else if (err.name === "NotReadableError") {
        setError("Camera is already in use by another app.");
      } else if (err.name === "OverconstrainedError") {
        setError("Camera constraints not supported. Try again.");
      }
    } finally {
      setIsStarting(false);
    }
  }, [isStarting]);

  // Swap cameras - works in both dual and single camera modes
  const swapCameras = useCallback(async () => {
    if (!canSwap || isSwapping) {
      remoteLog("warn", "[useDualCamera] Cannot swap - no second camera or already swapping");
      return;
    }

    // Check if we have both streams open (dual camera mode)
    if (frontStreamRef.current && backStreamRef.current) {
      // Dual camera mode - just swap the streams
      setIsSwapped((prev) => {
        const newSwapped = !prev;
        remoteLog("log", "[useDualCamera] Swapping streams (dual mode), newSwapped:", newSwapped);

        if (newSwapped) {
          setMainStream(backStreamRef.current);
          setSelfStream(frontStreamRef.current);
          currentStreamRef.current = backStreamRef.current;
        } else {
          setMainStream(frontStreamRef.current);
          setSelfStream(backStreamRef.current);
          currentStreamRef.current = frontStreamRef.current;
        }

        return newSwapped;
      });
      return;
    }

    // Single camera mode - close current and open other
    setIsSwapping(true);
    const newSwapped = !isSwapped;
    remoteLog("log", "[useDualCamera] Swapping cameras (single mode), switching to:", newSwapped ? "back" : "front");

    try {
      // Stop current video track (keep audio)
      const currentStream = currentStreamRef.current;
      if (currentStream) {
        currentStream.getVideoTracks().forEach(t => t.stop());
      }

      // Open the other camera
      const targetDeviceId = newSwapped ? backDeviceIdRef.current : frontDeviceIdRef.current;
      const facingMode = newSwapped ? "environment" : "user";

      remoteLog("log", "[useDualCamera] Opening camera:", { targetDeviceId: targetDeviceId?.slice(0,8), facingMode });

      const newStream = await navigator.mediaDevices.getUserMedia({
        video: targetDeviceId
          ? { deviceId: { exact: targetDeviceId }, width: { ideal: 1280 }, height: { ideal: 720 } }
          : { facingMode: { exact: facingMode }, width: { ideal: 1280 }, height: { ideal: 720 } },
        audio: false, // Don't request audio again
      });

      // Create a combined stream with new video + existing audio
      const audioTrack = audioStreamRef.current?.getAudioTracks()[0];
      const combinedStream = new MediaStream();
      newStream.getVideoTracks().forEach(t => combinedStream.addTrack(t));
      if (audioTrack) {
        combinedStream.addTrack(audioTrack);
      }

      // Update refs
      if (newSwapped) {
        if (frontStreamRef.current) {
          frontStreamRef.current.getVideoTracks().forEach(t => t.stop());
        }
        backStreamRef.current = newStream;
        frontStreamRef.current = null;
      } else {
        if (backStreamRef.current) {
          backStreamRef.current.getVideoTracks().forEach(t => t.stop());
        }
        frontStreamRef.current = newStream;
        backStreamRef.current = null;
      }

      currentStreamRef.current = combinedStream;
      setMainStream(combinedStream);
      setSelfStream(null); // No PiP in single camera mode
      setIsSwapped(newSwapped);

      const newTrack = newStream.getVideoTracks()[0];
      remoteLog("log", "[useDualCamera] Camera swapped to:", newTrack?.label);

    } catch (err) {
      remoteLog("error", "[useDualCamera] Failed to swap cameras:", err.name, err.message);
      setError("Failed to swap cameras: " + err.message);
    } finally {
      setIsSwapping(false);
    }
  }, [canSwap, isSwapped, isSwapping]);

  const stopCameras = useCallback(() => {
    if (frontStreamRef.current) {
      frontStreamRef.current.getTracks().forEach(t => t.stop());
      frontStreamRef.current = null;
    }
    if (backStreamRef.current) {
      backStreamRef.current.getTracks().forEach(t => t.stop());
      backStreamRef.current = null;
    }
    if (audioStreamRef.current && audioStreamRef.current !== frontStreamRef.current) {
      audioStreamRef.current.getTracks().forEach(t => t.stop());
    }
    audioStreamRef.current = null;
    currentStreamRef.current = null;
    frontDeviceIdRef.current = null;
    backDeviceIdRef.current = null;

    setMainStream(null);
    setSelfStream(null);
    setIsSwapped(false);
    setError(null);
    setCanSwap(false);

    remoteLog("log", "[useDualCamera] Cameras stopped");
  }, []);

  // Mute/unmute audio
  const toggleAudio = useCallback((muted) => {
    const stream = audioStreamRef.current;
    if (stream) {
      stream.getAudioTracks().forEach((track) => {
        track.enabled = !muted;
      });
    }
  }, []);

  // Enable/disable video
  const toggleVideo = useCallback((disabled) => {
    const stream = currentStreamRef.current;
    if (stream) {
      stream.getVideoTracks().forEach((track) => {
        track.enabled = !disabled;
      });
    }
  }, []);

  // Cleanup on unmount
  useEffect(() => {
    return () => {
      stopCameras();
    };
  }, [stopCameras]);

  return {
    mainStream,
    selfStream,
    isSwapped,
    error,
    isStarting,
    isSwapping,
    permissions,
    hasDualCameras: canSwap, // Can we swap between cameras?
    startCameras,
    swapCameras,
    stopCameras,
    toggleAudio,
    toggleVideo,
    checkPermissions,
  };
}

export default useDualCamera;
