import { compose } from '../../helpers';
import { forwardAttrs, getComponentName, syncAttrs } from '../../helpers/componentHelpers';
import {
  draggableMixin,
  createProxy,
  componentNameValidationMixin,
  hoverableMixin,
} from '../../mixins';

export const componentName = getComponentName('notification');

const NotificationMixin = (superclass) =>
  class NotificationMixinClass extends superclass {
    #card;

    constructor() {
      super();

      const that = this;

      Object.defineProperty(this.baseElement, '_container', {
        get() {
          if (!NotificationMixinClass._container) {
            NotificationMixinClass._container = document.createElement(
              'descope-notification-container'
            );

            // we are overriding the container position to be absolute because we want to keep in inside the flow/widget boundaries
            NotificationMixinClass._container.style.position = 'absolute';

            // we're adding the container to body to avoid Vaadin's container's `openChanged`
            // from breaking when trying to remove it directly from the body.
            document.body.appendChild(NotificationMixinClass._container);
          }

          if (!NotificationMixinClass._container.isConnected) {
            // Then, we're adding the container to the parentNode, which is the desired location
            // for us on the DOM
            setTimeout(() => that.parentNode.appendChild(NotificationMixinClass._container));
          }

          return NotificationMixinClass._container;
        },
      });

      Object.defineProperty(this.baseElement, '_card', {
        get() {
          return that.#card;
        },
      });

      // we want to replace the card with vaadin's remove notification so it will not throw
      this.baseElement._removeNotificationCard = this.close.bind(this);
    }

    close() {
      this.#card.close();
    }

    init() {
      super.init?.();

      this.createCard();

      forwardAttrs(this, this.#card);
      syncAttrs(this.#card, this, { includeAttrs: ['opened'] });

      this.baseElement.renderer = (cardRoot) => {
        Array.from(this.childNodes).forEach((child) => cardRoot.appendChild(child));
      };
    }

    // eslint-disable-next-line class-methods-use-this
    get isContainerEmpty() {
      return !NotificationMixinClass._container.children.length;
    }

    // eslint-disable-next-line class-methods-use-this
    removeContainer() {
      NotificationMixinClass._container.remove();
    }

    removeNotification() {
      // remove descope-notification from the DOM
      this.remove();
      // if needed, remove descope-notification-container
      if (this.isContainerEmpty) this.removeContainer();
    }

    createCard() {
      this.#card = document.createElement('descope-notification-card');
      this.#card.addEventListener('card-closed', this.removeNotification.bind(this));
    }
  };

export const NotificationClass = compose(
  draggableMixin,
  componentNameValidationMixin,
  hoverableMixin,
  NotificationMixin
)(
  createProxy({
    wrappedEleName: 'vaadin-notification',
    excludeAttrsSync: ['tabindex', 'style'],
    componentName,
  })
);
