/**
 * Copyright Aquera Inc 2023
 *
 * This source code is licensed under the BSD-3-Clause license found in the
 * LICENSE file in the root directory of this source tree.
 */

import { LitElement, html, css } from 'lit';
import { customElement, property, state } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js';
import { query } from 'lit/decorators.js';
import NileElement from '../internal/nile-element';
import type { CSSResultGroup } from 'lit';
import type NileMenuItem from '../nile-menu-item/nile-menu-item';
import { styles } from './nile-side-bar-action-menu.css';
import { HasSlotController } from '../internal/slot';

/**
 * A nile-side-bar-action-menu element.
 * @summary Menus provide a list of options for the user to choose from.
 *
 * @slot - The menu's content, including menu items, menu labels, and dividers.
 *
 * @event nile-select - Emitted when a menu item is selected.
 * @fires count-changed - Indicates when the count changes
 * @slot - This element has a slot
 * @csspart button - The button
 */
@customElement('nile-side-bar-action-menu')
export class NileSideBarActionMenu extends NileElement {
  static styles: CSSResultGroup = styles;
  @state() searchValue: string = '';
  @state() searchWidth: number = 0;

  @property({ type: Boolean, reflect: true }) searchEnabled = false;
  @property({ type: Boolean, reflect: true }) customSearch = false;

  @property({ type: Boolean }) showNoResults: boolean = false;

  @property({ type: Boolean }) allowSpaceKey: boolean = false;

  @property({ type: String }) noResultsMessage: string = 'No results found';

  @query('slot:not([name])') defaultSlot!: HTMLSlotElement;

  private readonly hasSlotController = new HasSlotController(
    this,
    'menu__footer',
    'menu__header'
  );

  connectedCallback() {
    super.connectedCallback();
    this.setAttribute('role', 'menu');
  }

  private handleClick(event: MouseEvent) {
    const target = event.target as HTMLElement;
    const item = target.closest('nile-menu-item');

    if (item?.hasSubMenu) {
      return;
    }

    if (!item || item.disabled) {
      return;
    }

    if (item.type === 'checkbox') {
      item.checked = !item.checked;
    }

    this.emit('nile-select', { value: item.value });
  }

  private handleKeyDown(event: KeyboardEvent) {
    if (event.key === 'Enter') {
      const item = this.getCurrentItem();
      event.preventDefault();
      item?.click();
    }

    if (!this.allowSpaceKey && event.key === ' ') {
      event.preventDefault();
    }

    if (['ArrowDown', 'ArrowUp', 'Home', 'End'].includes(event.key)) {
      const items = this.getAllItems();
      const activeItem = this.getCurrentItem();
      let index = activeItem ? items.indexOf(activeItem) : 0;

      if (items.length > 0) {
        event.preventDefault();

        if (event.key === 'ArrowDown') index++;
        else if (event.key === 'ArrowUp') index--;
        else if (event.key === 'Home') index = 0;
        else if (event.key === 'End') index = items.length - 1;

        if (index < 0) index = items.length - 1;
        if (index > items.length - 1) index = 0;

        this.setCurrentItem(items[index]);
        items[index].focus();
      }
    }
  }

  private handleMouseDown(event: MouseEvent) {
    const target = event.target as HTMLElement;

    if (this.isMenuItem(target)) {
      this.setCurrentItem(target as NileMenuItem);
    }
  }

  private handleSlotChange() {
    const items = this.getAllItems();

    if (items.length > 0) {
      this.setCurrentItem(items[0]);
    }
  }

  private isMenuItem(item: HTMLElement) {
    return (
      item.tagName.toLowerCase() === 'nile-menu-item' ||
      ['menuitem', 'menuitemcheckbox', 'menuitemradio'].includes(
        item.getAttribute('role') ?? ''
      )
    );
  }

  getAllItems() {
    return [...this.defaultSlot.assignedElements({ flatten: true })].filter(
      (el: HTMLElement) => {
        if (!this.isMenuItem(el)) {
          return false;
        }
        return true;
      }
    ) as NileMenuItem[];
  }

  getCurrentItem() {
    return this.getAllItems().find(i => i.getAttribute('tabindex') === '0');
  }

  setCurrentItem(item: NileMenuItem) {
    const items = this.getAllItems();
    items.forEach(i => {
      i.setAttribute('tabindex', i === item ? '0' : '-1');
    });
  }

  private handleSearchChange(e: any) {
    let items = this.getAllItems();
    let searchValue = e.target.value;
    if (this.customSearch) {
      this.emit('nile-search-value', { value: searchValue });
      return;
    }

    let counter = 0;
    items.map(curr_item => {
      this.searchWidth = Math.max(this.searchWidth, curr_item.offsetWidth);
      if (
        curr_item.innerText.toLowerCase().includes(e.target.value.toLowerCase())
      ) {
        curr_item.style.display = 'block';
        counter++;
      } else {
        curr_item.style.display = 'none';
      }
    });
    this.showNoResults = counter === 0;
  }

  render() {
    return html`
      <slot name="menu__pre-header" part="menu__pre-header" @click=${(e: Event) => {
            e.stopPropagation();
          }}></slot>
      ${this.searchEnabled
        ? html`<div class="search__wrapper" part="search__wrapper">
            <nile-input
              size="medium"
              .value=${this.searchValue}
              placeholder="Search..."
              @nile-input=${this.handleSearchChange}
              part="menu__input"
              clearable
            >
              <nile-icon
                name="var(--nile-icon-search, var(--ng-icon-search-lg))"
                method="var(--nile-svg-method-fill, var(--ng-svg-method-stroke))"
                slot="suffix"
              ></nile-icon>
            </nile-input>
          </div>`
        : html``}
      <div part="menu__items-wrapper" class="menu__items-wrapper">
        ${this.showNoResults
          ? html` <nile-menu-item>${this.noResultsMessage}</nile-menu-item> `
          : ''}

        <slot
          name="menu__header"
          part="menu__header"
          @click=${(e: Event) => {
            e.stopPropagation();
          }}
        ></slot>
        <slot
          class="menu__item-wrapper"
          @slotchange=${this.handleSlotChange}
          @click=${this.handleClick}
          @keydown=${this.handleKeyDown}
          @mousedown=${this.handleMouseDown}
        ></slot>
        </div>
        ${this.hasSlotController.test('menu__footer')
          ? html` <slot
              part="menu__footer"
              name="menu__footer"
              class="menu__footer"
            ></slot>`
          : null}

    `;
  }
}

export default NileSideBarActionMenu;

declare global {
  interface HTMLElementTagNameMap {
    'nile-side-bar-action-menu': NileSideBarActionMenu;
  }
}
