import { CSSResult, html, TemplateResult } from "lit";
import { property, queryAsync, state } from "lit/decorators.js";
import { classMap } from "lit/directives/class-map.js";
import { when } from 'lit/directives/when.js';
import UiRipple from "../../ui/effects/ripple.js";
import { ripple } from '../../ui/effects/rippleDirective.js';
import { UiElement } from "../../ui/UiElement.js";
import elementStyles from './AppNavigationItem.styles.js';
import '../../define/ui/ui-ripple.js';

export default class AppNavigationItemElement extends UiElement {
  static override get styles(): CSSResult | CSSResult[] {
    return elementStyles;
  }
  
  /**
   * When set, the button is a disabled state.
   * @attribute
   */
  @property({ reflect: true, type: Boolean }) disabled = false;

  /**
   * Whether the navigation item is selected.
   * @attribute
   */
  @property({ reflect: true, type: Boolean }) selected = false;

  @queryAsync('ui-ripple') protected ripple!: Promise<UiRipple | null>;

  @state() protected showRipple = false;

  protected readonly getRipple = (): Promise<UiRipple | null> => {
    this.showRipple = true;
    return this.ripple;
  };

  /**
   * Determines when the element has an icon in the "icon" slot.
   */
  @state() protected hasIcon: boolean;

  constructor() {
    super();

    this.hasIcon = false;

    this.actionController.cancelKeyboardEvents = true;
    this.addEventListener('keydown', this.handleKeyDown.bind(this));
    this.addEventListener('keyup', this.handleKeyUp.bind(this));
    this.addEventListener('click', this.handleClick.bind(this));
    this.addEventListener('pointerdown', this.handlePointerDown.bind(this));
    this.addEventListener('pointerup', this.handlePointerUp.bind(this));
    this.addEventListener('pointercancel', this.handlePointerCancel.bind(this));
    this.addEventListener('pointerleave', this.handlePointerLeave.bind(this));
    this.addEventListener('contextmenu', this.handleContextMenu.bind(this));
    this.addEventListener('focus', this.handleFocus.bind(this));
    this.addEventListener('blur', this.handleBlur.bind(this));
  }

  override connectedCallback(): void {
    super.connectedCallback();
    if (!this.hasAttribute('role')) {
      this.setAttribute('role', 'button');
    }
    if (!this.hasAttribute('tabindex') && !this.disabled) {
      this.setAttribute('tabindex', '0');
    }
  }

  protected async handleFocus(): Promise<void> {
    const _ripple = await this.getRipple();
    _ripple?.beginFocus();
  }

  protected async handleBlur(): Promise<void> {
    const _ripple = await this.getRipple();
    _ripple?.endFocus();
  }

  override async handleKeyDown(e: KeyboardEvent): Promise<void> {
    super.handleKeyDown(e);
    if (['Space'].includes(e.code)) {
      const _ripple = await this.getRipple();
      _ripple?.beginPress();
    }
  }

  override async handleKeyUp(e: KeyboardEvent): Promise<void> {
    super.handleKeyUp(e);
    if (['Space'].includes(e.code)) {
      const _ripple = await this.getRipple();
      _ripple?.endPress();
      this.click();
    }
  }

  /**
   * Sets the `_hasIcon` state property when the "icon" slot change event is dispatched.
   */
  protected iconSlotChange(e: Event): void {
    const slot = e.target as HTMLSlotElement;
    this.hasIcon = !!slot.assignedNodes().length;
  }

  protected override render(): TemplateResult {
    const { pressed = false } = this;
    const containerClasses = classMap({
      surface: true,
      withIcon: this.hasIcon,
      pressed,
    });
    return html`
    <div class="${containerClasses}" ${ripple(this.getRipple)}>
      <div class="container"></div>
      <div class="state"></div>
      ${when(this.showRipple, this.renderRipple)}
      <div class="content">
        ${this.renderIcon()}
        <slot></slot>
      </div>
    </div>
    `;
  }

  protected renderIcon(): TemplateResult {
    return html`<slot name="icon" @slotchange="${this.iconSlotChange}"></slot>`;
  }

  protected renderRipple = (): TemplateResult => {
    const { disabled } = this;
    return html`<ui-ripple class="ripple" ?disabled="${disabled}"></ui-ripple>`;
  };
}
