import {
  PropsWithChildren,
  createContext,
  useCallback,
  useContext,
  useEffect,
  useRef,
  useState,
} from "react";
import { AVPlaybackSource, AVPlaybackStatus, Audio } from "expo-av";

type AudioContextType = {
  loadAudio: (source?: AVPlaybackSource) => Promise<void>;
  togglePlay: () => Promise<void>;
  isPlaying: boolean;
  duration: number;
  position: number;
  onValueChange: (value: number) => void;
  onUnmount: () => void;
};

const AudioContext = createContext({
  loadAudio: async (source?: AVPlaybackSource) => {},
  togglePlay: async () => {},
  isPlaying: false,
  duration: 0,
  position: 0,
  onValueChange: (value: number) => {},
  onUnmount: () => {},
} as AudioContextType);

type AudioContextProviderProps = PropsWithChildren<{}>;

export const AudioContextProvider = ({
  children,
}: AudioContextProviderProps) => {
  const soundRef = useRef<Audio.Sound | null>(null);
  const [isPlaying, setIsPlaying] = useState(false);
  const [duration, setDuration] = useState(0);
  const [position, setPosition] = useState(0);

  const onPlaybackStatusUpdate = (status: AVPlaybackStatus) => {
    status.isLoaded && setPosition(status.positionMillis);
  };

  const onValueChange = (value: number) => {
    if (soundRef.current) {
      soundRef.current.setPositionAsync(value);
    }
  };

  const loadAudio = async (source?: AVPlaybackSource) => {
    if (!source) return;
    const { sound, status } = await Audio.Sound.createAsync(source, {
      shouldPlay: false,
    });
    if (status.isLoaded) {
      sound.setOnPlaybackStatusUpdate(onPlaybackStatusUpdate);
      setDuration(status.durationMillis || 0);
      soundRef.current = sound;
      setIsPlaying(status.isPlaying);
    }
  };

  const togglePlay = async () => {
    if (soundRef.current) {
      if (isPlaying) {
        await soundRef.current.pauseAsync();
      } else {
        await soundRef.current.playAsync();
      }
      setIsPlaying(!isPlaying);
    }
  };

  const onUnmount = () => {
    if (soundRef.current) {
      soundRef.current.unloadAsync();
    }
  };

  return (
    <AudioContext.Provider
      value={{
        loadAudio,
        togglePlay,
        isPlaying,
        duration,
        position,
        onValueChange,
        onUnmount,
      }}
    >
      {children}
    </AudioContext.Provider>
  );
};

export const useAudio = () => {
  const context = useContext(AudioContext);
  if (!context) {
    throw new Error("useAudio must be used within an AudioContextProvider");
  }
  return context;
};
