import {
  Filter,
  FilterOperator,
  OperatorFilterValue,
  operatorNeedsValue as coreOperatorNeedsValue,
} from '@wix/bex-core';
import { action, computed, makeObservable, observable, reaction } from 'mobx';
import { FilterToolbarState } from '../providers';
import { debounce } from 'lodash';

export interface OperatorFilterPickerStateParams<T> {
  toolbar: FilterToolbarState;
  filter: Filter<OperatorFilterValue<T>>;
  /**
   * Debounce delay in milliseconds before applying filter changes
   * @default 300
   */
  debounceMs?: number;
}

export class OperatorFilterPickerState<T> {
  readonly toolbar: FilterToolbarState;
  readonly _filter: OperatorFilterPickerStateParams<T>['filter'];
  private readonly debouncedRefresh: () => void;

  operator: FilterOperator;
  value: T | null;

  get filter() {
    return this.toolbar.getPendingFilter(this._filter);
  }

  constructor(params: OperatorFilterPickerStateParams<T>) {
    this.toolbar = params.toolbar;
    this._filter = params.filter;
    this.operator = params.filter.value?.operator!;
    this.value = params.filter.value?.value ?? null;

    const debounceMs = params.debounceMs ?? 300;
    this.debouncedRefresh = debounce(
      () => this.refreshIfChanged({ actionType: 'add' }),
      debounceMs,
    );

    makeObservable(this, {
      operator: observable,
      value: observable,
      isEmpty: computed,
      init: action,
      setOperator: action,
      setValue: action,
      onChange: action,
      applyImmediately: action,
    });
  }

  get isEmpty() {
    return this.filter.isEmpty;
  }

  get currentValue(): OperatorFilterValue<T> {
    return {
      operator: this.operator,
      value: this.value,
    };
  }

  init() {
    const dispose = reaction(
      () => this.filter.value,
      (filterValue) => {
        this.operator = filterValue.operator;
        this.value = filterValue.value;
      },
      { fireImmediately: true },
    );

    return () => {
      dispose();
    };
  }

  remove() {
    this.refreshIfChanged({ actionType: 'remove' });
  }

  setOperator(operator: FilterOperator) {
    this.operator = operator;
    if (!this.operatorNeedsValue()) {
      this.value = null;
    }
    this.applyImmediately();
  }

  setValue(value: T | null) {
    this.value = value;
    this.onChange();
  }

  /**
   * Called when value changes - debounced
   */
  onChange() {
    this.debouncedRefresh();
  }

  /**
   * Apply filter changes immediately (used for operator changes)
   */
  applyImmediately() {
    this.refreshIfChanged({ actionType: 'add' });
  }

  private refreshIfChanged(
    options: { actionType?: 'add' | 'remove'; clickedValueKey?: string } = {},
  ) {
    const newValue = this.currentValue;

    if (this.filter.hasDiff(newValue)) {
      this.filter.setValue(newValue, {
        emitEvents: ['change', 'beforeRefresh', 'refresh'],
        ...options,
      });
      return true;
    }

    return false;
  }

  /**
   * Check if the given operator requires a value input
   */
  operatorNeedsValue(): boolean {
    return coreOperatorNeedsValue(this.operator);
  }
}
