import debounce from 'debounce-promise';
import { useEffect, useRef, useState } from 'react';

import { t } from '@grafana/i18n';
import { config } from '@grafana/runtime';
import { contextSrv } from 'app/core/services/context_srv';
import { getRecentlyViewedDashboards } from 'app/features/browse-dashboards/api/recentlyViewed';
import { getGrafanaSearcher } from 'app/features/search/service/searcher';

import { type CommandPaletteAction } from '../types';
import { RECENT_DASHBOARDS_PRIORITY, SEARCH_RESULTS_PRIORITY } from '../values';

const MAX_SEARCH_RESULTS = 100;
const MAX_RECENT_DASHBOARDS = 5;

const debouncedSearch = debounce(getSearchResultActions, 200);

export async function getRecentDashboardActions(): Promise<CommandPaletteAction[]> {
  if (!contextSrv.user.isSignedIn) {
    return [];
  }

  const recentResults = await getRecentlyViewedDashboards(MAX_RECENT_DASHBOARDS);

  const recentDashboardActions: CommandPaletteAction[] = recentResults.map((item) => {
    const { url, name } = item; // items are backed by DataFrameView, so must hold the url in a closure
    return {
      id: `recent-dashboards${url}`,
      name: `${name}`,
      section: t('command-palette.section.recent-dashboards', 'Recent dashboards'),
      priority: RECENT_DASHBOARDS_PRIORITY,
      url,
    };
  });

  return recentDashboardActions;
}

export async function getSearchResultActions(searchQuery: string): Promise<CommandPaletteAction[]> {
  // Empty strings should not come through to here
  if (searchQuery.length === 0 || (!contextSrv.user.isSignedIn && !config.anonymousEnabled)) {
    return [];
  }

  const data = await getGrafanaSearcher().search({
    kind: ['dashboard', 'folder'],
    query: searchQuery,
    limit: MAX_SEARCH_RESULTS,
  });

  const goToSearchResultActions: CommandPaletteAction[] = data.view.map((item) => {
    const { url, name, kind, location } = item; // items are backed by DataFrameView, so must hold the url in a closure
    return {
      id: `go/${kind}${url}`,
      name: `${name}`,
      section:
        kind === 'dashboard'
          ? t('command-palette.section.dashboard-search-results', 'Dashboards')
          : t('command-palette.section.folder-search-results', 'Folders'),
      priority: SEARCH_RESULTS_PRIORITY,
      url,
      subtitle: data.view.dataFrame.meta?.custom?.locationInfo[location]?.name,
    };
  });

  return goToSearchResultActions;
}

/**
 * Implements actual search logic for dashboards and folders.
 */
export function useSearchResults({ searchQuery, show }: { searchQuery: string; show: boolean }) {
  const [searchResults, setSearchResults] = useState<CommandPaletteAction[]>([]);
  const [isFetchingSearchResults, setIsFetchingSearchResults] = useState(false);
  const lastSearchTimestamp = useRef<number>(0);

  // Hit dashboards API
  useEffect(() => {
    const timestamp = Date.now();
    if (show && searchQuery.length > 0) {
      setIsFetchingSearchResults(true);
      debouncedSearch(searchQuery).then((resultActions) => {
        // Only keep the results if it's was issued after the most recently resolved search.
        // This prevents results showing out of order if first request is slower than later ones.
        // We don't need to worry about clearing the isFetching state either - if there's a later
        // request in progress, this will clear it for us
        if (timestamp > lastSearchTimestamp.current) {
          setSearchResults(resultActions);
          setIsFetchingSearchResults(false);
          lastSearchTimestamp.current = timestamp;
        }
      });
    } else {
      setSearchResults([]);
      setIsFetchingSearchResults(false);
      lastSearchTimestamp.current = timestamp;
    }
  }, [show, searchQuery]);

  return {
    searchResults,
    isFetchingSearchResults,
  };
}
