import {
  Component,
  Element,
  Event,
  EventEmitter,
  h,
  Method,
  Prop,
  State,
} from '@stencil/core';

import { isNil, isUndefined } from '../../../../utils/unit';
import { PlayerProps } from '../../../core/player/PlayerProps';
import { withComponentRegistry } from '../../../core/player/withComponentRegistry';
import { withPlayerContext } from '../../../core/player/withPlayerContext';

/**
 * A collection of radio buttons describing a set of related options. Only one radio button in a
 * group can be selected at the same time.
 *
 * ## Visual
 *
 * <img
 *   src="https://raw.githubusercontent.com/vime-js/vime/master/packages/core/src/components/ui/settings/menu-radio-group/menu-radio-group.png"
 *   alt="Vime settings menu radio group component"
 * />
 */
@Component({
  tag: 'vm-menu-item',
  styleUrl: 'menu-item.css',
  shadow: true,
})
export class MenuItem {
  private menuItem?: HTMLDivElement;

  @Element() host!: HTMLVmMenuItemElement;

  @State() showTapHighlight = false;

  /**
   * The `id` attribute of the item.
   */
  @Prop() identifier?: string;

  /**
   * Whether the item is displayed or not.
   */
  @Prop() hidden = false;

  /**
   * The label/title of the item.
   */
  @Prop() label!: string;

  /**
   * If the item has a popup menu, then this should be a reference to it.
   */
  @Prop() menu?: HTMLVmMenuElement;

  /**
   * If the item has a popup menu, this indicates whether the menu is open or not. Sets the
   * `aria-expanded` property.
   */
  @Prop() expanded?: boolean;

  /**
   * If this item is to behave as a radio button, then this property determines whether the
   * radio is selected or not. Sets the `aria-checked` property.
   */
  @Prop() checked?: boolean;

  /**
   * This can provide additional context about some underlying state of the item. For example, if
   * the menu item opens/closes a submenu with options, the hint could be the currently selected
   * option. If `checked` is defined, `hint` is not shown.
   */
  @Prop() hint?: string;

  /**
   * This can provide additional context about the value of a menu item. For example, if the item
   * is a radio button for a set of video qualities, the badge could describe whether the quality
   * is UHD, HD etc. If `hint` is shown, `badge` is not shown.
   */
  @Prop() badge?: string;

  /**
   * The name of the checkmark icon to resolve from the icon library.
   */
  @Prop() checkIcon?: string = 'check';

  /**
   * The name of an icon library to use. Defaults to the library defined by the `icons` player
   * property.
   */
  @Prop() icons?: string;

  /** @internal */
  @Prop() isTouch: PlayerProps['isTouch'] = false;

  /**
   * Emitted when the item is focused.
   */
  @Event() vmFocus!: EventEmitter<void>;

  /**
   * Emitted when the item loses focus.
   */
  @Event() vmBlur!: EventEmitter<void>;

  constructor() {
    withComponentRegistry(this);
    withPlayerContext(this, ['isTouch']);
  }

  /**
   * Focuses the menu item.
   */
  @Method()
  async focusItem() {
    this.menuItem?.focus();
  }

  /**
   * Removes focus from the menu item.
   */
  @Method()
  async blurItem() {
    this.menuItem?.blur();
  }

  /**
   * Returns the height of the menu item.
   */
  @Method()
  async getHeight() {
    return parseFloat(
      this.menuItem ? window.getComputedStyle(this.menuItem).height : '0',
    );
  }

  private onClick() {
    if (!isNil(this.menu)) this.menu.active = !this.expanded;
  }

  private onFocus() {
    this.vmFocus.emit();
  }

  private onBlur() {
    this.vmBlur.emit();
  }

  private onTouchStart() {
    this.showTapHighlight = true;
  }

  private onTouchEnd() {
    setTimeout(() => {
      this.showTapHighlight = false;
    }, 100);
  }

  private onMouseLeave() {
    this.menuItem?.blur();
  }

  render() {
    const isCheckedDefined = !isUndefined(this.checked);
    const isMenuDefined = !isUndefined(this.menu);
    const hasExpanded = this.expanded ? 'true' : 'false';
    const isChecked = this.checked ? 'true' : 'false';
    const showCheckedIcon = isCheckedDefined && !isUndefined(this.checkIcon);
    const showLeftNavArrow = isMenuDefined && this.expanded;
    const showRightNavArrow = isMenuDefined && !this.expanded;
    const showHint =
      !isUndefined(this.hint) &&
      !isCheckedDefined &&
      (!isMenuDefined || !this.expanded);
    const showBadge =
      !isUndefined(this.badge) && !showHint && !showRightNavArrow;
    const hasSpacer = showHint || showRightNavArrow;

    return (
      <div
        class={{
          menuItem: true,
          notTouch: !this.isTouch,
          tapHighlight: this.showTapHighlight,
          showDivider: isMenuDefined && (this.expanded ?? false),
        }}
        id={this.identifier}
        role={isCheckedDefined ? 'menuitemradio' : 'menuitem'}
        tabindex="0"
        aria-label={this.label}
        aria-hidden={this.hidden ? 'true' : 'false'}
        aria-haspopup={isMenuDefined ? 'true' : undefined}
        aria-controls={this.menu?.identifier ?? this.menu?.id}
        aria-expanded={isMenuDefined ? hasExpanded : undefined}
        aria-checked={isCheckedDefined ? isChecked : undefined}
        onClick={this.onClick.bind(this)}
        onFocus={this.onFocus.bind(this)}
        onBlur={this.onBlur.bind(this)}
        onTouchStart={this.onTouchStart.bind(this)}
        onTouchEnd={this.onTouchEnd.bind(this)}
        onMouseLeave={this.onMouseLeave.bind(this)}
        ref={el => {
          this.menuItem = el;
        }}
      >
        {showCheckedIcon && (
          <vm-icon name={this.checkIcon!} library={this.icons} />
        )}
        {showLeftNavArrow && <span class="arrow left" />}
        {this.label}
        {hasSpacer && <span class="spacer" />}
        {showHint && <span class="hint">{this.hint}</span>}
        {showBadge && <span class="badge">{this.badge}</span>}
        {showRightNavArrow && <span class="arrow right" />}
      </div>
    );
  }
}
