import { createSelector } from '@reduxjs/toolkit';
import { debounce } from 'lodash';

import { type PluginError, type PluginType, unEscapeStringFromRegex } from '@grafana/data';
import { reportInteraction } from '@grafana/runtime';

import { filterByKeyword, isPluginUpdatable } from '../helpers';
import { RequestStatus, type PluginCatalogStoreState } from '../types';

import { pluginsAdapter } from './reducer';

export const selectRoot = (state: PluginCatalogStoreState) => state.plugins;

export const selectItems = createSelector(selectRoot, ({ items }) => items);

export const { selectAll, selectById } = pluginsAdapter.getSelectors(selectItems);

const debouncedTrackSearch = debounce((count) => {
  reportInteraction('plugins_search', {
    resultsCount: count,
    creator_team: 'grafana_plugins_catalog',
    schema_version: '1.0.0',
  });
}, 300);

export type PluginFilters = {
  // Searches for a string in certain fields (e.g. "name" or "orgName")
  // (Note: this will be an escaped regex string as it comes from `FilterInput`)
  keyword?: string;

  // (Optional, only applied if set)
  type?: PluginType;

  // (Optional, only applied if set)
  isInstalled?: boolean;

  // (Optional, only applied if set)
  isEnterprise?: boolean;

  // (Optional, only applied if set)
  hasUpdate?: boolean;
};

export const selectPlugins = (filters: PluginFilters) =>
  createSelector(selectAll, (plugins) => {
    const keyword = filters.keyword ? unEscapeStringFromRegex(filters.keyword.toLowerCase()) : '';
    // Fuzzy search does not consider plugin type filter
    const filteredPluginIds = keyword !== '' ? filterByKeyword(plugins, keyword) : null;

    // Filters are applied here
    const filteredPlugins = plugins.filter((plugin) => {
      if (keyword && filteredPluginIds == null) {
        return false;
      }

      if (keyword && !filteredPluginIds?.includes(plugin.id)) {
        return false;
      }

      if (filters.type && plugin.type !== filters.type) {
        return false;
      }

      if (filters.isInstalled !== undefined && plugin.isInstalled !== filters.isInstalled) {
        return false;
      }

      if (filters.isEnterprise !== undefined && plugin.isEnterprise !== filters.isEnterprise) {
        return false;
      }

      if (filters.hasUpdate !== undefined && (plugin.hasUpdate !== filters.hasUpdate || !isPluginUpdatable(plugin))) {
        return false;
      }

      return true;
    });

    if (keyword) {
      debouncedTrackSearch(filteredPlugins.length);
    }

    return filteredPlugins;
  });

export const selectPluginErrors = (filterByPluginType?: PluginType) =>
  createSelector(selectAll, (plugins) => {
    const pluginErrors: PluginError[] = [];
    for (const plugin of plugins) {
      if (plugin.error && (!filterByPluginType || plugin.type === filterByPluginType)) {
        pluginErrors.push({
          pluginId: plugin.id,
          errorCode: plugin.error,
          pluginType: plugin.type,
        });
      }
    }
    return pluginErrors;
  });

// The following selectors are used to get information about the outstanding or completed plugins-related network requests.
export const selectRequest = (actionType: string) =>
  createSelector(selectRoot, ({ requests = {} }) => requests[actionType]);

export const selectIsRequestPending = (actionType: string) =>
  createSelector(selectRequest(actionType), (request) => request?.status === RequestStatus.Pending);

export const selectRequestError = (actionType: string) =>
  createSelector(selectRequest(actionType), (request) =>
    request?.status === RequestStatus.Rejected ? request?.error : null
  );

export const selectIsRequestNotFetched = (actionType: string) =>
  createSelector(selectRequest(actionType), (request) => request === undefined);
