'use client';

import { useState, useEffect, useCallback } from 'react';
import { offlineLibraryManager, type OfflineLibraryStats, type SyncOperation } from '@/lib/offline-library';
import { Album, Artist, Song, Playlist } from '@/lib/navidrome';
import { useNavidrome } from '@/app/components/NavidromeContext';

export interface OfflineLibraryState {
  isInitialized: boolean;
  isOnline: boolean;
  isSyncing: boolean;
  lastSync: Date | null;
  stats: OfflineLibraryStats;
  syncProgress: {
    current: number;
    total: number;
    stage: string;
  } | null;
}

export function useOfflineLibrary() {
  const [state, setState] = useState<OfflineLibraryState>({
    isInitialized: false,
    isOnline: navigator.onLine,
    isSyncing: false,
    lastSync: null,
    stats: {
      albums: 0,
      artists: 0,
      songs: 0,
      playlists: 0,
      lastSync: null,
      pendingOperations: 0,
      storageSize: 0
    },
    syncProgress: null
  });

  const { api } = useNavidrome();

  // Initialize offline library
  useEffect(() => {
    const initializeOfflineLibrary = async () => {
      try {
        const initialized = await offlineLibraryManager.initialize();
        if (initialized) {
          const stats = await offlineLibraryManager.getLibraryStats();
          setState(prev => ({
            ...prev,
            isInitialized: true,
            stats,
            lastSync: stats.lastSync
          }));
        }
      } catch (error) {
        console.error('Failed to initialize offline library:', error);
      }
    };

    initializeOfflineLibrary();
  }, []);

  // Listen for online/offline events
  useEffect(() => {
    const handleOnline = () => {
      setState(prev => ({ ...prev, isOnline: true }));
      // Automatically sync when back online
      if (state.isInitialized && api) {
        syncPendingOperations();
      }
    };

    const handleOffline = () => {
      setState(prev => ({ ...prev, isOnline: false }));
    };

    window.addEventListener('online', handleOnline);
    window.addEventListener('offline', handleOffline);

    return () => {
      window.removeEventListener('online', handleOnline);
      window.removeEventListener('offline', handleOffline);
    };
  }, [state.isInitialized, api]);

  // Full library sync from server
  const syncLibraryFromServer = useCallback(async (): Promise<void> => {
    if (!api || !state.isInitialized || state.isSyncing) return;

    try {
      setState(prev => ({ 
        ...prev, 
        isSyncing: true,
        syncProgress: { current: 0, total: 100, stage: 'Starting sync...' }
      }));

      setState(prev => ({
        ...prev,
        syncProgress: { current: 20, total: 100, stage: 'Syncing albums...' }
      }));

      await offlineLibraryManager.syncFromServer(api);

      setState(prev => ({
        ...prev,
        syncProgress: { current: 80, total: 100, stage: 'Syncing pending operations...' }
      }));

      await offlineLibraryManager.syncPendingOperations(api);

      const stats = await offlineLibraryManager.getLibraryStats();
      
      setState(prev => ({
        ...prev,
        isSyncing: false,
        syncProgress: null,
        stats,
        lastSync: stats.lastSync
      }));

    } catch (error) {
      console.error('Library sync failed:', error);
      setState(prev => ({
        ...prev,
        isSyncing: false,
        syncProgress: null
      }));
      throw error;
    }
  }, [api, state.isInitialized, state.isSyncing]);

  // Sync only pending operations
  const syncPendingOperations = useCallback(async (): Promise<void> => {
    if (!api || !state.isInitialized) return;

    try {
      await offlineLibraryManager.syncPendingOperations(api);
      const stats = await offlineLibraryManager.getLibraryStats();
      setState(prev => ({ ...prev, stats }));
    } catch (error) {
      console.error('Failed to sync pending operations:', error);
    }
  }, [api, state.isInitialized]);

  // Data retrieval methods (offline-first)
  const getAlbums = useCallback(async (starred?: boolean): Promise<Album[]> => {
    if (!state.isInitialized) return [];
    
    try {
      // Try offline first
      const offlineAlbums = await offlineLibraryManager.getAlbums(starred);
      
      // If offline data exists, return it
      if (offlineAlbums.length > 0) {
        return offlineAlbums;
      }
      
      // If no offline data and we're online, try server
      if (state.isOnline && api) {
        const serverAlbums = starred 
          ? await api.getAlbums('starred')
          : await api.getAlbums('alphabeticalByName', 100);
        
        // Cache the results
        await offlineLibraryManager.storeAlbums(serverAlbums);
        return serverAlbums;
      }
      
      return [];
    } catch (error) {
      console.error('Failed to get albums:', error);
      return [];
    }
  }, [state.isInitialized, state.isOnline, api]);

  const getArtists = useCallback(async (starred?: boolean): Promise<Artist[]> => {
    if (!state.isInitialized) return [];
    
    try {
      const offlineArtists = await offlineLibraryManager.getArtists(starred);
      
      if (offlineArtists.length > 0) {
        return offlineArtists;
      }
      
      if (state.isOnline && api) {
        const serverArtists = await api.getArtists();
        await offlineLibraryManager.storeArtists(serverArtists);
        return serverArtists;
      }
      
      return [];
    } catch (error) {
      console.error('Failed to get artists:', error);
      return [];
    }
  }, [state.isInitialized, state.isOnline, api]);

  const getAlbum = useCallback(async (albumId: string): Promise<{ album: Album; songs: Song[] } | null> => {
    if (!state.isInitialized) return null;
    
    try {
      // Try offline first
      const offlineData = await offlineLibraryManager.getAlbum(albumId);
      
      if (offlineData && offlineData.songs.length > 0) {
        return offlineData;
      }
      
      // If no offline data and we're online, try server
      if (state.isOnline && api) {
        const serverData = await api.getAlbum(albumId);
        
        // Cache the results
        await offlineLibraryManager.storeAlbums([serverData.album]);
        await offlineLibraryManager.storeSongs(serverData.songs);
        
        return serverData;
      }
      
      return offlineData;
    } catch (error) {
      console.error('Failed to get album:', error);
      return null;
    }
  }, [state.isInitialized, state.isOnline, api]);

  const getPlaylists = useCallback(async (): Promise<Playlist[]> => {
    if (!state.isInitialized) return [];
    
    try {
      const offlinePlaylists = await offlineLibraryManager.getPlaylists();
      
      if (offlinePlaylists.length > 0) {
        return offlinePlaylists;
      }
      
      if (state.isOnline && api) {
        const serverPlaylists = await api.getPlaylists();
        await offlineLibraryManager.storePlaylists(serverPlaylists);
        return serverPlaylists;
      }
      
      return [];
    } catch (error) {
      console.error('Failed to get playlists:', error);
      return [];
    }
  }, [state.isInitialized, state.isOnline, api]);

  // Search (offline-first)
  const searchOffline = useCallback(async (query: string): Promise<{ artists: Artist[]; albums: Album[]; songs: Song[] }> => {
    if (!state.isInitialized) {
      return { artists: [], albums: [], songs: [] };
    }
    
    try {
      const offlineResults = await offlineLibraryManager.searchOffline(query);
      
      // If we have good offline results, return them
      const totalResults = offlineResults.artists.length + offlineResults.albums.length + offlineResults.songs.length;
      if (totalResults > 0) {
        return offlineResults;
      }
      
      // If no offline results and we're online, try server
      if (state.isOnline && api) {
        return await api.search2(query);
      }
      
      return offlineResults;
    } catch (error) {
      console.error('Search failed:', error);
      return { artists: [], albums: [], songs: [] };
    }
  }, [state.isInitialized, state.isOnline, api]);

  // Offline favorites management
  const starOffline = useCallback(async (id: string, type: 'song' | 'album' | 'artist'): Promise<void> => {
    if (!state.isInitialized) return;
    
    try {
      if (state.isOnline && api) {
        // If online, try server first
        await api.star(id, type);
      }
      
      // Always update offline data
      await offlineLibraryManager.starOffline(id, type);
      
      // Update stats
      const stats = await offlineLibraryManager.getLibraryStats();
      setState(prev => ({ ...prev, stats }));
      
    } catch (error) {
      console.error('Failed to star item:', error);
      // If server failed but we're online, still save offline for later sync
      if (state.isOnline) {
        await offlineLibraryManager.starOffline(id, type);
        const stats = await offlineLibraryManager.getLibraryStats();
        setState(prev => ({ ...prev, stats }));
      }
      throw error;
    }
  }, [state.isInitialized, state.isOnline, api]);

  const unstarOffline = useCallback(async (id: string, type: 'song' | 'album' | 'artist'): Promise<void> => {
    if (!state.isInitialized) return;
    
    try {
      if (state.isOnline && api) {
        await api.unstar(id, type);
      }
      
      await offlineLibraryManager.unstarOffline(id, type);
      
      const stats = await offlineLibraryManager.getLibraryStats();
      setState(prev => ({ ...prev, stats }));
      
    } catch (error) {
      console.error('Failed to unstar item:', error);
      if (state.isOnline) {
        await offlineLibraryManager.unstarOffline(id, type);
        const stats = await offlineLibraryManager.getLibraryStats();
        setState(prev => ({ ...prev, stats }));
      }
      throw error;
    }
  }, [state.isInitialized, state.isOnline, api]);

  // Playlist management
  const createPlaylistOffline = useCallback(async (name: string, songIds?: string[]): Promise<Playlist> => {
    if (!state.isInitialized) {
      throw new Error('Offline library not initialized');
    }
    
    try {
      if (state.isOnline && api) {
        // If online, try server first
        const serverPlaylist = await api.createPlaylist(name, songIds);
        await offlineLibraryManager.storePlaylists([serverPlaylist]);
        
        const stats = await offlineLibraryManager.getLibraryStats();
        setState(prev => ({ ...prev, stats }));
        
        return serverPlaylist;
      } else {
        // If offline, create locally and queue for sync
        const offlinePlaylist = await offlineLibraryManager.createPlaylistOffline(name, songIds);
        
        const stats = await offlineLibraryManager.getLibraryStats();
        setState(prev => ({ ...prev, stats }));
        
        return offlinePlaylist;
      }
    } catch (error) {
      console.error('Failed to create playlist:', error);
      
      // If server failed but we're online, create offline version for later sync
      if (state.isOnline) {
        const offlinePlaylist = await offlineLibraryManager.createPlaylistOffline(name, songIds);
        const stats = await offlineLibraryManager.getLibraryStats();
        setState(prev => ({ ...prev, stats }));
        return offlinePlaylist;
      }
      
      throw error;
    }
  }, [state.isInitialized, state.isOnline, api]);

  // Scrobble (offline-capable)
  const scrobbleOffline = useCallback(async (songId: string): Promise<void> => {
    if (!state.isInitialized) return;
    
    try {
      if (state.isOnline && api) {
        await api.scrobble(songId);
      } else {
        // Queue for later sync
        await offlineLibraryManager.addSyncOperation({
          type: 'scrobble',
          entityType: 'song',
          entityId: songId,
          data: {}
        });
        
        const stats = await offlineLibraryManager.getLibraryStats();
        setState(prev => ({ ...prev, stats }));
      }
    } catch (error) {
      console.error('Failed to scrobble:', error);
      // Queue for later sync if server failed
      await offlineLibraryManager.addSyncOperation({
        type: 'scrobble',
        entityType: 'song',
        entityId: songId,
        data: {}
      });
      
      const stats = await offlineLibraryManager.getLibraryStats();
      setState(prev => ({ ...prev, stats }));
    }
  }, [state.isInitialized, state.isOnline, api]);

  // Clear all offline data
  const clearOfflineData = useCallback(async (): Promise<void> => {
    if (!state.isInitialized) return;
    
    try {
      await offlineLibraryManager.clearAllData();
      
      const stats = await offlineLibraryManager.getLibraryStats();
      setState(prev => ({ 
        ...prev, 
        stats,
        lastSync: null 
      }));
    } catch (error) {
      console.error('Failed to clear offline data:', error);
      throw error;
    }
  }, [state.isInitialized]);

  // Get songs with offline-first approach
  const getSongs = useCallback(async (albumId?: string, artistId?: string): Promise<Song[]> => {
    if (!state.isInitialized) return [];
    
    try {
      const offlineSongs = await offlineLibraryManager.getSongs(albumId, artistId);
      
      if (offlineSongs.length > 0) {
        return offlineSongs;
      }
      
      if (state.isOnline && api) {
        let serverSongs: Song[] = [];
        
        if (albumId) {
          const { songs } = await api.getAlbum(albumId);
          await offlineLibraryManager.storeSongs(songs);
          serverSongs = songs;
        } else if (artistId) {
          const { albums } = await api.getArtist(artistId);
          const allSongs: Song[] = [];
          for (const album of albums) {
            const { songs } = await api.getAlbum(album.id);
            allSongs.push(...songs);
          }
          await offlineLibraryManager.storeSongs(allSongs);
          serverSongs = allSongs;
        }
        
        return serverSongs;
      }
      
      return [];
    } catch (error) {
      console.error('Failed to get songs:', error);
      return [];
    }
  }, [api, state.isInitialized, state.isOnline]);

  // Queue sync operation
  const queueSyncOperation = useCallback(async (operation: Omit<SyncOperation, 'id' | 'timestamp' | 'retryCount'>): Promise<void> => {
    if (!state.isInitialized) return;
    
    const fullOperation: SyncOperation = {
      ...operation,
      id: `${operation.type}-${operation.entityId}-${Date.now()}`,
      timestamp: Date.now(),
      retryCount: 0
    };
    
    await offlineLibraryManager.addSyncOperation(fullOperation);
    await refreshStats();
  }, [state.isInitialized]);

  // Update playlist offline
  const updatePlaylistOffline = useCallback(async (id: string, name?: string, comment?: string, songIds?: string[]): Promise<void> => {
    if (!state.isInitialized) return;
    
    await offlineLibraryManager.updatePlaylist(id, name, comment, songIds);
    await refreshStats();
  }, [state.isInitialized]);

  // Delete playlist offline
  const deletePlaylistOffline = useCallback(async (id: string): Promise<void> => {
    if (!state.isInitialized) return;
    
    await offlineLibraryManager.deletePlaylist(id);
    await refreshStats();
  }, [state.isInitialized]);

  // Refresh stats
  const refreshStats = useCallback(async (): Promise<void> => {
    if (!state.isInitialized) return;
    
    try {
      const stats = await offlineLibraryManager.getLibraryStats();
      setState(prev => ({ ...prev, stats, lastSync: stats.lastSync }));
    } catch (error) {
      console.error('Failed to refresh stats:', error);
    }
  }, [state.isInitialized]);

  return {
    // State
    ...state,
    
    // Sync methods
    syncLibraryFromServer,
    syncPendingOperations,
    
    // Data retrieval (offline-first)
    getAlbums,
    getArtists,
    getSongs,
    getAlbum,
    getPlaylists,
    searchOffline,
    
    // Offline operations
    starOffline,
    unstarOffline,
    createPlaylistOffline,
    updatePlaylistOffline,
    deletePlaylistOffline,
    scrobbleOffline,
    queueSyncOperation,
    
    // Management
    clearOfflineData,
    refreshStats
  };
}
