import { useCallback, useSyncExternalStore } from 'react';

import { isBrowser } from '@redocly/theme/core/utils';

const RECENT_SEARCHES_KEY = 'recentSearches';
const RECENT_SEARCHES_LIMIT = 5;

const createRecentSearchesStore = () => {
  const subscribers = new Set<() => void>();
  let cachedSnapshot: string[];

  const getSnapshot = (): string[] => {
    if (!isBrowser()) return [];

    if (cachedSnapshot) return cachedSnapshot;

    try {
      const stored = localStorage.getItem(RECENT_SEARCHES_KEY);
      cachedSnapshot = stored ? JSON.parse(stored) : [];
      return cachedSnapshot;
    } catch (e) {
      cachedSnapshot = [];
      return cachedSnapshot;
    }
  };

  const updateItems = (value: string, isAdd: boolean) => {
    if (!isBrowser()) return;

    const currentItems = getSnapshot();
    const valueIndex = currentItems.indexOf(value);

    if (valueIndex !== -1) {
      currentItems.splice(valueIndex, 1);
    }

    if (isAdd) {
      currentItems.unshift(value);
    }

    const limitedItems = currentItems.slice(0, RECENT_SEARCHES_LIMIT);

    localStorage.setItem(RECENT_SEARCHES_KEY, JSON.stringify(limitedItems));
    cachedSnapshot = limitedItems;

    subscribers.forEach((callback) => callback());
  };

  const subscribe = (callback: () => void) => {
    subscribers.add(callback);
    return () => subscribers.delete(callback);
  };

  return {
    getSnapshot,
    subscribe,
    updateItems,
  };
};

const recentSearchesStore = createRecentSearchesStore();

export const useRecentSearches = (): {
  items: string[];
  addSearchHistoryItem: (value: string) => void;
  removeSearchHistoryItem: (value: string) => void;
} => {
  const items = useSyncExternalStore(
    recentSearchesStore.subscribe,
    recentSearchesStore.getSnapshot,
    () => [],
  );

  const addSearchHistoryItem = useCallback((value: string) => {
    recentSearchesStore.updateItems(value, true);
  }, []);

  const removeSearchHistoryItem = useCallback((value: string) => {
    recentSearchesStore.updateItems(value, false);
  }, []);

  return { items, addSearchHistoryItem, removeSearchHistoryItem };
};
