/* eslint-disable import/no-cycle */
import titles from 'Components/Calculation/config/elements-titles';
import type * as Values from 'Components/Calculation/config/map/values';
import { getValueName } from 'Components/Calculation/config/map/values';
import { observable } from 'mobx';
import type RootStore from 'stores/root';
import Validation from 'stores/validation';
import type { BaseOption } from 'ui/elements/types';
import type { RemoveError, ValidationParams } from '../validation/types';
import OptionsStore from './options';
import StatusStore from './statuses';
import ValuesStore from './values';

export default class CalculationStore {
  $values: ValuesStore;
  $status: StatusStore;
  $options: OptionsStore;
  $validation: Partial<Record<Values.Elements, Validation>>;

  constructor(rootStore: RootStore) {
    this.$values = new ValuesStore(rootStore);
    this.$status = new StatusStore(rootStore);
    this.$options = new OptionsStore(rootStore);
    this.$validation = observable.object({});
  }

  private createElementValidation = <E extends Values.Elements>(elementName: E) => {
    this.$validation[elementName] = new Validation({
      err_key: elementName,
      err_title: titles[elementName],
    });
  };

  element = <E extends Values.Elements>(elementName: E) => ({
    reset: () => {
      const valueName = getValueName(elementName);
      this.$values.resetValue(valueName);

      this.$options.resetOptions(elementName);
      this.$status.resetStatus(elementName);
      this.$validation[elementName]?.clearErrors();

      return this.element(elementName);
    },

    block: () => {
      this.$status.setStatus(elementName, 'Disabled');

      return this.element(elementName);
    },

    unblock: () => {
      this.$status.setStatus(elementName, 'Default');

      return this.element(elementName);
    },

    getValue: () => {
      const valueName = getValueName(elementName);

      return this.$values.getValue(valueName) as Values.ElementsTypes[E];
    },

    setValue: (value: Values.ElementsTypes[E]) => {
      const valueName = getValueName(elementName);
      this.$values.setValue(valueName, value);

      return this.element(elementName);
    },

    resetValue: () => {
      const valueName = getValueName(elementName);
      this.$values.resetValue(valueName);

      return this.element(elementName);
    },

    getOption: () => {
      const valueName = getValueName(elementName);
      const value = this.$values.getValue(valueName) as Values.ElementsTypes[E];

      return this.$options.getOptions(elementName)?.find((x) => x.value === value);
    },

    setOptions: (options: BaseOption<Values.ElementsTypes[E]>[]) => {
      this.$options.setOptions(elementName, options);

      return this.element(elementName);
    },

    resetOptions: () => {
      this.$options.resetOptions(elementName);

      return this.element(elementName);
    },

    validate: ({ invalid, message }: ValidationParams) => {
      if (!this.$validation[elementName]) this.createElementValidation(elementName);

      let removeError: RemoveError | undefined;
      if (invalid) {
        removeError = this.$validation[elementName]?.addError(message);
      } else {
        this.$validation[elementName]?.removeError(message);
      }

      return {
        err(callback: (removeError: RemoveError) => void) {
          if (removeError) callback(removeError);
        },
      };
    },

    cleanErrors: () => {
      this.$validation[elementName]?.clearErrors();
    },
  });
}
