"use client";

import React, { createContext, useContext, useRef, useState, useEffect, useCallback } from "react";
import { Button } from "@/components/ui/button";
import { Slider } from "@/components/ui/slider";
import { Play, Pause, Loader2 } from "lucide-react";
import { cn } from "@/lib/utils";

interface AudioPlayerItem<TData = unknown> {
  id: string | number;
  src: string;
  data?: TData;
}

interface AudioPlayerContextType<TData = unknown> {
  ref: React.RefObject<HTMLAudioElement | null>;
  activeItem: AudioPlayerItem<TData> | null;
  duration: number;
  error: MediaError | null;
  isPlaying: boolean;
  isBuffering: boolean;
  playbackRate: number;
  isItemActive: (item: AudioPlayerItem<TData>) => boolean;
  setActiveItem: (item: AudioPlayerItem<TData>) => void;
  play: (item?: AudioPlayerItem<TData>) => void;
  pause: () => void;
  seek: (time: number) => void;
  setPlaybackRate: (rate: number) => void;
}

const AudioPlayerContext = createContext<AudioPlayerContextType | null>(null);

export function AudioPlayerProvider({ children }: { children: React.ReactNode }) {
  const audioRef = useRef<HTMLAudioElement>(null);
  const [activeItem, setActiveItem] = useState<AudioPlayerItem | null>(null);
  const [duration, setDuration] = useState(0);
  const [error, setError] = useState<MediaError | null>(null);
  const [isPlaying, setIsPlaying] = useState(false);
  const [isBuffering, setIsBuffering] = useState(false);
  const [playbackRate, setPlaybackRate] = useState(1);

  const isItemActive = useCallback((item: AudioPlayerItem) => {
    return activeItem?.id === item.id;
  }, [activeItem]);

  const play = useCallback((item?: AudioPlayerItem) => {
    if (!audioRef.current) return;

    if (item && !isItemActive(item)) {
      setActiveItem(item);
      audioRef.current.src = item.src;
      audioRef.current.load();
    }

    audioRef.current.play().catch((err: unknown) => {
      console.error("Error playing audio:", err);
      setError(err instanceof MediaError ? err : null);
    });
  }, [isItemActive]);

  const pause = useCallback(() => {
    if (audioRef.current) {
      audioRef.current.pause();
    }
  }, []);

  const seek = useCallback((time: number) => {
    if (audioRef.current) {
      audioRef.current.currentTime = time;
    }
  }, []);

  const handleSetPlaybackRate = useCallback((rate: number) => {
    if (audioRef.current) {
      audioRef.current.playbackRate = rate;
      setPlaybackRate(rate);
    }
  }, []);

  useEffect(() => {
    const audio = audioRef.current;
    if (!audio) return;

    const handleLoadedMetadata = () => {
      setDuration(audio.duration);
      setIsBuffering(false);
    };

    const handlePlay = () => setIsPlaying(true);
    const handlePause = () => setIsPlaying(false);
    const handleWaiting = () => setIsBuffering(true);
    const handleCanPlay = () => setIsBuffering(false);
    const handleError = () => setError(audio.error);

    audio.addEventListener("loadedmetadata", handleLoadedMetadata);
    audio.addEventListener("play", handlePlay);
    audio.addEventListener("pause", handlePause);
    audio.addEventListener("waiting", handleWaiting);
    audio.addEventListener("canplay", handleCanPlay);
    audio.addEventListener("error", handleError);

    return () => {
      audio.removeEventListener("loadedmetadata", handleLoadedMetadata);
      audio.removeEventListener("play", handlePlay);
      audio.removeEventListener("pause", handlePause);
      audio.removeEventListener("waiting", handleWaiting);
      audio.removeEventListener("canplay", handleCanPlay);
      audio.removeEventListener("error", handleError);
    };
  }, []);

  const value: AudioPlayerContextType = {
    ref: audioRef,
    activeItem,
    duration,
    error,
    isPlaying,
    isBuffering,
    playbackRate,
    isItemActive,
    setActiveItem,
    play,
    pause,
    seek,
    setPlaybackRate: handleSetPlaybackRate,
  };

  return (
    <AudioPlayerContext.Provider value={value}>
      <audio ref={audioRef} preload="metadata" />
      {children}
    </AudioPlayerContext.Provider>
  );
}

export function useAudioPlayer<TData = unknown>(): AudioPlayerContextType<TData> {
  const context = useContext(AudioPlayerContext);
  if (!context) {
    throw new Error("useAudioPlayer must be used within AudioPlayerProvider");
  }
  return context as AudioPlayerContextType<TData>;
}

export function useAudioPlayerTime(): number {
  const { ref } = useAudioPlayer();
  const [time, setTime] = useState(0);

  useEffect(() => {
    const audio = ref.current;
    if (!audio) return;

    let animationFrame: number;

    const updateTime = () => {
      setTime(audio.currentTime);
      animationFrame = requestAnimationFrame(updateTime);
    };

    const handlePlay = () => {
      updateTime();
    };

    const handlePause = () => {
      if (animationFrame) {
        cancelAnimationFrame(animationFrame);
      }
    };

    audio.addEventListener("play", handlePlay);
    audio.addEventListener("pause", handlePause);

    return () => {
      if (animationFrame) {
        cancelAnimationFrame(animationFrame);
      }
      audio.removeEventListener("play", handlePlay);
      audio.removeEventListener("pause", handlePause);
    };
  }, [ref]);

  return time;
}

interface AudioPlayerButtonProps extends React.ComponentProps<typeof Button> {
  item?: AudioPlayerItem;
}

export function AudioPlayerButton({ item, className, ...props }: AudioPlayerButtonProps) {
  const { play, pause, isPlaying, isBuffering, activeItem } = useAudioPlayer();

  const handleClick = () => {
    if (item) {
      if (activeItem?.id === item.id && isPlaying) {
        pause();
      } else {
        play(item);
      }
    } else {
      if (isPlaying) {
        pause();
      } else if (activeItem) {
        play();
      }
    }
  };

  const isActive = item ? activeItem?.id === item.id : !!activeItem;
  const showPlaying = isActive && isPlaying;
  const showBuffering = isActive && isBuffering;

  return (
    <Button
      variant="outline"
      size="icon"
      onClick={handleClick}
      className={cn("h-10 w-10", className)}
      disabled={showBuffering}
      {...props}
    >
      {showBuffering ? (
        <Loader2 className="h-4 w-4 animate-spin" />
      ) : showPlaying ? (
        <Pause className="h-4 w-4" />
      ) : (
        <Play className="h-4 w-4" />
      )}
    </Button>
  );
}

interface AudioPlayerProgressProps extends React.ComponentProps<typeof Slider> {
  className?: string;
}

export function AudioPlayerProgress({ className, ...props }: AudioPlayerProgressProps) {
  const { duration, seek } = useAudioPlayer();
  const time = useAudioPlayerTime();
  const [isSeeking, setIsSeeking] = useState(false);
  const [seekValue, setSeekValue] = useState([0]);

  const handleValueChange = (value: number[]) => {
    setSeekValue(value);
    setIsSeeking(true);
  };

  const handleValueCommit = (value: number[]) => {
    seek(value[0]);
    setIsSeeking(false);
  };

  const currentValue = isSeeking ? seekValue : [time];

  return (
    <Slider
      value={currentValue}
      onValueChange={handleValueChange}
      onValueCommit={handleValueCommit}
      max={duration || 100}
      step={0.1}
      className={cn("w-full", className)}
      {...props}
    />
  );
}

type AudioPlayerTimeProps = React.HTMLAttributes<HTMLSpanElement>;

export function AudioPlayerTime({ className, ...props }: AudioPlayerTimeProps) {
  const time = useAudioPlayerTime();

  const formatTime = (seconds: number) => {
    const mins = Math.floor(seconds / 60);
    const secs = Math.floor(seconds % 60);
    return `${mins}:${secs.toString().padStart(2, "0")}`;
  };

  return (
    <span className={cn("text-sm font-mono", className)} {...props}>
      {formatTime(time)}
    </span>
  );
}

type AudioPlayerDurationProps = React.HTMLAttributes<HTMLSpanElement>;

export function AudioPlayerDuration({ className, ...props }: AudioPlayerDurationProps) {
  const { duration } = useAudioPlayer();

  const formatTime = (seconds: number) => {
    if (!seconds || !isFinite(seconds)) return "--:--";
    const mins = Math.floor(seconds / 60);
    const secs = Math.floor(seconds % 60);
    return `${mins}:${secs.toString().padStart(2, "0")}`;
  };

  return (
    <span className={cn("text-sm font-mono", className)} {...props}>
      {formatTime(duration)}
    </span>
  );
}