import { action, makeObservable, observable } from 'mobx';
import { addResizeObserver, WixPatternsContainer } from '@wix/bex-core';

export interface Rect {
  bottom: number;
  height: number;
  left: number;
  right: number;
  top: number;
  width: number;
  // x: number;
  // y: number;
}

export interface EmptyRect {
  bottom: undefined;
  height: undefined;
  left: undefined;
  right: undefined;
  top: undefined;
  width: undefined;
  // x: undefined;
  // y: undefined;
}

export const emptyRect = () => ({
  bottom: undefined,
  height: undefined,
  left: undefined,
  right: undefined,
  top: undefined,
  width: undefined,
  // x: undefined,
  // y: undefined,
});

export interface RectStateParams {
  container: WixPatternsContainer;
}

export class RectState {
  readonly _syncThrottled;
  readonly _syncDebounced;
  readonly container;

  element?: Element | null | undefined;

  readonly rect = observable<Rect | EmptyRect>(emptyRect());

  constructor(params: RectStateParams) {
    this.container = params.container;

    makeObservable(this, {
      _sync: action.bound,
    });

    this._syncThrottled = this.container.lodash.throttle(() => {
      window.requestAnimationFrame(this._sync);
    }, 200);

    this._syncDebounced = () => window.requestAnimationFrame(this._sync);
  }

  _readRect() {
    const {
      element,
      container: { window },
    } = this;

    if (element == null) {
      return {};
    }

    if (process.env.NODE_ENV === 'test') {
      const { matches } = window.matchMedia('(min-width: 768px)');
      return matches
        ? {
            height: 600,
            width: 840,
            top: 0,
            bottom: 0,
            right: 0,
            left: 0,
            y: 0,
            x: 0,
          }
        : {
            height: 600,
            width: 610,
            top: 0,
            bottom: 0,
            right: 0,
            left: 0,
            y: 0,
            x: 0,
          };
    }

    const r = element.getBoundingClientRect();
    return {
      bottom: r.bottom,
      height: r.height,
      left: r.left,
      right: r.right,
      top: r.top,
      width: r.width,
      x: r.x,
      y: r.y,
    };
  }

  _sync() {
    const { rect } = this;
    Object.assign(rect, this._readRect());
  }

  init({ scrollElement }: { scrollElement?: Element | null } = {}) {
    const { element } = this;
    if (element) {
      this._syncThrottled();

      if (scrollElement) {
        scrollElement.addEventListener('scroll', this._syncThrottled);
      }

      const disposers = [addResizeObserver(element, this._syncDebounced)];

      return action(() => {
        disposers.forEach((d) => d?.());
        Object.assign(this.rect, emptyRect());

        if (scrollElement) {
          scrollElement.removeEventListener('scroll', this._syncThrottled);
        }
      });
    }

    return undefined;
  }
}
