"use client";

import React, { createContext, useContext, useEffect, useRef, useState } from "react";
import { useQuery, useMutation } from "convex/react";
import { api } from "@/convex/_generated/api";
import { Id } from "@/convex/_generated/dataModel";

interface PlaybackContextType {
  // Current playback state
  currentTrackId: Id<"atoms"> | null;
  isPlaying: boolean;
  position: number;
  volume: number;
  followMode: "NONE" | "ROOM" | "USER";

  // Playback controls
  play: (atomId: Id<"atoms">) => void;
  pause: () => void;
  resume: () => void;
  seek: (position: number) => void;
  setVolume: (volume: number) => void;

  // Follow mode controls
  setFollowMode: (mode: "NONE" | "ROOM" | "USER", userId?: Id<"users">) => void;

  // Room leader controls
  isRoomLeader: boolean;
  setRoomLeader: (isLeader: boolean) => void;
  roomLeader: any | null;

  // Audio element ref
  audioRef: React.RefObject<HTMLAudioElement | null>;
}

const PlaybackContext = createContext<PlaybackContextType | undefined>(undefined);

export function PlaybackProvider({
  children,
  roomId,
}: {
  children: React.ReactNode;
  roomId: Id<"rooms"> | null;
}) {
  const audioRef = useRef<HTMLAudioElement>(null);

  // Query user's playback state
  const userPlaybackState = useQuery(
    api.playback.getUserPlaybackState,
    roomId ? { roomId } : "skip"
  );

  // Query room playback state
  const roomPlaybackState = useQuery(
    api.playback.getRoomPlaybackState,
    roomId ? { roomId } : "skip"
  );

  // Query room leader
  const roomLeader = useQuery(
    api.playback.getRoomLeader,
    roomId ? { roomId } : "skip"
  );

  // Mutations
  const updateUserPlayback = useMutation(api.playback.updateUserPlaybackState);
  const setFollowModeMutation = useMutation(api.playback.setFollowMode);
  const setRoomLeaderMutation = useMutation(api.playback.setRoomLeader);
  const syncRoomToLeaderMutation = useMutation(api.playback.syncRoomToLeader);

  // Local state
  const [currentTrackId, setCurrentTrackId] = useState<Id<"atoms"> | null>(null);
  const [isPlaying, setIsPlaying] = useState(false);
  const [position, setPosition] = useState(0);
  const [volume, setVolumeState] = useState(0.7);
  const [followMode, setFollowModeState] = useState<"NONE" | "ROOM" | "USER">("NONE");

  // Determine effective playback state based on follow mode
  useEffect(() => {
    if (!userPlaybackState) return;

    let effectiveState = userPlaybackState;

    // If following room, use room playback state
    if (userPlaybackState.followMode === "ROOM" && roomPlaybackState) {
      effectiveState = {
        ...userPlaybackState,
        currentTrackId: roomPlaybackState.currentTrackId,
        isPlaying: roomPlaybackState.isPlaying,
        position: roomPlaybackState.position,
        timestamp: roomPlaybackState.timestamp,
      };
    }

    setCurrentTrackId(effectiveState.currentTrackId || null);
    setIsPlaying(effectiveState.isPlaying);
    setFollowModeState(effectiveState.followMode);
    setVolumeState(effectiveState.volume);

    // Calculate current position using timestamp-based sync
    // This ensures playback starts at the correct position when page loads
    if (effectiveState.isPlaying) {
      const elapsed = Date.now() - effectiveState.timestamp;
      const calculatedPosition = effectiveState.position + elapsed;
      setPosition(calculatedPosition);

      // Set audio element position when track changes or on initial load
      if (audioRef.current && effectiveState.currentTrackId) {
        audioRef.current.currentTime = calculatedPosition / 1000; // Convert to seconds
      }
    } else {
      setPosition(effectiveState.position);

      // Set audio element position even when paused
      if (audioRef.current && effectiveState.currentTrackId) {
        audioRef.current.currentTime = effectiveState.position / 1000; // Convert to seconds
      }
    }
  }, [userPlaybackState, roomPlaybackState, audioRef]);

  // Update audio element when playback state changes
  useEffect(() => {
    if (!audioRef.current) return;

    if (isPlaying) {
      audioRef.current.play().catch(console.error);
    } else {
      audioRef.current.pause();
    }
  }, [isPlaying]);

  useEffect(() => {
    if (audioRef.current) {
      audioRef.current.volume = volume;
    }
  }, [volume]);

  // Playback control functions
  const play = async (atomId: Id<"atoms">) => {
    setCurrentTrackId(atomId);
    setIsPlaying(true);
    setPosition(0);

    // Only update backend state if we're in a room
    if (roomId) {
      await updateUserPlayback({
        roomId,
        currentTrackId: atomId,
        isPlaying: true,
        position: 0,
        volume,
      });

      // If user is room leader, sync room playback
      if (roomLeader && userPlaybackState?.userId === roomLeader._id) {
        await syncRoomToLeaderMutation({ roomId });
      }
    }
  };

  const pause = async () => {
    if (!audioRef.current) return;

    const currentPosition = audioRef.current.currentTime * 1000;
    setIsPlaying(false);

    // Only update backend state if we're in a room
    if (roomId) {
      await updateUserPlayback({
        roomId,
        currentTrackId: currentTrackId || undefined,
        isPlaying: false,
        position: currentPosition,
        volume,
      });

      // If user is room leader, sync room playback
      if (roomLeader && userPlaybackState?.userId === roomLeader._id) {
        await syncRoomToLeaderMutation({ roomId });
      }
    }
  };

  const resume = async () => {
    if (!audioRef.current) return;

    const currentPosition = audioRef.current.currentTime * 1000;
    setIsPlaying(true);

    // Only update backend state if we're in a room
    if (roomId) {
      await updateUserPlayback({
        roomId,
        currentTrackId: currentTrackId || undefined,
        isPlaying: true,
        position: currentPosition,
        volume,
      });

      // If user is room leader, sync room playback
      if (roomLeader && userPlaybackState?.userId === roomLeader._id) {
        await syncRoomToLeaderMutation({ roomId });
      }
    }
  };

  const seek = async (newPosition: number) => {
    setPosition(newPosition);
    if (audioRef.current) {
      audioRef.current.currentTime = newPosition / 1000;
    }

    // Only update backend state if we're in a room
    if (roomId) {
      await updateUserPlayback({
        roomId,
        currentTrackId: currentTrackId || undefined,
        isPlaying,
        position: newPosition,
        volume,
      });

      // If user is room leader, sync room playback
      if (roomLeader && userPlaybackState?.userId === roomLeader._id) {
        await syncRoomToLeaderMutation({ roomId });
      }
    }
  };

  const setVolume = async (newVolume: number) => {
    setVolumeState(newVolume);

    // Only update backend state if we're in a room
    if (roomId) {
      await updateUserPlayback({
        roomId,
        currentTrackId: currentTrackId || undefined,
        isPlaying,
        position,
        volume: newVolume,
      });
    }
  };

  const setFollowMode = async (mode: "NONE" | "ROOM" | "USER", userId?: Id<"users">) => {
    if (!roomId) return;

    setFollowModeState(mode);

    await setFollowModeMutation({
      roomId,
      followMode: mode,
      followingUserId: userId,
    });
  };

  const setRoomLeader = async (isLeader: boolean) => {
    if (!roomId) return;

    await setRoomLeaderMutation({
      roomId,
      isLeader,
    });

    // If becoming leader, sync room to current playback
    if (isLeader) {
      await syncRoomToLeaderMutation({ roomId });
    }
  };

  const isRoomLeader = roomLeader && userPlaybackState?.userId === roomLeader._id;

  return (
    <PlaybackContext.Provider
      value={{
        currentTrackId,
        isPlaying,
        position,
        volume,
        followMode,
        play,
        pause,
        resume,
        seek,
        setVolume,
        setFollowMode,
        isRoomLeader: isRoomLeader || false,
        setRoomLeader,
        roomLeader,
        audioRef,
      }}
    >
      {children}
      {/* Global audio element */}
      <audio ref={audioRef} />
    </PlaybackContext.Provider>
  );
}

export function usePlayback() {
  const context = useContext(PlaybackContext);
  if (context === undefined) {
    throw new Error("usePlayback must be used within a PlaybackProvider");
  }
  return context;
}
