import { Action } from '@reduxjs/toolkit';
import reduceReducers from 'reduce-reducers';

import { AlertManagerCortexConfig } from 'app/plugins/datasource/alertmanager/types';

import { alertmanagerApi } from '../api/alertmanagerApi';
import { muteTimingsReducer } from '../reducers/alertmanager/muteTimings';
import { routesReducer } from '../reducers/alertmanager/notificationPolicyRoutes';
import { notificationTemplatesReducer } from '../reducers/alertmanager/notificationTemplates';
import { receiversReducer } from '../reducers/alertmanager/receivers';
import { useAlertmanager } from '../state/AlertmanagerContext';

import { mergeRequestStates } from './mergeRequestStates';

const ERR_NO_ACTIVE_AM = new Error('no active Alertmanager');

const { useLazyGetAlertmanagerConfigurationQuery, useUpdateAlertmanagerConfigurationMutation } = alertmanagerApi;

export const initialAlertmanagerConfiguration: AlertManagerCortexConfig = {
  alertmanager_config: {
    receivers: [],
    route: {},
    time_intervals: [],
    mute_time_intervals: [],
    inhibit_rules: [],
    templates: [],
  },
  template_files: {},
};

const configurationReducer = reduceReducers(
  initialAlertmanagerConfiguration,
  muteTimingsReducer,
  receiversReducer,
  notificationTemplatesReducer,
  routesReducer
);

/**
 * This hook will make sure we are always applying actions that mutate the Alertmanager configuration
 * on top of the latest Alertmanager configuration object.
 */
export function useProduceNewAlertmanagerConfiguration() {
  const { selectedAlertmanager } = useAlertmanager();

  const [fetchAlertmanagerConfig, fetchAlertmanagerState] = useLazyGetAlertmanagerConfigurationQuery();
  const [updateAlertManager, updateAlertmanagerState] = useUpdateAlertmanagerConfigurationMutation();

  const newConfigurationState = mergeRequestStates(fetchAlertmanagerState, updateAlertmanagerState);

  if (!selectedAlertmanager) {
    throw ERR_NO_ACTIVE_AM;
  }

  /**
   * This function will fetch the latest Alertmanager configuration, apply a diff to it via a reducer and
   * returns the result.
   *
   * ┌────────────────────────────┐  ┌───────────────┐  ┌───────────────────┐
   * │ fetch latest configuration │─▶│ apply reducer │─▶│  new rule config  │
   * └────────────────────────────┘  └───────────────┘  └───────────────────┘
   */
  const produceNewAlertmanagerConfiguration = async (action: Action) => {
    const currentAlertmanagerConfiguration = await fetchAlertmanagerConfig(selectedAlertmanager).unwrap();

    const newConfig = configurationReducer(currentAlertmanagerConfiguration, action);

    return updateAlertManager({
      selectedAlertmanager,
      config: newConfig,
    }).unwrap();
  };

  return [produceNewAlertmanagerConfiguration, newConfigurationState] as const;
}
