import { createContext, useContext, useRef, useState, useCallback, useEffect } from "react";
import { getTrackStreamUrl } from "./api";

const MusicPlayerCtx = createContext(null);

export const MusicPlayerProvider = ({ children }) => {
  const audioRef = useRef(new Audio());
  const [queue, setQueue] = useState([]);
  const [index, setIndex] = useState(-1);
  const [isPlaying, setIsPlaying] = useState(false);
  const [progress, setProgress] = useState({ position: 0, duration: 0 });
  const [outputDevices, setOutputDevices] = useState([]);
  const [sinkId, setSinkId] = useState("default");
  const sinkSupported = typeof audioRef.current.setSinkId === "function";

  const current = index >= 0 ? queue[index] : null;

  const refreshOutputDevices = useCallback(async () => {
    if (!navigator.mediaDevices?.enumerateDevices) return;
    try {
      const devices = await navigator.mediaDevices.enumerateDevices();
      const outs = devices.filter((d) => d.kind === "audiooutput");
      setOutputDevices(outs.map((d, i) => ({ deviceId: d.deviceId, label: d.label || `Output ${i + 1}` })));
    } catch { /* enumeration unsupported/denied — picker just won't populate */ }
  }, []);

  const selectOutput = useCallback(async (deviceId) => {
    if (!sinkSupported) return;
    try {
      await audioRef.current.setSinkId(deviceId);
      setSinkId(deviceId);
    } catch { /* device may have disappeared (e.g. BT speaker turned off) — keep current output */ }
  }, [sinkSupported]);

  const playAt = useCallback((newQueue, startIndex) => {
    setQueue(newQueue);
    setIndex(startIndex);
  }, []);

  useEffect(() => {
    const audio = audioRef.current;
    if (!current) { audio.pause(); return; }
    audio.src = getTrackStreamUrl(current);
    audio.play().catch(() => {}); // isPlaying reflects reality via the native play/pause listeners below
  }, [current]);

  // isPlaying always mirrors the audio element's own state, so it stays correct through anything
  // that changes playback outside togglePlay — a stall, an OS media-key pause, autoplay being blocked.
  useEffect(() => {
    const audio = audioRef.current;
    const onTime = () => setProgress({ position: audio.currentTime, duration: audio.duration || 0 });
    const onEnded = () => {
      if (index >= 0 && index < queue.length - 1) setIndex(index + 1);
    };
    const onPlay = () => setIsPlaying(true);
    const onPause = () => setIsPlaying(false);
    audio.addEventListener("timeupdate", onTime);
    audio.addEventListener("ended", onEnded);
    audio.addEventListener("play", onPlay);
    audio.addEventListener("pause", onPause);
    return () => {
      audio.removeEventListener("timeupdate", onTime);
      audio.removeEventListener("ended", onEnded);
      audio.removeEventListener("play", onPlay);
      audio.removeEventListener("pause", onPause);
    };
  }, [index, queue]);

  const togglePlay = useCallback(() => {
    const audio = audioRef.current;
    if (!current) return;
    if (audio.paused) audio.play().catch(() => {});
    else audio.pause();
  }, [current]);

  const next = useCallback(() => {
    if (index >= 0 && index < queue.length - 1) setIndex(index + 1);
  }, [index, queue]);

  const prev = useCallback(() => {
    if (index > 0) setIndex(index - 1);
  }, [index]);

  const seek = useCallback((seconds) => {
    audioRef.current.currentTime = seconds;
  }, []);

  return (
    <MusicPlayerCtx.Provider value={{
      current, isPlaying, progress, playAt, togglePlay, next, prev, seek,
      hasNext: index < queue.length - 1, hasPrev: index > 0,
      outputDevices, sinkId, sinkSupported, refreshOutputDevices, selectOutput,
    }}>
      {children}
    </MusicPlayerCtx.Provider>
  );
};

export const useMusicPlayer = () => useContext(MusicPlayerCtx);
