import { useCallback, useEffect, useRef } from "react";
import type { RecordingMode } from "@/hooks/use-recording-mode";

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

function getMediaMime(mode: "video" | "audio"): string {
  if (mode === "audio") {
    return MediaRecorder.isTypeSupported(AUDIO_PREFERRED_MIME)
      ? AUDIO_PREFERRED_MIME
      : AUDIO_FALLBACK_MIME;
  }
  return MediaRecorder.isTypeSupported(VIDEO_PREFERRED_MIME)
    ? VIDEO_PREFERRED_MIME
    : VIDEO_FALLBACK_MIME;
}

interface UseRecorderOptions {
  mode: RecordingMode;
  onStreamReady: (stream: MediaStream) => void;
  onStreamCleanup: () => void;
  onFinish: (blob: Blob, durationMs: number, mimeType: string) => void;
  onError: (message: string) => void;
}

/**
 * Manages MediaRecorder lifecycle. Pure media utility — knows nothing
 * about application state. The consumer provides callbacks for all outputs.
 */
export function useRecorder({
  mode,
  onStreamReady,
  onStreamCleanup,
  onFinish,
  onError,
}: UseRecorderOptions) {
  const recorderRef = useRef<MediaRecorder | null>(null);
  const streamRef = useRef<MediaStream | null>(null);
  const chunksRef = useRef<Blob[]>([]);
  const startTimeRef = useRef<number>(0);

  // Refs to avoid stale closures in MediaRecorder event handlers
  const onStreamCleanupRef = useRef(onStreamCleanup);
  const onFinishRef = useRef(onFinish);
  const onErrorRef = useRef(onError);
  useEffect(() => {
    onStreamCleanupRef.current = onStreamCleanup;
    onFinishRef.current = onFinish;
    onErrorRef.current = onError;
  });

  const stopTracks = useCallback(() => {
    streamRef.current?.getTracks().forEach((t) => t.stop());
    streamRef.current = null;
    recorderRef.current = null;
    chunksRef.current = [];
    onStreamCleanupRef.current();
  }, []);

  const startRecording = useCallback(async () => {
    try {
      const constraints =
        mode === "video" ? { video: true, audio: true } : { audio: true };

      const mediaStream =
        await navigator.mediaDevices.getUserMedia(constraints);

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

      const mime = getMediaMime(mode);
      const recorder = new MediaRecorder(mediaStream, { 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 });
        stopTracks();

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

      recorder.start();
    } catch (err) {
      stopTracks();
      onErrorRef.current(
        err instanceof Error ? err.message : "Failed to start recording",
      );
    }
  }, [mode, onStreamReady, stopTracks]);

  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();
      }
    }
    stopTracks();
  }, [stopTracks]);

  useEffect(() => {
    return () => stopTracks();
  }, [stopTracks]);

  return { startRecording, stopRecording, cancelRecording };
}
