import * as React from 'react';
import { bindActionCreators } from 'redux';
import { connect, Dispatch } from 'react-redux';
import {
  ContextTypes,
  contextTypeValidator,
} from 'mweb/common/utils/contextTypeValidator';
import { RootState } from 'mweb/common/reducers/root';
import {
  ExperimentNames,
  ExperimentGroups,
  ExperimentData,
  experimentsRecordDecision,
} from 'mweb/common/actions/experiments';
import { getActiveExperimentData } from 'mweb/common/selectors/experiments';

interface ExperimentStateProps {
  experimentData: ExperimentData;
}

interface ExperimentDispatchProps {
  recordExperimentDecision: (experimentData: ExperimentData) => void;
}

interface ExperimentContext {
  activeGroup: ExperimentGroups | undefined;
}

interface ExperimentOwnProps {
  experimentName: ExperimentNames;
  preventRecordingDecision?: boolean;
}

export interface ExperimentProps
  extends ExperimentStateProps,
    ExperimentDispatchProps,
    ExperimentOwnProps {}

interface TreatmentProps {
  children: React.ReactNode;
  group: string;
}

export class Treatment extends React.Component<TreatmentProps, {}> {
  static contextTypes: ContextTypes<ExperimentContext> = {
    activeGroup: contextTypeValidator,
  };

  context: ExperimentContext;

  render(): React.ReactNode {
    return this.context.activeGroup === this.props.group
      ? this.props.children
      : null;
  }
}

export class ExperimentBase extends React.Component<ExperimentProps, {}> {
  static childContextTypes: ContextTypes<ExperimentContext> = {
    activeGroup: contextTypeValidator,
  };

  getChildContext = (): ExperimentContext => {
    return {
      activeGroup: this.props.experimentData.group,
    };
  };

  componentDidMount(): void {
    if (!this.props.preventRecordingDecision) {
      this.props.recordExperimentDecision(this.props.experimentData);
    }
  }

  render(): React.ReactNode {
    return this.props.children || null;
  }
}

function mapStateToProps(
  state: RootState,
  ownProps: ExperimentOwnProps,
): ExperimentStateProps {
  return {
    experimentData: getActiveExperimentData(state, ownProps.experimentName),
  };
}

function mapDispatchToProps(
  dispatch: Dispatch<RootState>,
): ExperimentDispatchProps {
  return bindActionCreators(
    {
      recordExperimentDecision: experimentsRecordDecision,
    },
    dispatch,
  );
}

export const Experiment = connect(mapStateToProps, mapDispatchToProps)(
  ExperimentBase,
) as React.ComponentClass<ExperimentOwnProps>;
