import { createContext, useContext, useState, useRef, useCallback, useEffect, ReactNode } from 'react';
import { useAudioAnalyzer } from './AudioAnalyzerContext';

export interface Station {
  stationuuid: string;
  name: string;
  url: string;
  favicon: string;
  country: string;
  tags: string;
  bitrate: number;
}

interface MusicContextType {
  isPlaying: boolean;
  isBuffering: boolean;
  volume: number;
  stations: Station[];
  currentIndex: number;
  selectedStation: Station | null;
  hasFetched: boolean;
  setVolume: (volume: number) => void;
  playStation: (station: Station, index: number) => void;
  togglePlay: () => void;
  playNext: () => void;
  playPrevious: () => void;
  fetchStations: () => Promise<void>;
  stopAudio: () => void;
}

const MusicContext = createContext<MusicContextType | undefined>(undefined);

export const MusicProvider = ({ children }: { children: ReactNode }) => {
  const [isPlaying, setIsPlaying] = useState(false);
  const [isBuffering, setIsBuffering] = useState(false);
  const [volume, setVolumeState] = useState(50);
  const [stations, setStations] = useState<Station[]>([]);
  const [currentIndex, setCurrentIndex] = useState(0);
  const [selectedStation, setSelectedStation] = useState<Station | null>(null);
  const [hasFetched, setHasFetched] = useState(false);
  const [failedStations, setFailedStations] = useState<Set<string>>(new Set());
  const audioRef = useRef<HTMLAudioElement | null>(null);
  const { connectAudioElement, disconnectAudioElement } = useAudioAnalyzer();

  // Update volume on audio element when volume state changes
  useEffect(() => {
    if (audioRef.current) {
      audioRef.current.volume = volume / 100;
    }
  }, [volume]);

  const setVolume = useCallback((newVolume: number) => {
    setVolumeState(newVolume);
  }, []);

  const fetchStations = useCallback(async () => {
    if (hasFetched) return;
    
    // Multiple radio-browser API servers for fallback
    const apiServers = [
      'https://de1.api.radio-browser.info',
      'https://de2.api.radio-browser.info',
      'https://nl1.api.radio-browser.info',
      'https://nl2.api.radio-browser.info',
      'https://at1.api.radio-browser.info',
      'https://fr1.api.radio-browser.info',
      'https://all.api.radio-browser.info',
    ];
    
    for (const server of apiServers) {
      try {
        const controller = new AbortController();
        const timeoutId = setTimeout(() => controller.abort(), 5000);
        
        const response = await fetch(
          `${server}/json/stations/topclick/100`,
          { signal: controller.signal }
        );
        clearTimeout(timeoutId);
        
        if (!response.ok) continue;
        
        const data: Station[] = await response.json();
        const validStations = data.filter(s => !failedStations.has(s.stationuuid));
        
        if (validStations.length > 0) {
          setStations(validStations);
          setHasFetched(true);
          console.log(`Fetched stations from ${server}`);
          return;
        }
      } catch (err) {
        console.warn(`Failed to fetch from ${server}:`, err);
        continue;
      }
    }
    
    console.error('All radio API servers failed');
  }, [hasFetched, failedStations]);

  const stopCurrentAudio = useCallback(() => {
    if (audioRef.current) {
      disconnectAudioElement(audioRef.current);
      audioRef.current.pause();
      audioRef.current.src = '';
      audioRef.current.onplay = null;
      audioRef.current.onpause = null;
      audioRef.current.onerror = null;
      audioRef.current.onwaiting = null;
      audioRef.current.onplaying = null;
      audioRef.current = null;
    }
    setIsBuffering(false);
  }, [disconnectAudioElement]);

  const playStation = useCallback((station: Station, index: number) => {
    stopCurrentAudio();
    setIsBuffering(true);
    
    const audio = new Audio(station.url);
    audio.crossOrigin = 'anonymous';
    audio.volume = volume / 100;
    audioRef.current = audio;
    
    // Connect to analyzer for visualization
    connectAudioElement(audio);
    
    audio.onerror = () => {
      console.error('Failed to play station:', station.name);
      setFailedStations(prev => new Set(prev).add(station.stationuuid));
      setIsPlaying(false);
      setIsBuffering(false);
      setSelectedStation(null);
    };
    
    audio.onwaiting = () => {
      setIsBuffering(true);
    };
    
    audio.onplaying = () => {
      setIsBuffering(false);
      setIsPlaying(true);
      // Set flag for music listener achievement (checked in AchievementsContext)
      localStorage.setItem('has-listened-to-music', 'true');
    };
    
    audio.onplay = () => {
      setIsPlaying(true);
    };
    
    audio.onpause = () => {
      if (audioRef.current === audio) {
        setIsPlaying(false);
      }
    };
    
    audio.play().catch((err) => {
      console.error('Playback error:', err);
      setFailedStations(prev => new Set(prev).add(station.stationuuid));
      setIsPlaying(false);
      setIsBuffering(false);
    });
    
    setSelectedStation(station);
    setCurrentIndex(index);
  }, [volume, stopCurrentAudio, connectAudioElement]);

  const togglePlay = useCallback(() => {
    if (!audioRef.current || !selectedStation) {
      if (stations.length > 0) {
        playStation(stations[0], 0);
      }
      return;
    }
    
    if (isPlaying) {
      audioRef.current.pause();
    } else {
      setIsBuffering(true);
      audioRef.current.play().catch(() => {
        setIsBuffering(false);
      });
    }
  }, [selectedStation, stations, playStation, isPlaying]);

  const playNext = useCallback(() => {
    if (stations.length === 0) return;
    const nextIndex = (currentIndex + 1) % stations.length;
    playStation(stations[nextIndex], nextIndex);
  }, [currentIndex, stations, playStation]);

  const playPrevious = useCallback(() => {
    if (stations.length === 0) return;
    const prevIndex = currentIndex === 0 ? stations.length - 1 : currentIndex - 1;
    playStation(stations[prevIndex], prevIndex);
  }, [currentIndex, stations, playStation]);

  // Handle media keys
  useEffect(() => {
    const handleMediaKey = (e: KeyboardEvent) => {
      if (e.key === 'MediaPlayPause') {
        e.preventDefault();
        togglePlay();
      } else if (e.key === 'MediaTrackNext') {
        e.preventDefault();
        playNext();
      } else if (e.key === 'MediaTrackPrevious') {
        e.preventDefault();
        playPrevious();
      }
    };

    window.addEventListener('keydown', handleMediaKey);
    return () => window.removeEventListener('keydown', handleMediaKey);
  }, [togglePlay, playNext, playPrevious]);

  return (
    <MusicContext.Provider
      value={{
        isPlaying,
        isBuffering,
        volume,
        stations,
        currentIndex,
        selectedStation,
        hasFetched,
        setVolume,
        playStation,
        togglePlay,
        playNext,
        playPrevious,
        fetchStations,
        stopAudio: stopCurrentAudio,
      }}
    >
      {children}
    </MusicContext.Provider>
  );
};

export const useMusic = () => {
  const context = useContext(MusicContext);
  if (context === undefined) {
    throw new Error('useMusic must be used within a MusicProvider');
  }
  return context;
};
