import { useCallback, useEffect, useRef } from "react";
import { platform } from "@/lib/platform";
import { requireDesktop } from "@/lib/platform/desktop-only";

const VIDEO_PREFERRED_MIME = "video/webm;codecs=vp9,opus";
const VIDEO_FALLBACK_MIME = "video/webm";

function getScreenMime(): string {
  return MediaRecorder.isTypeSupported(VIDEO_PREFERRED_MIME)
    ? VIDEO_PREFERRED_MIME
    : VIDEO_FALLBACK_MIME;
}

// ---------------------------------------------------------------------------
// Hook
// ---------------------------------------------------------------------------

interface UseScreenRecorderOptions {
  micDeviceId?: string;
  onFinish: (blob: Blob, durationMs: number, mimeType: string) => void;
  onError: (message: string) => void;
}

async function getMicStream(
  micDeviceId: string | undefined,
): Promise<MediaStream> {
  const constraints: MediaStreamConstraints = {
    audio: micDeviceId ? { deviceId: { exact: micDeviceId } } : true,
  };
  try {
    return await navigator.mediaDevices.getUserMedia(constraints);
  } catch (err) {
    if (
      micDeviceId &&
      err instanceof Error &&
      (err.name === "OverconstrainedError" || err.name === "NotFoundError")
    ) {
      return navigator.mediaDevices.getUserMedia({ audio: true });
    }
    throw err;
  }
}

/**
 * Manages screen recording via Electron's desktopCapturer.
 * Captures screen video + mic audio.
 */
export function useScreenRecorder({
  micDeviceId,
  onFinish,
  onError,
}: UseScreenRecorderOptions) {
  const recorderRef = useRef<MediaRecorder | null>(null);
  const screenStreamRef = useRef<MediaStream | null>(null);
  const micStreamRef = useRef<MediaStream | null>(null);
  const chunksRef = useRef<Blob[]>([]);
  const startTimeRef = useRef<number>(0);
  const cleanupIpcRef = useRef<(() => void) | null>(null);

  const onFinishRef = useRef(onFinish);
  const onErrorRef = useRef(onError);
  useEffect(() => {
    onFinishRef.current = onFinish;
    onErrorRef.current = onError;
  });

  const stopAllTracks = useCallback(() => {
    screenStreamRef.current?.getTracks().forEach((t) => t.stop());
    micStreamRef.current?.getTracks().forEach((t) => t.stop());
    screenStreamRef.current = null;
    micStreamRef.current = null;
    recorderRef.current = null;
    chunksRef.current = [];
    cleanupIpcRef.current?.();
    cleanupIpcRef.current = null;
  }, []);

  const startRecording = useCallback(
    async (sourceId: string) => {
      if (!requireDesktop("Screen recording")) return;
      try {
        // 1. Screen video
        const screenStream = await navigator.mediaDevices.getUserMedia({
          audio: false,
          video: {
            mandatory: {
              chromeMediaSource: "desktop",
              chromeMediaSourceId: sourceId,
            },
          } as MediaTrackConstraints,
        });
        screenStreamRef.current = screenStream;

        // 2. Mic audio
        const micStream = await getMicStream(micDeviceId);
        micStreamRef.current = micStream;

        // 3. Combine screen video + mic audio
        const combined = new MediaStream([
          ...screenStream.getVideoTracks(),
          ...micStream.getAudioTracks(),
        ]);

        chunksRef.current = [];
        startTimeRef.current = Date.now();

        const mime = getScreenMime();
        const recorder = new MediaRecorder(combined, { mimeType: mime });
        recorderRef.current = recorder;

        recorder.ondataavailable = (e) => {
          if (e.data.size > 0) chunksRef.current.push(e.data);
        };

        recorder.onstop = () => {
          const durationMs = Date.now() - startTimeRef.current;
          const blob = new Blob(chunksRef.current, { type: mime });
          stopAllTracks();
          platform.screenRecord.cancel();

          if (blob.size > 0) {
            onFinishRef.current(blob, durationMs, mime);
          }
        };

        recorder.start(1000);

        // 4. Show floating control window
        platform.screenRecord.start();

        // 5. Listen for stop from floating window
        cleanupIpcRef.current = platform.screenRecord.onStopRequested(() => {
          if (recorderRef.current?.state === "recording") {
            recorderRef.current.stop();
          }
        });
      } catch (err) {
        stopAllTracks();
        platform.screenRecord.cancel();
        onErrorRef.current(
          err instanceof Error ? err.message : "Failed to start screen recording",
        );
      }
    },
    [micDeviceId, stopAllTracks],
  );

  const stopRecording = useCallback(() => {
    if (recorderRef.current?.state === "recording") {
      recorderRef.current.stop();
    }
  }, []);

  const cancelRecording = useCallback(() => {
    if (recorderRef.current) {
      recorderRef.current.ondataavailable = null;
      recorderRef.current.onstop = null;
      if (recorderRef.current.state === "recording") {
        recorderRef.current.stop();
      }
    }
    stopAllTracks();
    platform.screenRecord.cancel();
  }, [stopAllTracks]);

  // Cleanup on unmount
  useEffect(() => {
    return () => {
      stopAllTracks();
      platform.screenRecord.cancel();
    };
  }, [stopAllTracks]);

  return { startRecording, stopRecording, cancelRecording };
}
