import KTData from '../../helpers/data';
import KTDom from '../../helpers/dom';
import KTEventHandler from '../../helpers/event-handler';
import KTComponent from '../component';
import { KTAccordionInterface, KTAccordionConfigInterface } from './types';

export class KTAccordion extends KTComponent implements KTAccordionInterface {
  protected override _name: string = 'accordion';
  protected override _defaultConfig: KTAccordionConfigInterface = {
    hiddenClass: 'hidden',
    activeClass: 'active',
    expandAll: false
  };
  protected override _config: KTAccordionConfigInterface = this._defaultConfig;
  protected _accordionElements: NodeListOf<HTMLElement>;

  constructor(element: HTMLElement, config?: KTAccordionConfigInterface) {
    super();

    if (KTData.has(element as HTMLElement, this._name)) return;

    this._init(element);
    this._buildConfig(config);
    this._handlers();
  }

  protected _handlers(): void {
    KTEventHandler.on(this._element, '[data-accordion-toggle]', 'click', (event: Event, target: HTMLElement) => {
      event.preventDefault();
      const accordionElement = target.closest('[data-accordion-item]') as HTMLElement;

      if (accordionElement) this._toggle(accordionElement);
    });
  }

  protected _toggle(accordionElement: HTMLElement): void {
    const payload = { cancel: false };
    this._fireEvent('toggle', payload);
    this._dispatchEvent('toggle', payload);
    if (payload.cancel === true) {
      return;
    }

    if (accordionElement.classList.contains('active')) {
      this._hide(accordionElement);
    } else {
      this._show(accordionElement);
    }
  }

  protected _show(accordionElement: HTMLElement): void {
    if (accordionElement.hasAttribute('animating') || accordionElement.classList.contains(this._getOption('activeClass') as string)) return;

    const toggleElement = KTDom.child(accordionElement, '[data-accordion-toggle]');
    if (!toggleElement) return;

    const contentElement = KTDom.getElement(toggleElement.getAttribute('data-accordion-toggle'));
    if (!contentElement) return;

    const payload = { cancel: false };
    this._fireEvent('show', payload);
    this._dispatchEvent('show', payload);
    if (payload.cancel === true) {
      return;
    }

    if (this._getOption('expandAll') as boolean === false) {
      this._hideSiblings(accordionElement);
    }

    accordionElement.setAttribute('aria-expanded', 'true');
    accordionElement.classList.add(this._getOption('activeClass') as string);

    accordionElement.setAttribute('animating', 'true');
    contentElement.classList.remove(this._getOption('hiddenClass') as string);
    contentElement.style.height = `0px`;
    KTDom.reflow(contentElement);
    contentElement.style.height = `${contentElement.scrollHeight}px`;

    KTDom.transitionEnd(contentElement, () => {
      accordionElement.removeAttribute('animating');
      contentElement.style.height = ''

      this._fireEvent('shown');
      this._dispatchEvent('shown');
    });
  }

  protected _hide(accordionElement: HTMLElement): void {
    if (accordionElement.hasAttribute('animating') || !accordionElement.classList.contains(this._getOption('activeClass') as string)) return;

    const toggleElement = KTDom.child(accordionElement, '[data-accordion-toggle]');
    if (!toggleElement) return;

    const contentElement = KTDom.getElement(toggleElement.getAttribute('data-accordion-toggle'));
    if (!contentElement) return;

    const payload = { cancel: false };
    this._fireEvent('hide', payload);
    this._dispatchEvent('hide', payload);
    if (payload.cancel === true) {
      return;
    }

    accordionElement.setAttribute('aria-expanded', 'false');    

    contentElement.style.height = `${contentElement.scrollHeight}px`;
    KTDom.reflow(contentElement);
    contentElement.style.height = '0px';
    accordionElement.setAttribute('animating', 'true');

    KTDom.transitionEnd(contentElement, () => {
      accordionElement.removeAttribute('animating');
      accordionElement.classList.remove(this._getOption('activeClass') as string);
      contentElement.classList.add(this._getOption('hiddenClass') as string);

      this._fireEvent('hidden');
      this._dispatchEvent('hidden');
    });
  }

  protected _hideSiblings(accordionElement: HTMLElement): void {
    const siblings = KTDom.siblings(accordionElement);

    siblings?.forEach((sibling) => {
      this._hide(sibling as HTMLElement);
    });
  }

  public show(accordionElement: HTMLElement): void {
    this._show(accordionElement);
  }

  public hide(accordionElement: HTMLElement): void {
    this._hide(accordionElement);
  }

  public toggle(accordionElement: HTMLElement): void {
    this._toggle(accordionElement);
  }

  public static getInstance(element: HTMLElement): KTAccordion {
    if (!element) return null;

		if (KTData.has(element, 'accordion')) {
			return KTData.get(element, 'accordion') as KTAccordion;
		}

		if (element.getAttribute('data-accordion') === "true") {
			return new KTAccordion(element);
		}

    return null;
  }

  public static getOrCreateInstance(element: HTMLElement, config?: KTAccordionConfigInterface): KTAccordion {
    return this.getInstance(element) || new KTAccordion(element, config);
  }

  public static createInstances(): void {
    const elements = document.querySelectorAll('[data-accordion="true"]');

    elements.forEach((element) => {
      new KTAccordion(element as HTMLElement);
    });
  }

  public static init(): void {
    KTAccordion.createInstances();
  }
}
