import {
  addErrorMessage,
  addLoadingMessage,
  addSuccessMessage,
} from 'sentry/actionCreators/indicator';
import type {RequestOptions} from 'sentry/api';
import {Client} from 'sentry/api';
import {t} from 'sentry/locale';
import PluginsStore from 'sentry/stores/pluginsStore';
import type {Plugin} from 'sentry/types/integrations';

const activeFetch: Record<string, Promise<any> | null> = {};
// PluginsStore always exists, so api client should be independent of component lifecycle
const api = new Client();

type Slugs = {
  /**
   * Organization slug
   */
  orgId: string;

  /**
   * Plugin slug
   */
  pluginId: string;

  /**
   * Project slug
   */
  projectId: string;
};

type DoUpdateParams = Slugs & {
  update: Partial<Plugin>;
} & Partial<RequestOptions>;

function doUpdate({orgId, projectId, pluginId, update, ...params}: DoUpdateParams) {
  PluginsStore.onUpdate(pluginId, update);
  const request = api.requestPromise(
    `/projects/${orgId}/${projectId}/plugins/${pluginId}/`,
    {
      ...params,
    }
  );

  // This is intentionally not chained because we want the unhandled promise to be returned
  request
    .then(() => {
      PluginsStore.onUpdateSuccess(pluginId);
    })
    .catch(resp => {
      const err =
        typeof resp?.responseJSON?.detail === 'string'
          ? new Error(resp.responseJSON.detail)
          : new Error('Unable to update plugin');
      PluginsStore.onUpdateError(pluginId, err);
    });

  return request;
}

type FetchPluginsOptions = {
  /**
   * Reset will set loading state = true
   */
  resetLoading?: boolean;
};

/**
 * Fetches list of available plugins for a project
 */
export function fetchPlugins(
  {orgId, projectId}: Pick<Slugs, 'orgId' | 'projectId'>,
  options?: FetchPluginsOptions
): Promise<Plugin[]> {
  const path = `/projects/${orgId}/${projectId}/plugins/`;

  // Make sure we throttle fetches
  if (activeFetch[path]) {
    return activeFetch[path];
  }

  PluginsStore.onFetchAll(options);
  const request = api.requestPromise(path, {
    method: 'GET',
    includeAllArgs: true,
  });

  activeFetch[path] = request;

  // This is intentionally not chained because we want the unhandled promise to be returned
  request
    .then(([data, _, resp]) => {
      PluginsStore.onFetchAllSuccess(data, {
        pageLinks: resp?.getResponseHeader('Link') ?? undefined,
      });

      return data;
    })
    .catch(err => {
      PluginsStore.onFetchAllError(err);
      throw new Error('Unable to fetch plugins');
    })
    .then(() => (activeFetch[path] = null));

  return request;
}

type EnableDisablePluginParams = Slugs;

/**
 * Enables a plugin
 */
export function enablePlugin(params: EnableDisablePluginParams) {
  addLoadingMessage(t('Enabling...'));
  return doUpdate({...params, update: {enabled: true}, method: 'POST'})
    .then(() => addSuccessMessage(t('Plugin was enabled')))
    .catch(() => addErrorMessage(t('Unable to enable plugin')));
}

/**
 * Disables a plugin
 */
export function disablePlugin(params: EnableDisablePluginParams) {
  addLoadingMessage(t('Disabling...'));
  return doUpdate({...params, update: {enabled: false}, method: 'DELETE'})
    .then(() => addSuccessMessage(t('Plugin was disabled')))
    .catch(() => addErrorMessage(t('Unable to disable plugin')));
}
