/**
 * collapsible.js
 * Script to expand and collapse elements
 */

import ElementHelpers from '../helpers/element-helpers';

/**
 * Declare constants
 */
const attributeBase = 'data-rs-collapsible';

export class Collapsible {
  constructor(element) {
    this.element = element;
    this.content = this.element.parentElement.querySelector(
      `[${this.attributes.content}]`
    );
    this.triggerElement = this.element;
    this.initialExpanded =
      this.content.getAttribute(this.attributes.content) ===
      this.states.expanded;

    this.addEventHandlers();
    if (this.initialExpanded) {
      this.handleExpandedElement();
    }
  }

  /**
   * Declare attribute constants
   */
  get attributes() {
    return {
      content: `${attributeBase}-content`,
      button: `${attributeBase}-button`,
      expanded: `${attributeBase}-expanded`,
      navigation: 'navigation',
      ariaExpanded: 'aria-expanded',
      ariaHidden: 'aria-hidden',
      filter: 'filter',
      closeOnOutsideClick: `${attributeBase}-close-on-outside-click`,
      closeOnScroll: `${attributeBase}-close-on-scroll`
    };
  }

  /**
   * Declare classes
   */
  get classes() {
    return {
      collapsibleTriggerExpanded: 'collapsible__trigger--expanded'
    };
  }

  /**
   * Declare state constants
   */
  get states() {
    return {
      expanded: 'expanded'
    };
  }

  /**
   * Add event handlers
   */
  addEventHandlers() {
    if (
      this.element.getAttribute(attributeBase) === this.attributes.navigation
    ) {
      this.triggerElement = this.element.querySelector(
        `[${this.attributes.button}]`
      );
    }

    if (this.triggerElement != null) {
      this.triggerElement.addEventListener('click', clickEvent => {
        clickEvent.preventDefault();
        this.toggle();
      });
    }
    if (this.initialExpanded) {
      window.addEventListener('load', () => {
        this.handleExpandedElement();
      });
    }

    // on orientation change, recalculate content height
    window.addEventListener('orientationchange', () => {
      this.recalculateContentHeight();
    });

    window.addEventListener('resize', () => {
      this.recalculateContentHeight();
    });

    window.addEventListener('scroll', () => {
      this.simulateClickOnScroll();
    });
  }

  get isExpanedElementToClose() {
    const elementsToClose = ElementHelpers.getElementByAttribute(
      this.attributes.closeOnScroll
    );

    const elementsToCloseIsExpanded = elementsToClose.classList.contains(
      this.classes.collapsibleTriggerExpanded
    );

    return elementsToCloseIsExpanded;
  }

  /*
   * if the element is expanded and needs to be closed on scroll, simulate a click
   */
  simulateClickOnScroll() {
    if (
      this.isExpanedElementToClose &&
      this.element.hasAttribute(this.attributes.closeOnScroll)
    ) {
      const elementPos = this.element.parentElement.offsetTop;
      const windowScrollPos = window.scrollY;

      if (windowScrollPos > elementPos) {
        this.element.click();
      }
    }
  }

  toggle() {
    // set state element (trigger)
    if (this.isTriggerExpanded) {
      this.collapseTrigger();
      this.collapseContent();
    } else {
      const contentHeight = this.content.scrollHeight;
      this.expandTrigger();
      this.expandContent(contentHeight);
    }
  }

  handleExpandedElement() {
    if (ElementHelpers.isDesktop()) {
      const contentHeight = this.content.scrollHeight;
      if (this.content.style.maxHeight !== `${contentHeight}px`) {
        this.expandContent(contentHeight);
        this.expandTrigger();
      }
      if (this.initialExpanded === false) {
        this.content.setAttribute(
          this.attributes.content,
          this.attributes.filter
        );
        this.element.setAttribute(
          this.attributes.expanded,
          this.states.expanded
        );
      }
    } else {
      // do not collapse on mobile devices if in filter
      this.collapseTrigger();
      this.collapseContent();
    }
  }

  get isTriggerExpanded() {
    return this.element.classList.contains(
      this.classes.collapsibleTriggerExpanded
    );
  }

  collapseTrigger() {
    this.element.classList.remove(this.classes.collapsibleTriggerExpanded);
    this.element.setAttribute(this.attributes.ariaExpanded, 'false');
    this.element.removeAttribute(this.attributes.expanded);
  }

  collapseContent() {
    this.content.style.maxHeight = '0px';
    this.content.setAttribute(this.attributes.content, '');
    this.content.setAttribute(this.attributes.ariaHidden, 'true');
  }

  /**
   * Handles element on expand - add class and set aria expanded attribute
   */
  expandTrigger() {
    this.element.classList.add(this.classes.collapsibleTriggerExpanded);
    this.element.setAttribute(this.attributes.ariaExpanded, 'true');
  }

  /**
   * Handles content on expand - set maxheight, expanded state en aria hidden attribute
   *
   * @param {number} contentHeight - height of content element
   */
  expandContent(contentHeight) {
    this.content.style.maxHeight = `${contentHeight + 30}px`; // add 30px to fit on IOS mobile (gives back different scrollHeight)
    this.content.setAttribute(this.attributes.content, this.states.expanded);
    this.content.setAttribute(this.attributes.ariaHidden, 'false');
  }

  /**
   * Recalculate content height and set max height
   */
  recalculateContentHeight() {
    this.content = this.element.parentElement.querySelector(
      `[${this.attributes.content}]`
    );
    this.contentHeight = this.content.scrollHeight;

    // set maxheight on expanded content
    if (
      this.content.getAttribute(this.attributes.content) ===
      this.states.expanded
    ) {
      this.content.style.maxHeight = `${this.contentHeight}px`;
    }
  }

  /**
   * Get selector
   */
  static getSelector() {
    return `[${attributeBase}]`;
  }
}
