export interface ShortcutLinks {
  shortcut_title: string;
  shortcut_url: string;
  shortcut_is_external: string;
  shortcut_aria_label: string;
}

export interface DesktopLinks {
  label: string;
  labelExtra: string;
  url: string;
  description: string;
  toggleOff: string;
  menuLinkId: string;
  isExternal: string;
  isActive: string;
  shortcutsTitle: string;
  closeButtonTitle: string;
  items: Array<DesktopLinks>;
  shortcuts: Array<ShortcutLinks>;
}

import {ColorVariant} from '../../../utils/utils';
import {Component, h, Element, Prop, State, Watch, EventEmitter, Event, Listen} from '@stencil/core';
import ResizeObserver from 'resize-observer-polyfill';

@Component({
  tag: 'hy-desktop-menu-links',
  styleUrl: 'hy-desktop-menu-links.scss',
  shadow: true,
})
export class HyDesktopMenuLinks {
  @Element() el: HTMLElement;
  /*
  First level menu links to be displayed on Desktop screens.
  * */
  @Prop() dataDesktopLinks: DesktopLinks[] | string;
  private _dataDesktopLinks: DesktopLinks[];
  @State() firstLevelLinksList: Array<object> = [];
  @State() menuLinkItems: Array<object> = [];
  @State() hasToolbar: boolean = false;
  @State() isDesktopMenuOpen: boolean = false;
  @State() currenOpenMenuId: number = 0;

  @Event() menuDesktopToggled: EventEmitter;

  private _submenuLeftMargin: number = 32;
  private _headerBorderOffset: number = 0;

  private _hoverTimer = null;
  private _fadeOutTimer = null;

  private ro: ResizeObserver;

  @Watch('dataDesktopLinks')
  dataDesktopLinksWatcher(data: DesktopLinks[] | string) {
    this._dataDesktopLinks = typeof data === 'string' ? JSON.parse(data) : data;
  }

  componentWillLoad() {
    this.dataDesktopLinksWatcher(this.dataDesktopLinks);
  }

  removeBackdropShadow(size: number) {
    // Close backdrop shadow if the screen is < 1200px
    if (size < 1200) {
      this.showBackdropShadow();
    }
  }

  showBackdropShadow(state = 'close', top = 0) {
    let hyHeader = this.el.closest('.hy-site-header') as HTMLElement;
    if (!hyHeader) return;

    let hyBackdropDiv = hyHeader.children[0] as HTMLElement;

    if (hyBackdropDiv) {
      if (state === 'open') {
        const windowHeight = window.outerHeight;

        hyBackdropDiv.style.height = `${windowHeight}px`;
        hyBackdropDiv.style.top = `${top}px`;
        hyBackdropDiv.style.position = 'absolute';
        hyBackdropDiv.classList.add('is-active');
      } else {
        hyBackdropDiv.removeAttribute('style');
        hyBackdropDiv.classList.remove('is-active');
      }
    }
  }

  showPanel(id) {
    // Close menu lang menu if it's open
    this.menuDesktopToggled.emit();

    clearTimeout(this._fadeOutTimer);

    // Open desktop menu panel
    this.isDesktopMenuOpen = true;

    const menuItems = this.el.shadowRoot.querySelectorAll(`.desktop-menu-link`);
    const menuPanelItems = this.el.shadowRoot.querySelectorAll('.hy-desktop-menu-panel'); // all panels
    const activeMenuItem = this.el.shadowRoot.querySelector(`.desktop-menu-link[link-id="${id}"]`) as HTMLElement;
    const activeMenuItemSibling = activeMenuItem.nextElementSibling as HTMLElement; // current panel

    // Reset elements by removing the active classes.
    menuItems.forEach((item) => {
      item.classList.remove('desktop-menu-link--is-active');
      item.setAttribute('aria-expanded', 'false');
    });
    menuPanelItems.forEach((item) => {
      item.classList.remove('hy-desktop-menu-panel--is-active');
      item.classList.remove('hy-desktop-menu-panel--overflow');
      item.setAttribute('aria-hidden', 'true');
      (item as HTMLElement).style.transition = 'none';
      (item as HTMLElement).style.opacity = '0';
    });

    // Add active classes to the currently active item and its sibling element.
    activeMenuItem.classList.add('desktop-menu-link--is-active');
    activeMenuItem.setAttribute('aria-expanded', 'true');
    activeMenuItemSibling.classList.add('hy-desktop-menu-panel--is-active');

    (activeMenuItemSibling as HTMLElement).style.opacity = '1';

    if (this.hasToolbar) {
      activeMenuItemSibling.classList.add('hy-desktop-menu-panel--is-active--has-toolbar');
    }
    activeMenuItemSibling.setAttribute('aria-hidden', 'false');

    //Hide is-active-trail underlining
    const activeTrailMenuItem = this.el.shadowRoot.querySelector(
      `.desktop-menu-link__label--is-active-trail`
    ) as HTMLElement;
    if (activeTrailMenuItem) {
      activeTrailMenuItem.classList.add('desktop-menu-link__label--is-active-trail--disabled');
    }

    // Add panels top value automatically with the correct header height
    let topOffset = window.pageYOffset;
    const bodyElementClasses = document.body.classList;
    const headerElement = document.querySelector('hy-site-header');
    const headerShadowRootElement = headerElement.shadowRoot.querySelector('header');
    let headerElementHeight = headerShadowRootElement.offsetHeight + this._headerBorderOffset;
    let headerElementOffset = headerShadowRootElement.offsetTop;

    let panelElementOffset = headerElementHeight + headerElementOffset;

    // Add shadow backdrop
    let rect = activeMenuItemSibling.getBoundingClientRect();
    let backdropOffset = headerElementHeight + headerElementOffset + rect.height;
    let backdropToolbarOffset = backdropOffset - 79;

    window.addEventListener('scroll', () => {
      topOffset = window.pageYOffset;

      if (topOffset === 0) {
        activeMenuItemSibling.style.top = `${panelElementOffset}px`;
      }

      if (headerShadowRootElement.classList.contains('hy-site-header--sticky-active') && topOffset > 0) {
        if (bodyElementClasses.contains('toolbar-horizontal')) {
          activeMenuItemSibling.style.top = `${headerElementHeight}px`;

          if (this.isDesktopMenuOpen) {
            this.showBackdropShadow('open', backdropToolbarOffset);
          }
        }
      }
    });

    if (headerShadowRootElement.classList.contains('hy-site-header--sticky-active') && topOffset > 0) {
      if (bodyElementClasses.contains('toolbar-horizontal')) {
        activeMenuItemSibling.style.top = `${headerElementHeight}px`;

        if (this.isDesktopMenuOpen) {
          this.showBackdropShadow('open', backdropToolbarOffset);
        }
      } else {
        activeMenuItemSibling.style.top = `${panelElementOffset}px`;
        this.showBackdropShadow('open', backdropOffset);
      }
    } else {
      activeMenuItemSibling.style.top = `${panelElementOffset}px`;
      this.showBackdropShadow('open', backdropOffset);
    }

    // Position submenu block under the activated top menu item.
    const menuPanelLeftPosition = activeMenuItem.offsetLeft - this._submenuLeftMargin;
    activeMenuItemSibling.style.paddingLeft = `${menuPanelLeftPosition}px`;

    // Position shortcuts block.
    let shortcutsDiv = activeMenuItemSibling.querySelectorAll('ul.shortcuts-panel')[0] as HTMLElement; // shortcuts block
    if (shortcutsDiv) {
      let subMenuDiv = activeMenuItemSibling.querySelectorAll(
        '.hy-desktop-menu-panel__desktop-menu__menu-items'
      )[0] as HTMLElement; // 2nd level menu block

      let spaceLeftAfterSubmenu = subMenuDiv.getBoundingClientRect().right + shortcutsDiv.offsetWidth;
      if (spaceLeftAfterSubmenu >= document.body.scrollWidth) {
        // Shortcuts should be placed to the left.
        let shortcutsLeftPosition = subMenuDiv.getBoundingClientRect().left - shortcutsDiv.offsetWidth;
        shortcutsDiv.style.left = shortcutsLeftPosition.toString().concat('px');
      } else {
        // Shortcuts should be placed to the right.
        let shortcutsLeftPosition = subMenuDiv.getBoundingClientRect().right;
        shortcutsDiv.style.left = shortcutsLeftPosition.toString().concat('px');
      }
    }
  }

  closePanel(fadeOut = false) {
    this.isDesktopMenuOpen = false;
    this.currenOpenMenuId = 0;
    this.showBackdropShadow();
    this.clearPanelItemsStatus(fadeOut);
    clearTimeout(this._hoverTimer);
  }

  clearPanelItemsStatus(fadeOut = false) {
    const menuItems = this.el.shadowRoot.querySelectorAll(`.desktop-menu-link`);
    const menuPanelItems = this.el.shadowRoot.querySelectorAll('.hy-desktop-menu-panel');

    //Show is-active-trail underlining
    const activeTrailMenuItem = this.el.shadowRoot.querySelector(
      `.desktop-menu-link__label--is-active-trail`
    ) as HTMLElement;
    if (activeTrailMenuItem) {
      activeTrailMenuItem.classList.remove('desktop-menu-link__label--is-active-trail--disabled');
    }

    // Reset elements by removing the active classes.
    menuItems.forEach((item) => {
      item.classList.remove('desktop-menu-link--is-active');
      item.setAttribute('aria-expanded', 'false');
    });

    if (fadeOut) {
      menuPanelItems.forEach((item) => {
        (item as HTMLElement).style.opacity = '0';
        (item as HTMLElement).style.transition = 'opacity 1s';
      });

      this._fadeOutTimer = setTimeout(() => {
        menuPanelItems.forEach((item) => {
          item.classList.remove('hy-desktop-menu-panel--is-active');
          item.setAttribute('aria-hidden', 'true');
        });
      }, 350);
    } else {
      menuPanelItems.forEach((item) => {
        item.classList.remove('hy-desktop-menu-panel--is-active');
        item.setAttribute('aria-hidden', 'true');
        (item as HTMLElement).style.opacity = '0';
        (item as HTMLElement).style.transition = 'none';
      });
    }
  }

  handleDesktopMenuClose(event) {
    let fadeOut = true;
    this.closePanel(fadeOut);

    event.stopPropagation();
  }

  // CLose the desktop menu panel if user opens the language menu.
  @Listen('menuLanguageToggled', {target: 'document'})
  menuLanguageToggled() {
    let fadeOut = true;
    this.closePanel(fadeOut);
  }

  // Close the desktop menu panel if user opens University main menu
  @Listen('universityMainMenuToggled', {target: 'document'})
  universityMainMenuPanelToggled() {
    let fadeOut = true;
    this.closePanel(fadeOut);
  }

  // Close the desktop menu panel if user opens search panel
  @Listen('searchPanelToggled', {target: 'document'})
  searchPanelToggled() {
    let fadeOut = true;
    this.closePanel(fadeOut);
  }

  // CLose the desktop menu panel if user scrolls Sticky Header till the very top.
  @Listen('headerScrollUp', {target: 'document'})
  headerScrollUp() {
    let fadeOut = false;
    this.closePanel(fadeOut);
  }

  handleDesktopMenuEnter(event, id) {
    clearTimeout(this._fadeOutTimer);

    this._hoverTimer = setTimeout(() => {
      const activeMenuItem = this.el.shadowRoot.querySelector(`.desktop-menu-link[link-id="${id}"]`) as HTMLElement;

      // Set focus to the button.
      if (activeMenuItem !== null) activeMenuItem.focus();

      this.currenOpenMenuId = id;
      this.showPanel(id);
    }, 350);

    event.stopPropagation();
  }

  handleDesktopMenuLeave(event) {
    let leaveEvent = event as MouseEvent;

    let hyHeader = this.el.closest('.hy-site-header') as HTMLElement;
    const headerHeight = hyHeader.offsetTop + hyHeader.offsetHeight;

    if (leaveEvent.clientY < headerHeight - 4) {
      this.closePanel();
    }

    event.stopPropagation();
  }

  /*
    Close the panel if mouse is moving over the menu label.
  * */
  handleDesktopMenuMove(event, id) {
    if (this.isDesktopMenuOpen) {
      let moveEvent = event as MouseEvent;

      const activeMenuItem = this.el.shadowRoot.querySelector(
        `.desktop-menu-link[link-id="${id}"] .desktop-menu-link__label`
      ) as HTMLElement;
      let topBorder = activeMenuItem.getClientRects()[0].top;

      if (this.currenOpenMenuId == id) {
        // Mouse moving around the same menu link
        if (moveEvent.clientY < topBorder - 4) {
          this.closePanel();
        }
      }

      event.stopPropagation();
    }
  }

  handleDesktopMenuFocus(event, id) {
    if (this.currenOpenMenuId != id) {
      this.currenOpenMenuId = id;
      this.showPanel(id);
    }

    event.stopPropagation();
  }

  handleDesktopMenuClick(event, id) {
    if (!this.isDesktopMenuOpen) {
      this.currenOpenMenuId = id;
      this.showPanel(id);
    } else {
      this.handleDesktopMenuClose(event);
    }

    event.stopPropagation();
  }

  componentDidLoad() {
    // Set the browser resize observer to gather information about browser width.
    this.ro = new ResizeObserver((entries) => {
      for (const entry of entries) {
        this.removeBackdropShadow(entry.contentRect.width);
      }
    });
    this.ro.observe(document.body);

    let hyToolbar = document.querySelectorAll('#toolbar-administration')[0];
    if (hyToolbar) {
      this.hasToolbar = true;
    }

    const links = this._dataDesktopLinks as Array<DesktopLinks>;

    let menuLinkItems = [];

    if (links) {
      links.map(
        ({
          menuLinkId: id,
          shortcuts,
          items,
          url,
          description,
          toggleOff,
          label,
          labelExtra,
          isExternal,
          isActive,
          shortcutsTitle,
          closeButtonTitle,
        }) => {
          let classAttributes = [
            'desktop-menu-link',
            isActive === 'true' ? 'desktop-menu-link--is-active-trail' : '',
          ].join(' ');
          let classAttributesLabel = [
            'desktop-menu-link__label',
            isActive === 'true' ? 'desktop-menu-link__label--is-active-trail' : '',
          ].join(' ');
          let target = isExternal ? '_blank' : '_self';

          if (toggleOff == 'true') {
            menuLinkItems.push(
              <li onMouseEnter={(e) => this.handleDesktopMenuClose(e)}>
                <a aria-current={label} href={url} target={target} class="desktop-menu-link toggle" menu-link-id={id}>
                  {labelExtra ? <span class="label">{labelExtra}</span> : <span class="label">{label}</span>}
                </a>
              </li>
            );
          } else {
            menuLinkItems.push(
              <li>
                <button
                  type="button"
                  class={classAttributes}
                  link-id={id}
                  onMouseDown={(e) => this.handleDesktopMenuClick(e, id)}
                  onFocus={(e) => this.handleDesktopMenuFocus(e, id)}
                  onMouseEnter={(e) => this.handleDesktopMenuEnter(e, id)}
                  onMouseMove={(e) => this.handleDesktopMenuMove(e, id)}
                  aria-expanded="false"
                >
                  <span class={classAttributesLabel}>{label}</span>
                  <hy-icon icon={'hy-icon-caret-down'} size={32} />
                </button>
                <div class="hy-desktop-menu-panel" aria-hidden="true">
                  <div class="hy-desktop-menu-panel__desktop-menu">
                    <div class="hy-desktop-menu-panel__desktop-menu__menu-items">
                      <a
                        aria-current={label}
                        href={url}
                        target={target}
                        class="hy-desktop-menu-panel__desktop-menu__first-level-menu-item"
                        menu-link-id={id}
                      >
                        <span class="heading-icon">
                          <hy-icon icon={'hy-icon-arrow-right'} size={40} />
                        </span>
                        {labelExtra ? <span class="label">{labelExtra}</span> : <span class="label">{label}</span>}
                        {description && <span class="description">{description}</span>}
                      </a>
                      <ul class={'hy-desktop-menu-panel__desktop-menu__second-level-menu'} menu-link-id={id}>
                        {items.map(({label, url, isExternal}) => {
                          let subitemTarget = isExternal ? '_blank' : '_self';
                          return (
                            <li>
                              <a href={url} target={subitemTarget}>
                                <span class="heading-icon">
                                  <hy-icon icon={'hy-icon-caret-right'} size={12} />
                                </span>
                                <span class="label">{label}</span>
                                {isExternal && (
                                  <span class="external-icon">
                                    <hy-icon icon={'hy-icon-arrow-right'} size={12} />
                                  </span>
                                )}
                              </a>
                            </li>
                          );
                        })}
                      </ul>
                    </div>
                    {shortcuts.length > 0 && (
                      <ul class="shortcuts-panel">
                        <h2 class="shortcuts-panel__title">{shortcutsTitle}</h2>
                        {shortcuts.map(
                          ({shortcut_title, shortcut_url, shortcut_is_external, shortcut_aria_label}, index) => {
                            let target = shortcut_is_external ? '_blank' : '_self';

                            let shortcutClass = [
                              'shortcuts-panel__shortcut-item',
                              index == 0 ? 'shortcuts-panel__shortcut-item__first' : '',
                            ].join(' ');

                            return (
                              <li class={shortcutClass}>
                                <a
                                  aria-current={shortcut_aria_label}
                                  href={shortcut_url}
                                  class="shortcut-item__link"
                                  target={target}
                                  aria-label={shortcut_aria_label}
                                >
                                  <span class="label">{shortcut_title}</span>
                                  <span class="icon">
                                    <hy-icon icon={'hy-icon-arrow-right'} size={24} />
                                  </span>
                                </a>
                              </li>
                            );
                          }
                        )}
                      </ul>
                    )}
                  </div>
                  <button
                    onClick={(e) => this.handleDesktopMenuClose(e)}
                    class={{
                      'hy-desktop-menu-panel__panel-toggle': true,
                    }}
                    aria-label="Close menu"
                  >
                    <span class="hy-desktop-menu-panel__panel-toggle__label">
                      <span class="hy-desktop-menu-panel__panel-toggle__label__title">{closeButtonTitle}</span>
                      <hy-icon icon={'hy-icon-remove'} size={20} fill={ColorVariant.black} />
                    </span>
                  </button>
                </div>
              </li>
            );
          }
        }
      );
    }

    this.menuLinkItems = menuLinkItems;
  }

  render() {
    return (
      <nav
        role={'navigation'}
        class="hy-site-header__menu-desktop"
        onMouseLeave={(e) => this.handleDesktopMenuClose(e)}
      >
        <ul class="hy-site-header__menu-desktop-container">{this.menuLinkItems}</ul>
      </nav>
    );
  }
}