import { Dispatch } from 'redux';

import { RootState } from 'mweb/common/reducers/root';
import { isExperimentDecisionRecorded } from 'mweb/common/selectors/experiments';
import { getActiveExperimentGroup } from 'mweb/common/utils/experimentUtils';

export enum ExperimentUUIDs {
  Test = 'TEST_EXPERIMENT_UUID',
  Branch = 'd5d96231-332d-4538-92c5-bdadbf938201',
}

export enum ExperimentNames {
  Test = 'TEST_EXPERIMENT_NAME',
  Branch = 'mobile_web_branch',
}

export enum ExperimentGroups {
  Control = 'control',
  Treatment = 'treatment',
}

export interface WeightedExperimentGroup {
  readonly value: ExperimentGroups;
  readonly weight: number;
}

export interface ActiveExperimentData {
  readonly uuid: ExperimentUUIDs;
  readonly name: ExperimentNames;
  readonly group: ExperimentGroups;
}

interface DefaultExperimentData {
  readonly uuid: undefined;
  readonly name: ExperimentNames;
  readonly group: ExperimentGroups.Control;
}

export type ExperimentData = ActiveExperimentData | DefaultExperimentData;

export interface ExperimentOverrideMapping {
  [uuid: string]: ExperimentGroups;
}

export interface ExperimentMetadata {
  readonly uuid: ExperimentUUIDs;
  readonly name: ExperimentNames;
  readonly groups: WeightedExperimentGroup[];
}

export const EXPERIMENTS_CONFIGURED_ACTION_TYPE =
  'EXPERIMENTS_CONFIGURED_ACTION_TYPE';

export interface ExperimentsExperimentConfiguredAction {
  type: typeof EXPERIMENTS_CONFIGURED_ACTION_TYPE;
  payload: ExperimentData;
}

export const EXPERIMENTS_RECORD_DECISION_ACTION_TYPE =
  'EXPERIMENTS_RECORD_DECISION_ACTION_TYPE';

export interface ExperimentsDecisionRecordedAction {
  type: typeof EXPERIMENTS_RECORD_DECISION_ACTION_TYPE;
  payload: ExperimentData;
}

export type ExperimentsAction =
  | ExperimentsExperimentConfiguredAction
  | ExperimentsDecisionRecordedAction;

/**
 * Create an action that will determine which experiment group the bucket
 * belongs in.
 *
 * @param metadata Experiment metadata containing weights for each experiment
 *   group that can be used to determine to which group the bucket belongs.
 * @param bucketId The currently active CDN bucket.
 * @param experimentOverrides A mapping of ExperimentUUIDs to ExperimentGroups
 *   that will be used to override the natural bucket experimentation groups.
 */
export function experimentsConfigureExperiment(
  metadata: ExperimentMetadata,
  bucketId: string,
  experimentOverrides: ExperimentOverrideMapping,
): ExperimentsExperimentConfiguredAction {
  return {
    type: EXPERIMENTS_CONFIGURED_ACTION_TYPE,
    payload: {
      uuid: metadata.uuid,
      name: metadata.name,
      group: getActiveExperimentGroup(metadata, bucketId, experimentOverrides),
    },
  };
}

export function experimentsRecordDecision(
  data: ExperimentData,
): (dispatch: Dispatch<RootState>, getState: () => RootState) => void {
  return (dispatch, getState) => {
    const state = getState();
    if (!isExperimentDecisionRecorded(state, data.name)) {
      dispatch(experimentsDecisionRecordedActionBuilder(data));
    }
  };
}

export function experimentsDecisionRecordedActionBuilder(
  data: ExperimentData,
): ExperimentsDecisionRecordedAction {
  return {
    type: EXPERIMENTS_RECORD_DECISION_ACTION_TYPE,
    payload: data,
  };
}
