"use client";

import { createContext, useContext, useCallback, useRef, useState, useEffect } from "react";
import type { SanityImageSource } from "@sanity/image-url";

export type PlayerTrack = {
  previewMp3Url: string;
  title: string;
  /** Movement name, displayed in regular weight after the bold work title. */
  movement?: string;
  subtitle: string;
  albumCover?: SanityImageSource;
  /** Full track duration in seconds (from tracklist metadata). */
  trackDuration?: number;
  /** Release slug for linking back to the release page. */
  releaseSlug?: string;
};

type PlayerState = {
  playlist: PlayerTrack[];
  currentIndex: number | null;
  isPlaying: boolean;
  currentTime: number;
  audioDuration: number;
};

type PlayerActions = {
  loadPlaylist: (tracks: PlayerTrack[], startIndex: number) => void;
  togglePlayback: () => void;
  next: () => void;
  previous: () => void;
  seekTo: (fraction: number) => void;
  stop: () => void;
};

type PlayerContextValue = PlayerState & PlayerActions;

const PlayerContext = createContext<PlayerContextValue | null>(null);

export function usePlayer() {
  const ctx = useContext(PlayerContext);
  if (!ctx) throw new Error("usePlayer must be used within a PlayerProvider");
  return ctx;
}

export function usePlayerOptional() {
  return useContext(PlayerContext);
}

export function PlayerProvider({ children }: { children: React.ReactNode }) {
  const audioRef = useRef<HTMLAudioElement | null>(null);
  const [playlist, setPlaylist] = useState<PlayerTrack[]>([]);
  const [currentIndex, setCurrentIndex] = useState<number | null>(null);
  const [isPlaying, setIsPlaying] = useState(false);
  const [currentTime, setCurrentTime] = useState(0);
  const [audioDuration, setAudioDuration] = useState(0);

  // Create audio element once
  useEffect(() => {
    const audio = new Audio();
    audioRef.current = audio;

    let rafId: number | null = null;

    // Poll currentTime at ~60fps for a silky-smooth progress bar
    const tick = () => {
      setCurrentTime(audio.currentTime);
      rafId = requestAnimationFrame(tick);
    };

    const onDurationChange = () => setAudioDuration(audio.duration || 0);
    const onPlay = () => {
      setIsPlaying(true);
      if (rafId === null) rafId = requestAnimationFrame(tick);
    };
    const onPause = () => {
      setIsPlaying(false);
      if (rafId !== null) { cancelAnimationFrame(rafId); rafId = null; }
    };

    audio.addEventListener("durationchange", onDurationChange);
    audio.addEventListener("play", onPlay);
    audio.addEventListener("pause", onPause);

    return () => {
      if (rafId !== null) cancelAnimationFrame(rafId);
      audio.removeEventListener("durationchange", onDurationChange);
      audio.removeEventListener("play", onPlay);
      audio.removeEventListener("pause", onPause);
      audio.pause();
      audio.src = "";
    };
  }, []);

  // Handle track ended → auto-advance
  useEffect(() => {
    const audio = audioRef.current;
    if (!audio) return;

    const onEnded = () => {
      if (currentIndex !== null && currentIndex < playlist.length - 1) {
        const nextIdx = currentIndex + 1;
        setCurrentIndex(nextIdx);
        audio.src = playlist[nextIdx].previewMp3Url;
        audio.play().catch(() => {});
      } else {
        audio.pause();
        audio.src = "";
        setPlaylist([]);
        setCurrentIndex(null);
        setCurrentTime(0);
        setAudioDuration(0);
        setIsPlaying(false);
      }
    };

    audio.addEventListener("ended", onEnded);
    return () => audio.removeEventListener("ended", onEnded);
  }, [currentIndex, playlist]);

  const loadPlaylist = useCallback((tracks: PlayerTrack[], startIndex: number) => {
    const audio = audioRef.current;
    if (!audio) return;

    setPlaylist(tracks);
    setCurrentIndex(startIndex);
    setCurrentTime(0);
    setAudioDuration(0);

    audio.src = tracks[startIndex].previewMp3Url;
    audio.play().catch(() => {});
  }, []);

  const togglePlayback = useCallback(() => {
    const audio = audioRef.current;
    if (!audio || currentIndex === null) return;

    if (audio.paused) {
      audio.play().catch(() => {});
    } else {
      audio.pause();
    }
  }, [currentIndex]);

  const next = useCallback(() => {
    const audio = audioRef.current;
    if (!audio || currentIndex === null) return;

    if (currentIndex < playlist.length - 1) {
      const nextIdx = currentIndex + 1;
      setCurrentIndex(nextIdx);
      setCurrentTime(0);
      audio.src = playlist[nextIdx].previewMp3Url;
      audio.play().catch(() => {});
    }
  }, [currentIndex, playlist]);

  const previous = useCallback(() => {
    const audio = audioRef.current;
    if (!audio || currentIndex === null) return;

    if (currentIndex > 0) {
      const prevIdx = currentIndex - 1;
      setCurrentIndex(prevIdx);
      setCurrentTime(0);
      audio.src = playlist[prevIdx].previewMp3Url;
      audio.play().catch(() => {});
    }
  }, [currentIndex, playlist]);

  const seekTo = useCallback((fraction: number) => {
    const audio = audioRef.current;
    if (!audio || !audio.duration) return;
    audio.currentTime = fraction * audio.duration;
  }, []);

  const stop = useCallback(() => {
    const audio = audioRef.current;
    if (!audio) return;
    audio.pause();
    audio.src = "";
    setPlaylist([]);
    setCurrentIndex(null);
    setCurrentTime(0);
    setAudioDuration(0);
    setIsPlaying(false);
  }, []);

  return (
    <PlayerContext.Provider
      value={{
        playlist,
        currentIndex,
        isPlaying,
        currentTime,
        audioDuration,
        loadPlaylist,
        togglePlayback,
        next,
        previous,
        seekTo,
        stop,
      }}
    >
      {children}
    </PlayerContext.Provider>
  );
}
