/**
 * Global Data Store with Caching and Request Deduplication
 *
 * This store provides centralized data management for the worship platform.
 * Features:
 * - In-memory caching with configurable TTL (time-to-live)
 * - Request deduplication (prevents multiple identical API calls)
 * - Stale-while-revalidate pattern
 * - Automatic cache invalidation on mutations
 * - Optimistic updates for better UX
 */

import { create } from "zustand";
import api from "@utils/api";

// Cache TTL configuration (in milliseconds)
const CACHE_TTL = {
  songs: 5 * 60 * 1000, // 5 minutes - songs don't change often
  lists: 2 * 60 * 1000, // 2 minutes - lists may change more frequently
  profiles: 5 * 60 * 1000, // 5 minutes - profiles rarely change
  stats: 1 * 60 * 1000, // 1 minute - stats are quick to fetch
  songDetail: 5 * 60 * 1000, // 5 minutes - individual song details
};

// Track in-flight requests to prevent duplicates
const inFlightRequests = new Map();

/**
 * Execute a request with deduplication
 * If the same request is already in flight, return that promise instead
 */
const deduplicatedFetch = async (key, fetchFn) => {
  // If request is already in flight, return existing promise
  if (inFlightRequests.has(key)) {
    return inFlightRequests.get(key);
  }

  // Create new request promise
  const requestPromise = fetchFn().finally(() => {
    // Clean up after request completes
    inFlightRequests.delete(key);
  });

  inFlightRequests.set(key, requestPromise);
  return requestPromise;
};

/**
 * Check if cached data is still valid
 */
const isCacheValid = (lastFetched, ttl) => {
  if (!lastFetched) return false;
  return Date.now() - lastFetched < ttl;
};

/**
 * Main data store using Zustand
 */
const useDataStore = create((set, get) => ({
  // ================== SONGS ==================
  songs: [],
  songsLastFetched: null,
  songsLoading: false,
  songsError: null,

  /**
   * Fetch all songs with caching
   * @param {boolean} force - Force refresh even if cache is valid
   */
  fetchSongs: async (force = false) => {
    const state = get();

    // Return cached data if still valid
    if (!force && isCacheValid(state.songsLastFetched, CACHE_TTL.songs)) {
      return state.songs;
    }

    // Set loading state (but keep existing data for stale-while-revalidate)
    set({ songsLoading: true, songsError: null });

    try {
      const songs = await deduplicatedFetch("songs", async () => {
        const res = await api.get("/songs?limit=200");
        if (res.data.success) {
          return res.data.songs;
        }
        throw new Error("Failed to fetch songs");
      });

      set({
        songs,
        songsLastFetched: Date.now(),
        songsLoading: false,
      });

      return songs;
    } catch (error) {
      set({ songsError: error.message, songsLoading: false });
      return state.songs; // Return stale data on error
    }
  },

  /**
   * Get a single song - checks local cache first
   */
  getSong: (id) => {
    const state = get();
    return state.songs.find((s) => s.id === id) || null;
  },

  /**
   * Invalidate songs cache (call after mutations)
   */
  invalidateSongs: () => {
    set({ songsLastFetched: null });
  },

  /**
   * Update a song in the local cache (optimistic update)
   */
  updateSongInCache: (songId, updates) => {
    set((state) => ({
      songs: state.songs.map((song) =>
        song.id === songId ? { ...song, ...updates } : song,
      ),
    }));
  },

  /**
   * Add a song to the local cache
   */
  addSongToCache: (song) => {
    set((state) => ({
      songs: [song, ...state.songs],
    }));
  },

  /**
   * Remove a song from the local cache
   */
  removeSongFromCache: (songId) => {
    set((state) => ({
      songs: state.songs.filter((song) => song.id !== songId),
    }));
  },

  // ================== SONG DETAILS ==================
  songDetails: {}, // Map of song id -> { data, lastFetched }
  songDetailsLoading: {},

  /**
   * Fetch a single song's full details
   */
  fetchSongDetail: async (id, force = false) => {
    const state = get();
    const cached = state.songDetails[id];

    // Return cached data if still valid
    if (
      !force &&
      cached &&
      isCacheValid(cached.lastFetched, CACHE_TTL.songDetail)
    ) {
      return cached.data;
    }

    // Set loading state
    set((state) => ({
      songDetailsLoading: { ...state.songDetailsLoading, [id]: true },
    }));

    try {
      const song = await deduplicatedFetch(`song-${id}`, async () => {
        const res = await api.get(`/songs/${id}`);
        if (res.data.success) {
          return res.data.song;
        }
        throw new Error("Failed to fetch song");
      });

      set((state) => ({
        songDetails: {
          ...state.songDetails,
          [id]: { data: song, lastFetched: Date.now() },
        },
        songDetailsLoading: { ...state.songDetailsLoading, [id]: false },
      }));

      // Also update the song in the songs list if it exists
      const existingIndex = get().songs.findIndex((s) => s.id === id);
      if (existingIndex !== -1) {
        set((state) => ({
          songs: state.songs.map((s) => (s.id === id ? { ...s, ...song } : s)),
        }));
      }

      return song;
    } catch (error) {
      set((state) => ({
        songDetailsLoading: { ...state.songDetailsLoading, [id]: false },
      }));
      return cached?.data || null;
    }
  },

  /**
   * Invalidate a specific song's cache
   */
  invalidateSongDetail: (id) => {
    set((state) => ({
      songDetails: {
        ...state.songDetails,
        [id]: { ...state.songDetails[id], lastFetched: null },
      },
    }));
  },

  // ================== WORSHIP LISTS ==================
  lists: [],
  listsLastFetched: null,
  listsLoading: false,
  listsError: null,

  /**
   * Fetch all worship lists
   */
  fetchLists: async (force = false) => {
    const state = get();

    if (!force && isCacheValid(state.listsLastFetched, CACHE_TTL.lists)) {
      return state.lists;
    }

    set({ listsLoading: true, listsError: null });

    try {
      const lists = await deduplicatedFetch("lists", async () => {
        const res = await api.get("/lists");
        if (res.data.success) {
          return res.data.lists;
        }
        throw new Error("Failed to fetch lists");
      });

      set({
        lists,
        listsLastFetched: Date.now(),
        listsLoading: false,
      });

      return lists;
    } catch (error) {
      set({ listsError: error.message, listsLoading: false });
      return state.lists;
    }
  },

  /**
   * Invalidate lists cache
   */
  invalidateLists: () => {
    set({ listsLastFetched: null });
  },

  /**
   * Update a list in the local cache
   */
  updateListInCache: (listId, updates) => {
    set((state) => ({
      lists: state.lists.map((list) =>
        list.id === listId ? { ...list, ...updates } : list,
      ),
    }));
  },

  /**
   * Add a list to the local cache
   */
  addListToCache: (list) => {
    set((state) => ({
      lists: [list, ...state.lists],
    }));
  },

  /**
   * Remove a list from the local cache
   */
  removeListFromCache: (listId) => {
    set((state) => ({
      lists: state.lists.filter((list) => list.id !== listId),
    }));
  },

  // ================== LIST DETAILS (songs in a list) ==================
  listDetails: {}, // Map of list id -> { songs, lastFetched }
  listDetailsLoading: {},

  /**
   * Fetch songs for a specific list
   */
  fetchListDetail: async (listId, force = false) => {
    const state = get();
    const cached = state.listDetails[listId];

    if (!force && cached && isCacheValid(cached.lastFetched, CACHE_TTL.lists)) {
      return cached.songs;
    }

    set((state) => ({
      listDetailsLoading: { ...state.listDetailsLoading, [listId]: true },
    }));

    try {
      const songs = await deduplicatedFetch(`list-${listId}`, async () => {
        const res = await api.get(`/lists/${listId}`);
        if (res.data.success) {
          return res.data.songs || [];
        }
        throw new Error("Failed to fetch list details");
      });

      set((state) => ({
        listDetails: {
          ...state.listDetails,
          [listId]: { songs, lastFetched: Date.now() },
        },
        listDetailsLoading: { ...state.listDetailsLoading, [listId]: false },
      }));

      return songs;
    } catch (error) {
      set((state) => ({
        listDetailsLoading: { ...state.listDetailsLoading, [listId]: false },
      }));
      return cached?.songs || [];
    }
  },

  /**
   * Invalidate a specific list's details
   */
  invalidateListDetail: (listId) => {
    set((state) => ({
      listDetails: {
        ...state.listDetails,
        [listId]: { ...state.listDetails[listId], lastFetched: null },
      },
    }));
  },

  // ================== PROFILES ==================
  profiles: [],
  profilesLastFetched: null,
  profilesLoading: false,
  profilesError: null,

  /**
   * Fetch all profiles
   */
  fetchProfiles: async (force = false) => {
    const state = get();

    if (!force && isCacheValid(state.profilesLastFetched, CACHE_TTL.profiles)) {
      return state.profiles;
    }

    set({ profilesLoading: true, profilesError: null });

    try {
      const profiles = await deduplicatedFetch("profiles", async () => {
        const res = await api.get("/profiles");
        if (res.data.success) {
          return res.data.profiles || [];
        }
        throw new Error("Failed to fetch profiles");
      });

      set({
        profiles,
        profilesLastFetched: Date.now(),
        profilesLoading: false,
      });

      return profiles;
    } catch (error) {
      set({ profilesError: error.message, profilesLoading: false });
      return state.profiles;
    }
  },

  /**
   * Invalidate profiles cache
   */
  invalidateProfiles: () => {
    set({ profilesLastFetched: null });
  },

  // ================== STATS ==================
  stats: { songs: 0, profiles: 0, lists: 0 },
  statsLastFetched: null,
  statsLoading: false,

  /**
   * Fetch stats
   */
  fetchStats: async (force = false) => {
    const state = get();

    if (!force && isCacheValid(state.statsLastFetched, CACHE_TTL.stats)) {
      return state.stats;
    }

    set({ statsLoading: true });

    try {
      const stats = await deduplicatedFetch("stats", async () => {
        const res = await api.get("/stats");
        if (res.data.success) {
          return res.data.stats;
        }
        throw new Error("Failed to fetch stats");
      });

      set({
        stats,
        statsLastFetched: Date.now(),
        statsLoading: false,
      });

      return stats;
    } catch (error) {
      set({ statsLoading: false });
      return state.stats;
    }
  },

  // ================== SEARCH ==================
  searchCache: {}, // Map of query -> { results, timestamp }
  searchLoading: false,

  /**
   * Search songs with caching
   */
  searchSongs: async (query) => {
    const state = get();
    const trimmedQuery = query.trim().toLowerCase();

    if (!trimmedQuery) {
      return [];
    }

    // Check search cache (short TTL of 30 seconds)
    const cached = state.searchCache[trimmedQuery];
    if (cached && Date.now() - cached.timestamp < 30000) {
      return cached.results;
    }

    // First, try to search locally in the songs array
    const localResults = state.songs
      .filter(
        (song) =>
          song.title?.toLowerCase().includes(trimmedQuery) ||
          song.artist?.toLowerCase().includes(trimmedQuery) ||
          song.singer?.toLowerCase().includes(trimmedQuery),
      )
      .slice(0, 20);

    // If we have songs cached and get results, use local search
    if (state.songs.length > 0 && localResults.length > 0) {
      set((state) => ({
        searchCache: {
          ...state.searchCache,
          [trimmedQuery]: { results: localResults, timestamp: Date.now() },
        },
      }));
      return localResults;
    }

    // Otherwise, fall back to API search
    set({ searchLoading: true });

    try {
      const results = await deduplicatedFetch(
        `search-${trimmedQuery}`,
        async () => {
          const res = await api.get(
            `/songs/search?q=${encodeURIComponent(trimmedQuery)}`,
          );
          if (res.data.success) {
            return res.data.songs || [];
          }
          throw new Error("Search failed");
        },
      );

      set((state) => ({
        searchCache: {
          ...state.searchCache,
          [trimmedQuery]: { results, timestamp: Date.now() },
        },
        searchLoading: false,
      }));

      return results;
    } catch (error) {
      set({ searchLoading: false });
      return localResults; // Fall back to local results
    }
  },

  /**
   * Clear search cache
   */
  clearSearchCache: () => {
    set({ searchCache: {} });
  },

  // ================== GLOBAL CACHE MANAGEMENT ==================

  /**
   * Invalidate all caches (use after major changes)
   */
  invalidateAll: () => {
    set({
      songsLastFetched: null,
      listsLastFetched: null,
      profilesLastFetched: null,
      statsLastFetched: null,
      songDetails: {},
      listDetails: {},
      searchCache: {},
    });
  },

  /**
   * Prefetch common data (call on app initialization)
   */
  prefetch: async () => {
    // Fetch in parallel
    await Promise.all([
      get().fetchSongs(),
      get().fetchLists(),
      get().fetchStats(),
    ]);
  },

  /**
   * Get cache status (for debugging)
   */
  getCacheStatus: () => {
    const state = get();
    return {
      songs: {
        count: state.songs.length,
        lastFetched: state.songsLastFetched,
        valid: isCacheValid(state.songsLastFetched, CACHE_TTL.songs),
      },
      lists: {
        count: state.lists.length,
        lastFetched: state.listsLastFetched,
        valid: isCacheValid(state.listsLastFetched, CACHE_TTL.lists),
      },
      profiles: {
        count: state.profiles.length,
        lastFetched: state.profilesLastFetched,
        valid: isCacheValid(state.profilesLastFetched, CACHE_TTL.profiles),
      },
      stats: {
        lastFetched: state.statsLastFetched,
        valid: isCacheValid(state.statsLastFetched, CACHE_TTL.stats),
      },
    };
  },
}));

export default useDataStore;

// Export individual selectors for better performance
export const useSongs = () => useDataStore((state) => state.songs);
export const useSongsLoading = () =>
  useDataStore((state) => state.songsLoading);
export const useLists = () => useDataStore((state) => state.lists);
export const useListsLoading = () =>
  useDataStore((state) => state.listsLoading);
export const useProfiles = () => useDataStore((state) => state.profiles);
export const useProfilesLoading = () =>
  useDataStore((state) => state.profilesLoading);
export const useStats = () => useDataStore((state) => state.stats);
export const useStatsLoading = () =>
  useDataStore((state) => state.statsLoading);
