import { useCallback, useEffect, useRef } from "react";
import { apiClient } from "@/api/client";
import { useAppStore } from "@/stores/app-store";
import { usePlaybackStore } from "@/stores/playback-store";
import { useRecordingStore } from "@/stores/recording-store";

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") {
    if (MediaRecorder.isTypeSupported(AUDIO_PREFERRED_MIME))
      return AUDIO_PREFERRED_MIME;
    return AUDIO_FALLBACK_MIME;
  }
  if (MediaRecorder.isTypeSupported(VIDEO_PREFERRED_MIME))
    return VIDEO_PREFERRED_MIME;
  return VIDEO_FALLBACK_MIME;
}

export function useRecorder(
  streamId: string | null,
  networkId: string | null,
) {
  const recorderRef = useRef<MediaRecorder | null>(null);
  const streamRef = useRef<MediaStream | null>(null);
  const chunksRef = useRef<Blob[]>([]);
  const startTimeRef = useRef<number>(0);
  const mimeRef = useRef<string>("");

  const recordingMode = useRecordingStore((s) => s.recordingMode);
  const setStatus = useRecordingStore((s) => s.setStatus);
  const setError = useRecordingStore((s) => s.setError);
  const setMediaStream = useRecordingStore((s) => s.setMediaStream);
  const setReviewBlob = useRecordingStore((s) => s.setReviewBlob);
  const resetRecording = useRecordingStore((s) => s.reset);
  const addParticleToStream = useAppStore((s) => s.addParticleToStream);

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

  const confirmSend = useCallback(async () => {
    if (!streamId || !networkId) return;

    const { reviewBlob, reviewDurationMs } = useRecordingStore.getState();
    if (!reviewBlob) return;

    setStatus("uploading");

    try {
      const mimeType = reviewBlob.type || VIDEO_FALLBACK_MIME;
      const fileName = `recording-${Date.now()}.webm`;

      const { object_id, upload_url } = await apiClient.prepareUpload({
        network_id: networkId,
        name: fileName,
        content_type: mimeType,
        content_length: reviewBlob.size,
      });

      await fetch(upload_url, {
        method: "PUT",
        headers: { "Content-Type": mimeType },
        body: reviewBlob,
      });

      await apiClient.confirmUpload(object_id);

      const particle = await apiClient.createStreamParticle(streamId, {
        type: "media",
        data: {
          object_id,
          duration_ms: reviewDurationMs,
          mime_type: mimeType,
        },
      });

      addParticleToStream(streamId, particle);

      const playbackState = usePlaybackStore.getState();
      if (playbackState.streamId === streamId) {
        usePlaybackStore.setState({
          particles: [...playbackState.particles, particle],
        });
      }

      resetRecording();
    } catch (err) {
      setError(err instanceof Error ? err.message : "Upload failed");
    }
  }, [streamId, networkId, setStatus, setError, resetRecording, addParticleToStream]);

  const startRecording = useCallback(async () => {
    const currentStatus = useRecordingStore.getState().status;
    if (currentStatus !== "idle") return;

    try {
      const constraints =
        recordingMode === "video"
          ? { video: true, audio: true }
          : { audio: true };

      setStatus("recording");

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

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

      const mime = getMediaMime(recordingMode);
      mimeRef.current = mime;
      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) {
          setReviewBlob(blob, durationMs);
        } else {
          resetRecording();
        }
      };

      recorder.start();
    } catch (err) {
      stopTracks();
      setError(
        err instanceof Error ? err.message : "Failed to start recording",
      );
    }
  }, [
    recordingMode,
    setStatus,
    setError,
    setMediaStream,
    setReviewBlob,
    stopTracks,
    resetRecording,
  ]);

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

  const cancelRecording = useCallback(() => {
    const currentStatus = useRecordingStore.getState().status;

    if (currentStatus === "reviewing") {
      resetRecording();
      return;
    }

    if (recorderRef.current) {
      recorderRef.current.ondataavailable = null;
      recorderRef.current.onstop = null;
      if (recorderRef.current.state === "recording") {
        recorderRef.current.stop();
      }
    }
    stopTracks();
    resetRecording();
  }, [stopTracks, resetRecording]);

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

  return { startRecording, stopRecording, cancelRecording, confirmSend };
}
