import { 
  ref, 
  get, 
  set, 
  remove, 
  onValue, 
  off,
  update
} from 'firebase/database';
import { database } from './config';
import { debugLog } from '../utils/logger';
import type { Song, QueueItem, Controller, Singer, DisabledSong } from '../types';

// Basic CRUD operations for controllers
export const controllerService = {
  // Get a specific controller
  getController: async (controllerName: string) => {
    const controllerRef = ref(database, `controllers/${controllerName}`);
    const snapshot = await get(controllerRef);
    return snapshot.exists() ? snapshot.val() : null;
  },

  // Set/update a controller
  setController: async (controllerName: string, data: Controller) => {
    const controllerRef = ref(database, `controllers/${controllerName}`);
    await set(controllerRef, data);
  },

  // Update specific parts of a controller
  updateController: async (controllerName: string, updates: Partial<Controller>) => {
    const controllerRef = ref(database, `controllers/${controllerName}`);
    await update(controllerRef, updates);
  },

  // Listen to controller changes in real-time
  subscribeToController: (controllerName: string, callback: (data: Controller | null) => void) => {
    const controllerRef = ref(database, `controllers/${controllerName}`);
    onValue(controllerRef, (snapshot) => {
      callback(snapshot.exists() ? snapshot.val() : null);
    });
    
    // Return unsubscribe function
    return () => off(controllerRef);
  }
};

// Queue management operations
export const queueService = {
  // Add song to queue
  addToQueue: async (controllerName: string, queueItem: Omit<QueueItem, 'key'>) => {
    const queueRef = ref(database, `controllers/${controllerName}/player/queue`);
    
    // Get current queue to find the next sequential key
    const snapshot = await get(queueRef);
    const currentQueue = snapshot.exists() ? snapshot.val() : {};
    
    // Find the next available numerical key
    const existingKeys = Object.keys(currentQueue)
      .filter(key => /^\d+$/.test(key)) // Only consider numerical keys
      .map(key => parseInt(key, 10))
      .sort((a, b) => a - b);
    
    const nextKey = existingKeys.length > 0 ? Math.max(...existingKeys) + 1 : 0;
    
    // Add the item with the sequential key
    const newItemRef = ref(database, `controllers/${controllerName}/player/queue/${nextKey}`);
    await set(newItemRef, queueItem);
    
    return { key: nextKey.toString() };
  },

  // Remove song from queue
  removeFromQueue: async (controllerName: string, queueItemKey: string) => {
    const queueItemRef = ref(database, `controllers/${controllerName}/player/queue/${queueItemKey}`);
    await remove(queueItemRef);
  },

  // Update queue item
  updateQueueItem: async (controllerName: string, queueItemKey: string, updates: Partial<QueueItem>) => {
    const queueItemRef = ref(database, `controllers/${controllerName}/player/queue/${queueItemKey}`);
    await update(queueItemRef, updates);
  },

  // Clean up queue with inconsistent keys (migrate push ID keys to sequential numerical keys)
  cleanupQueueKeys: async (controllerName: string) => {
    const queueRef = ref(database, `controllers/${controllerName}/player/queue`);
    const snapshot = await get(queueRef);
    
    if (!snapshot.exists()) return;
    
    const queue = snapshot.val();
    const updates: Record<string, QueueItem | null> = {};
    let hasChanges = false;
    
    // Find all push ID keys (non-numerical keys)
    const pushIdKeys = Object.keys(queue).filter(key => !/^\d+$/.test(key));
    
    if (pushIdKeys.length === 0) return; // No cleanup needed
    
    // Get existing numerical keys to find the next available key
    const existingNumericalKeys = Object.keys(queue)
      .filter(key => /^\d+$/.test(key))
      .map(key => parseInt(key, 10))
      .sort((a, b) => a - b);
    
    let nextKey = existingNumericalKeys.length > 0 ? Math.max(...existingNumericalKeys) + 1 : 0;
    
    // Migrate push ID items to sequential numerical keys
    pushIdKeys.forEach(pushIdKey => {
      const item = queue[pushIdKey];
      // Remove the old item with push ID
      updates[pushIdKey] = null;
      // Add the item with sequential numerical key
      updates[nextKey.toString()] = item;
      nextKey++;
      hasChanges = true;
    });
    
    if (hasChanges) {
      await update(queueRef, updates);
    }
  },

  // Listen to queue changes
  subscribeToQueue: (controllerName: string, callback: (data: Record<string, QueueItem>) => void) => {
    const queueRef = ref(database, `controllers/${controllerName}/player/queue`);
    onValue(queueRef, (snapshot) => {
      callback(snapshot.exists() ? snapshot.val() : {});
    });
    
    return () => off(queueRef);
  }
};

// Player state operations
export const playerService = {
  // Update player state
  updatePlayerState: async (controllerName: string, state: Partial<Controller['player']>) => {
    const playerRef = ref(database, `controllers/${controllerName}/player`);
    await update(playerRef, state);
  },

  // Update just the player state value
  updatePlayerStateValue: async (controllerName: string, stateValue: string) => {
    const stateRef = ref(database, `controllers/${controllerName}/player/state`);
    await set(stateRef, stateValue);
  },

  // Listen to player state changes
  subscribeToPlayerState: (controllerName: string, callback: (data: Controller['player']) => void) => {
    const playerRef = ref(database, `controllers/${controllerName}/player`);
    onValue(playerRef, (snapshot) => {
      callback(snapshot.exists() ? snapshot.val() : {});
    });
    
    return () => off(playerRef);
  }
};

// History operations
export const historyService = {
  // Add song to history
  addToHistory: async (controllerName: string, song: Omit<Song, 'key'>) => {
    const historyRef = ref(database, `controllers/${controllerName}/history`);
    const historySnapshot = await get(historyRef);
    const currentHistory = historySnapshot.exists() ? historySnapshot.val() : {};
    const numericKeys = Object.keys(currentHistory)
      .map((key) => parseInt(key, 10))
      .filter((num) => !isNaN(num));
    const nextKey = numericKeys.length > 0 ? Math.max(...numericKeys) + 1 : 1;
    const nextKeyStr = String(nextKey);
    const newHistoryRef = ref(database, `controllers/${controllerName}/history/${nextKeyStr}`);
    await set(newHistoryRef, song);
    return { key: nextKeyStr };
  },

  // Remove song from history
  removeFromHistory: async (controllerName: string, historyItemKey: string) => {
    const historyItemRef = ref(database, `controllers/${controllerName}/history/${historyItemKey}`);
    await remove(historyItemRef);
  },

  // Listen to history changes
  subscribeToHistory: (controllerName: string, callback: (data: Record<string, Song>) => void) => {
    const historyRef = ref(database, `controllers/${controllerName}/history`);
    onValue(historyRef, (snapshot) => {
      callback(snapshot.exists() ? snapshot.val() : {});
    });
    
    return () => off(historyRef);
  }
};

// Favorites operations
export const favoritesService = {
  // Add song to favorites
  addToFavorites: async (controllerName: string, song: Omit<Song, 'key'>) => {
    const favoritesRef = ref(database, `controllers/${controllerName}/favorites`);
    const favoritesSnapshot = await get(favoritesRef);
    const currentFavorites = favoritesSnapshot.exists() ? favoritesSnapshot.val() : {};
    const numericKeys = Object.keys(currentFavorites)
      .map((key) => parseInt(key, 10))
      .filter((num) => !isNaN(num));
    const nextKey = numericKeys.length > 0 ? Math.max(...numericKeys) + 1 : 1;
    const nextKeyStr = String(nextKey);
    const newFavoriteRef = ref(database, `controllers/${controllerName}/favorites/${nextKeyStr}`);
    await set(newFavoriteRef, song);
    return { key: nextKeyStr };
  },

  // Remove song from favorites
  removeFromFavorites: async (controllerName: string, songKey: string) => {
    const songRef = ref(database, `controllers/${controllerName}/favorites/${songKey}`);
    await remove(songRef);
  },

  // Listen to favorites changes
  subscribeToFavorites: (controllerName: string, callback: (data: Record<string, Song>) => void) => {
    const favoritesRef = ref(database, `controllers/${controllerName}/favorites`);
    onValue(favoritesRef, (snapshot) => {
      callback(snapshot.exists() ? snapshot.val() : {});
    });
    
    return () => off(favoritesRef);
  }
};

// Singer management operations
export const singerService = {
  // Add a new singer
  addSinger: async (controllerName: string, singerName: string) => {
    const singersRef = ref(database, `controllers/${controllerName}/player/singers`);
    const singersSnapshot = await get(singersRef);
    
    const currentSingers = singersSnapshot.exists() ? singersSnapshot.val() : {};
    
    // Check if singer already exists
    const existingSinger = Object.values(currentSingers).find((singer) => 
      (singer as Singer).name.toLowerCase() === singerName.toLowerCase()
    );
    
    if (existingSinger) {
      throw new Error('Singer already exists');
    }
    
    // Find the next available numeric key
    const numericKeys = Object.keys(currentSingers)
      .map((key) => parseInt(key, 10))
      .filter((num) => !isNaN(num));
    const nextKey = numericKeys.length > 0 ? Math.max(...numericKeys) + 1 : 1;
    const nextKeyStr = String(nextKey);
    
    // Create new singer with current timestamp
    const newSinger: Omit<Singer, 'key'> = {
      name: singerName,
      lastLogin: new Date().toISOString()
    };
    
    // Add to singers list with numeric key
    const newSingerRef = ref(database, `controllers/${controllerName}/player/singers/${nextKeyStr}`);
    await set(newSingerRef, newSinger);
    
    return { key: nextKeyStr };
  },

  // Remove singer and all their queue items
  removeSinger: async (controllerName: string, singerName: string) => {
    // First, remove all queue items for this singer
    const queueRef = ref(database, `controllers/${controllerName}/player/queue`);
    const queueSnapshot = await get(queueRef);
    
    if (queueSnapshot.exists()) {
      const queue = queueSnapshot.val();
      const updates: Record<string, QueueItem | null> = {};
      
      // Find all queue items for this singer and mark them for removal
      Object.entries(queue).forEach(([key, item]) => {
        if (item && (item as QueueItem).singer.name === singerName) {
          updates[key] = null; // Mark for removal
        }
      });
      
      // Remove the queue items
      if (Object.keys(updates).length > 0) {
        await update(queueRef, updates);
      }
    }
    
    // Then, remove the singer from the singers list
    const singersRef = ref(database, `controllers/${controllerName}/player/singers`);
    const singersSnapshot = await get(singersRef);
    
    if (singersSnapshot.exists()) {
      const singers = singersSnapshot.val();
      const updates: Record<string, Singer | null> = {};
      
      // Find the singer by name and mark for removal
      Object.entries(singers).forEach(([key, singer]) => {
        if (singer && (singer as Singer).name === singerName) {
          updates[key] = null; // Mark for removal
        }
      });
      
      // Remove the singer
      if (Object.keys(updates).length > 0) {
        await update(singersRef, updates);
      }
    }
  },

  // Listen to singers changes
  subscribeToSingers: (controllerName: string, callback: (data: Record<string, Singer>) => void) => {
    const singersRef = ref(database, `controllers/${controllerName}/player/singers`);
    onValue(singersRef, (snapshot) => {
      callback(snapshot.exists() ? snapshot.val() : {});
    });
    
    return () => off(singersRef);
  }
};

// Settings operations
export const settingsService = {
  // Get current settings
  getSettings: async (controllerName: string) => {
    const settingsRef = ref(database, `controllers/${controllerName}/player/settings`);
    const snapshot = await get(settingsRef);
    return snapshot.exists() ? snapshot.val() : null;
  },

  // Update a specific setting
  updateSetting: async (controllerName: string, setting: string, value: boolean) => {
    const settingRef = ref(database, `controllers/${controllerName}/player/settings/${setting}`);
    await set(settingRef, value);
  },

  // Update multiple settings at once
  updateSettings: async (controllerName: string, settings: Record<string, boolean>) => {
    const settingsRef = ref(database, `controllers/${controllerName}/player/settings`);
    await update(settingsRef, settings);
  },

  // Listen to settings changes
  subscribeToSettings: (controllerName: string, callback: (data: Record<string, boolean>) => void) => {
    const settingsRef = ref(database, `controllers/${controllerName}/player/settings`);
    onValue(settingsRef, (snapshot) => {
      callback(snapshot.exists() ? snapshot.val() : {});
    });
    
    return () => off(settingsRef);
  }
};

// Disabled songs management operations
export const disabledSongsService = {
  // Generate a hash for the song path to use as a Firebase-safe key
  generateSongKey: (songPath: string): string => {
    // Simple hash function for the path
    let hash = 0;
    for (let i = 0; i < songPath.length; i++) {
      const char = songPath.charCodeAt(i);
      hash = ((hash << 5) - hash) + char;
      hash = hash & hash; // Convert to 32-bit integer
    }
    return Math.abs(hash).toString(36); // Convert to base36 for shorter keys
  },

  // Add a song to the disabled list
  addDisabledSong: async (controllerName: string, song: Song) => {
    debugLog('disabledSongsService.addDisabledSong called with:', { controllerName, song });
    
    if (!controllerName) {
      throw new Error('Controller name is required');
    }
    
    if (!song.path) {
      throw new Error('Song path is required');
    }
    
    if (!song.artist || !song.title) {
      throw new Error('Song artist and title are required');
    }
    
    const songKey = disabledSongsService.generateSongKey(song.path);
    debugLog('Generated song key:', songKey);
    
    const disabledSongRef = ref(database, `controllers/${controllerName}/disabledSongs/${songKey}`);
    const disabledSong = {
      path: song.path,
      artist: song.artist,
      title: song.title,
      key: song.key,
      disabledAt: new Date().toISOString(),
    };
    
    debugLog('Saving disabled song:', disabledSong);
    
    // Add timeout to prevent hanging
    const timeoutPromise = new Promise((_, reject) => {
      setTimeout(() => reject(new Error('Operation timed out')), 10000);
    });
    
    const savePromise = set(disabledSongRef, disabledSong);
    
    await Promise.race([savePromise, timeoutPromise]);
    debugLog('Disabled song saved successfully');
  },

  // Remove a song from the disabled list
  removeDisabledSong: async (controllerName: string, songPath: string) => {
    const songKey = disabledSongsService.generateSongKey(songPath);
    const disabledSongRef = ref(database, `controllers/${controllerName}/disabledSongs/${songKey}`);
    
    // Add timeout to prevent hanging
    const timeoutPromise = new Promise((_, reject) => {
      setTimeout(() => reject(new Error('Operation timed out')), 10000);
    });
    
    const removePromise = remove(disabledSongRef);
    
    await Promise.race([removePromise, timeoutPromise]);
  },

  // Check if a song is disabled
  isSongDisabled: async (controllerName: string, songPath: string): Promise<boolean> => {
    const songKey = disabledSongsService.generateSongKey(songPath);
    const disabledSongRef = ref(database, `controllers/${controllerName}/disabledSongs/${songKey}`);
    const snapshot = await get(disabledSongRef);
    return snapshot.exists();
  },

  // Get all disabled songs
  getDisabledSongs: async (controllerName: string) => {
    const disabledSongsRef = ref(database, `controllers/${controllerName}/disabledSongs`);
    const snapshot = await get(disabledSongsRef);
    return snapshot.exists() ? snapshot.val() : {};
  },

  // Get disabled song paths as a Set for fast lookup
  getDisabledSongPaths: async (controllerName: string): Promise<Set<string>> => {
    const disabledSongs = await disabledSongsService.getDisabledSongs(controllerName);
    return new Set(Object.values(disabledSongs as Record<string, DisabledSong>).map((song) => song.path));
  },

  // Listen to disabled songs changes
  subscribeToDisabledSongs: (controllerName: string, callback: (data: Record<string, DisabledSong>) => void) => {
    const disabledSongsRef = ref(database, `controllers/${controllerName}/disabledSongs`);
    onValue(disabledSongsRef, (snapshot) => {
      callback(snapshot.exists() ? snapshot.val() : {});
    });
    
    return () => off(disabledSongsRef);
  }
}; 