import { createContext, useCallback, useEffect, useState, PropsWithChildren } from 'react';
import { SpotifyAuth } from '@/lib/spotify/auth';
import { useAuth } from './auth-context';
import type {
  SpotifyContextType,
  SpotifyAuthState,
  SpotifyDevice,
  SpotifyPlaybackState,
  SpotifyTrack,
} from '@/lib/spotify/types';

const defaultSpotifyState: SpotifyAuthState = {
  isAuthenticated: false,
  accessToken: null,
  refreshToken: null,
  expiresAt: null,
  scopes: [],
};

export const SpotifyContext = createContext<SpotifyContextType | null>(null);

export const SpotifyProvider: React.FC<PropsWithChildren> = ({ children }) => {
  const { roles } = useAuth();
  const isAdmin = roles?.includes('Admin') || false;
  
  const [authState, setAuthState] = useState<SpotifyAuthState>(defaultSpotifyState);
  
  const [currentTrack, setCurrentTrack] = useState<SpotifyTrack | null>(null);
  const [playbackState, setPlaybackState] = useState<SpotifyPlaybackState | null>(null);
  
  const [devices, setDevices] = useState<SpotifyDevice[]>([]);
  const [activeDevice, setActiveDeviceState] = useState<SpotifyDevice | null>(null);
  
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const spotifyAuth = new SpotifyAuth(
    import.meta.env.VITE_SPOTIFY_CLIENT_ID!,
    import.meta.env.VITE_SPOTIFY_REDIRECT_URI!
  );

  useEffect(() => {
    if (isAdmin) {
      checkExistingAuth();
      handleOAuthCallback();
    }
  }, [isAdmin]);

  const checkExistingAuth = async () => {
    try {
      const response = await fetch('/api/spotify/token', {
        credentials: 'include',
      });
      
      if (response.ok) {
        const data = await response.json();
        if (data.access_token) {
          setAuthState({
            isAuthenticated: true,
            accessToken: data.access_token,
            refreshToken: data.refresh_token,
            expiresAt: Date.now() + (3600 * 1000),
            scopes: [],
          });
          
          // Load initial data
          await Promise.all([getDevices(), refreshPlaybackState()]);
        }
      }
    } catch (error) {
      console.error('Failed to check existing auth:', error);
    }
  };

  const handleOAuthCallback = () => {
    const urlParams = new URLSearchParams(window.location.search);
    const spotifyAuth = urlParams.get('spotify_auth');
    const error = urlParams.get('spotify_error');
    const details = urlParams.get('details');

    if (spotifyAuth === 'success') {
      checkExistingAuth();
      
      const newUrl = new URL(window.location.href);
      newUrl.searchParams.delete('spotify_auth');
      newUrl.searchParams.delete('state');
      window.history.replaceState({}, '', newUrl.toString());
    }

    if (error) {
      let errorMessage = `Authentication failed: ${error}`;
      if (details) {
        errorMessage += ` - ${decodeURIComponent(details)}`;
      }
      setError(errorMessage);
      
      console.error('Spotify OAuth Error:', { error, details });
      
      const newUrl = new URL(window.location.href);
      newUrl.searchParams.delete('spotify_error');
      newUrl.searchParams.delete('details');
      window.history.replaceState({}, '', newUrl.toString());
    }
  };

  const login = useCallback(() => {
    if (!isAdmin) return;
    spotifyAuth.startAuthFlow(window.location.pathname);
  }, [isAdmin, spotifyAuth]);

  const logout = useCallback(() => {
    setAuthState(defaultSpotifyState);
    setCurrentTrack(null);
    setPlaybackState(null);
    setDevices([]);
    setActiveDeviceState(null);
    setError(null);
    
    document.cookie = 'spotify_access_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
    document.cookie = 'spotify_refresh_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
  }, []);

  const makeSpotifyRequest = async (endpoint: string, options: RequestInit = {}) => {
    const response = await fetch(`/api/spotify/${endpoint}`, {
      ...options,
      credentials: 'include',
      headers: {
        'Content-Type': 'application/json',
        ...options.headers,
      },
    });

    if (!response.ok) {
      let errorMessage = 'Request failed';
      try {
        const errorData = await response.json();
        errorMessage = errorData.error || errorMessage;
      } catch {
        errorMessage = `HTTP ${response.status}: ${response.statusText}`;
      }
      throw new Error(errorMessage);
    }

    if (response.status === 204 || response.headers.get('content-length') === '0') {
      return {};
    }

    const contentType = response.headers.get('content-type') || '';
    if (!contentType.includes('application/json')) {
      console.warn(`Non-JSON response from ${endpoint}:`, contentType);
      return {};
    }

    try {
      return await response.json();
    } catch (error) {
      console.warn(`Failed to parse JSON response from ${endpoint}:`, error);
      return {};
    }
  };

  const play = useCallback(async (deviceId?: string) => {
    if (!authState.isAuthenticated) return;
    
    setIsLoading(true);
    setError(null);
    
    try {
      await makeSpotifyRequest('playback', {
        method: 'POST',
        body: JSON.stringify({ action: 'play', deviceId }),
      });
      
      setTimeout(refreshPlaybackState, 500);
    } catch (error) {
      if (error instanceof Error && !error.message.includes('JSON')) {
        setError(error.message);
      }
      console.warn('Playback action completed with warning:', error);
    } finally {
      setIsLoading(false);
    }
  }, [authState.isAuthenticated]);

  const pause = useCallback(async () => {
    if (!authState.isAuthenticated) return;
    
    setIsLoading(true);
    setError(null);
    
    try {
      await makeSpotifyRequest('playback', {
        method: 'POST',
        body: JSON.stringify({ action: 'pause' }),
      });
      
      setTimeout(refreshPlaybackState, 500);
    } catch (error) {
      if (error instanceof Error && !error.message.includes('JSON')) {
        setError(error.message);
      }
      console.warn('Playback action completed with warning:', error);
    } finally {
      setIsLoading(false);
    }
  }, [authState.isAuthenticated]);

  const skipNext = useCallback(async () => {
    if (!authState.isAuthenticated) return;
    
    setIsLoading(true);
    setError(null);
    
    try {
      await makeSpotifyRequest('playback', {
        method: 'POST',
        body: JSON.stringify({ action: 'next' }),
      });
      
      setTimeout(refreshPlaybackState, 500);
    } catch (error) {
      if (error instanceof Error && !error.message.includes('JSON')) {
        setError(error.message);
      }
      console.warn('Playback action completed with warning:', error);
    } finally {
      setIsLoading(false);
    }
  }, [authState.isAuthenticated]);

  const skipPrevious = useCallback(async () => {
    if (!authState.isAuthenticated) return;
    
    setIsLoading(true);
    setError(null);
    
    try {
      await makeSpotifyRequest('playback', {
        method: 'POST',
        body: JSON.stringify({ action: 'previous' }),
      });
      
      setTimeout(refreshPlaybackState, 500);
    } catch (error) {
      if (error instanceof Error && !error.message.includes('JSON')) {
        setError(error.message);
      }
      console.warn('Playback action completed with warning:', error);
    } finally {
      setIsLoading(false);
    }
  }, [authState.isAuthenticated]);

  const setVolume = useCallback(async (volumePercent: number) => {
    if (!authState.isAuthenticated) return;
    
    setIsLoading(true);
    setError(null);
    
    try {
      await makeSpotifyRequest('playback', {
        method: 'POST',
        body: JSON.stringify({ action: 'volume', volumePercent }),
      });
    } catch (error) {
      setError(error instanceof Error ? error.message : 'Failed to set volume');
    } finally {
      setIsLoading(false);
    }
  }, [authState.isAuthenticated]);

  const getDevices = useCallback(async () => {
    if (!authState.isAuthenticated) return;
    
    setIsLoading(true);
    setError(null);
    
    try {
      const data = await makeSpotifyRequest('playback?type=devices');
      setDevices(data.devices || []);
      
      const active = data.devices?.find((d: SpotifyDevice) => d.is_active);
      if (active) {
        setActiveDeviceState(active);
      }
    } catch (error) {
      setError(error instanceof Error ? error.message : 'Failed to get devices');
    } finally {
      setIsLoading(false);
    }
  }, [authState.isAuthenticated]);

  const setActiveDevice = useCallback(async (deviceId: string) => {
    if (!authState.isAuthenticated) return;
    
    setIsLoading(true);
    setError(null);
    
    try {
      await makeSpotifyRequest('playback', {
        method: 'POST',
        body: JSON.stringify({ action: 'transfer', deviceId }),
      });
      
      const device = devices.find(d => d.id === deviceId);
      if (device) {
        setActiveDeviceState(device);
      }
      
      setTimeout(getDevices, 1000);
    } catch (error) {
      setError(error instanceof Error ? error.message : 'Failed to set active device');
    } finally {
      setIsLoading(false);
    }
  }, [authState.isAuthenticated, devices]);

  const refreshPlaybackState = useCallback(async () => {
    if (!authState.isAuthenticated) return;
    
    try {
      const data = await makeSpotifyRequest('playback?type=state');
      const state = data.playbackState;
      
      setPlaybackState(state);
      setCurrentTrack(state?.item || null);
      
      if (state?.device) {
        setActiveDeviceState(state.device);
      }
    } catch (error) {
      console.warn('Failed to refresh playback state:', error);
    }
  }, [authState.isAuthenticated]);

  useEffect(() => {
    if (!authState.isAuthenticated) return;

    const interval = setInterval(refreshPlaybackState, 5000);
    return () => clearInterval(interval);
  }, [authState.isAuthenticated, refreshPlaybackState]);

  const contextValue: SpotifyContextType = {
    ...authState,
    currentTrack,
    playbackState,
    devices,
    activeDevice,
    isLoading,
    error,
    login,
    logout,
    play,
    pause,
    skipNext,
    skipPrevious,
    setVolume,
    getDevices,
    setActiveDevice,
    refreshPlaybackState,
  };

  if (!isAdmin) {
    return <>{children}</>;
  }

  return (
    <SpotifyContext.Provider value={contextValue}>
      {children}
    </SpotifyContext.Provider>
  );
};