import { action, computed, makeObservable, observable } from 'mobx';

type IndeterminateType = 'empty' | 'partial' | 'full';

interface IndeterminateStatus {
  type: IndeterminateType;
  toggle: () => IndeterminateStatus;
  toggleCheckbox: boolean;
}

const statuses: Readonly<Record<IndeterminateType, IndeterminateStatus>> = {
  empty: {
    type: 'empty',
    toggle: () => statuses.partial,
    toggleCheckbox: true,
  },

  partial: {
    type: 'partial',
    toggle: () => statuses.full,
    toggleCheckbox: true,
  },
  full: {
    type: 'full',
    toggle: () => statuses.empty,
    toggleCheckbox: false,
  },
};

export class IndeterminateMapState {
  readonly _map = observable.map<string, IndeterminateStatus>([], {
    deep: false,
  });

  constructor() {
    makeObservable(this, {
      toggle: action,
      setManyPartial: action,
      partialModeKeys: computed,
      partialModeKeysSet: computed,
    });
  }

  toggle(key: string) {
    const { _map } = this;

    const currentStatus = _map.get(key);

    if (currentStatus == null) {
      return;
    }

    const nextStatus = currentStatus.toggle();
    _map.set(key, nextStatus);

    return nextStatus;
  }

  setManyPartial(keys: string[]) {
    const { _map } = this;

    for (const key of keys) {
      if (!_map.has(key)) {
        _map.set(key, statuses.partial);
      }
    }
  }

  isPartial(key: string) {
    const { _map } = this;
    return _map.get(key)?.type === 'partial';
  }

  get partialModeKeys() {
    return Array.from(this._map.entries())
      .filter(([, value]) => value.type === 'partial')
      .map(([key]) => key);
  }

  get partialModeKeysSet() {
    const { partialModeKeys } = this;
    return new Set(partialModeKeys);
  }
}
