import { bindable, child, computedFrom, customElement, Parser, processContent } from "aurelia-framework";
import { IMenuItems } from "../menu/ui-menus";
import { UIDrop } from "../shared/ui-drop";
import { UIInternal } from "../../utils/ui-internal";
import { KeyboardShortcut } from "../../constants/constants";

@customElement("ui-button")
export class UIButton {
  /**
   * Button icon
   */
  @bindable() public icon: string = "";
  /**
   * Href for link type buttons
   */
  @bindable() public href: string = "";
  /**
   * Button label, used when button had dropdown
   */
  @bindable() public label: string;
  /**
   * Button size
   */
  @bindable() public size: "nm" | "sm" | "md" | "lg" = "nm";
  /**
   * Button type
   */
  @bindable() public type: "default" | "outline" | "solid" | "tool" | "ghost" = "default";
  /**
   * Button id
   */
  @bindable() public id: string = "";

  /**
   * Busy indicator
   */
  @bindable() public busy: boolean = false;
  /**
   * Active button
   */
  @bindable() public active: boolean = false;
  /**
   * Disabled
   */
  @bindable() public disabled: boolean | string | Function = false;

  /**
   * Menu items to create auto dropdown
   */
  @bindable() public menuItems: IMenuItems[] | (() => IMenuItems[]);

  @bindable() public buttonVisible: boolean | string | ((obj: { record: KeyValue }) => void);

  @bindable() protected showCaret: boolean = true;
  @bindable() protected current: AnyObject;

  @bindable() protected textColor: string;
  @bindable() protected backgroundColor: string;

  @bindable() public keyboardShortcut: string | string[];

  /*** Start private props ***/
  @child("div.ui-drop")
  protected elDropdown: Element;
  protected hasDrop: boolean = false;
  protected dropEl: UIDrop;
  protected badgeEl: HTMLAnchorElement;
  protected split: boolean = false;
  private elDisabled: boolean = false;
  /*** End private props ***/

  constructor(public element: Element, private parser: Parser) {
    if (element.hasAttribute("icon-hilight")) {
      element.classList.add("ui-btn__icon--hilight");
    }
    if (element.hasAttribute("icon-end")) {
      element.classList.add("ui-btn__icon--end");
    }
    if (element.hasAttribute("icon-top")) {
      element.classList.add("ui-btn__icon--top");
    }
    if (element.hasAttribute("no-caret")) {
      element.classList.add("ui-btn__caret--hide");
    }
    if (element.hasAttribute("block")) {
      element.classList.add("ui-btn--block");
    }
    this.split = element.hasAttribute("split");
  }

  @computedFrom("disabled", "elDisabled")
  get isDisabled(): boolean {
    if (typeof (this.disabled) == 'boolean') {
      return this.disabled || this.elDisabled;
    } else if (typeof (this.disabled) == 'function') {
      return this.disabled(this.current) || this.elDisabled;
    } else if (typeof (this.disabled) == 'string' && this.disabled.trim() != '') {
      const expression = this.parser.parse(this.disabled);
      const scope = {
        bindingContext: { record: this.current },
        overrideContext: null
      };
      return expression.evaluate(scope);
    }
  }

  @computedFrom("disabled", "elDisabled")
  get tabIndex(): number {
    if (this.isDisabled || this.busy) return -1;
    return 0;
  }

  @computedFrom("textColor", "backgroundColor")
  get style(): string {
    if (this.textColor == null && this.backgroundColor == null) {
      return null;
    }
    let str: string = "";
    if (this.textColor != null) {
      str += `color:${this.textColor};`;
    }
    if (this.backgroundColor != null) {
      str += `background-color:${this.backgroundColor};`;
    }
    return str;
  }

  public disable(disabled: boolean): void {
    this.elDisabled = disabled;
  }

  protected attached() {
    if (this.keyboardShortcut?.length > 0) {
      this.handleShortcutsEvent = async (event) => await this.handleShortcuts.call(this, event);
      document.addEventListener("keydown", this.handleShortcutsEvent, false);
    }
    UIInternal.queueTask(() => {
      this.hasDrop = !!this.elDropdown || !!this.dropEl;
      if (this.hasDrop) {
        if (!this.dropEl) {
          this.dropEl = getSlotViewModel(this.elDropdown) as UIDrop;
        }
        this.dropEl.tether(this.element);
      }
    });

    this.hrefChanged();
  }

  public detached() {
    document.removeEventListener("keydown", this.handleShortcutsEvent, false);
  }

  protected hrefChanged(): void {
    if (this.badgeEl) {
      if (this.href) {
        this.badgeEl.href = this.href;
      } else if (this.badgeEl.attributes.getNamedItem("href")) {
        this.badgeEl.attributes.removeNamedItem("href");
      }
    }
  }
  protected fireKeydown(e: KeyboardEvent) {
    if ((e.key === KeyboardShortcut.ArrowDown || e.key === KeyboardShortcut.Enter)) {
      UIInternal.broadcast(UIInternal.EVT_VIEWPORT_CLICK, e.target);
      return this.fireClick(e);
    }
    else if (e.key === KeyboardShortcut.Tab) {
      UIInternal.broadcast(UIInternal.EVT_VIEWPORT_CLICK, document);
    }
    return true;
  }
  protected fireClickInner($event): boolean {
    if (this.isDisabled || this.busy) {
      $event.stopEvent();
      return false;
    }
  }

  protected fireClick($event): boolean {
    if (this.isDisabled || this.busy) {
      $event.stopEvent();
      return false;
    }
    if (!this.href) {
      $event.stopEvent();
      if (this.hasDrop && !this.split) {
        return this.toggleDrop();
      } else {
        return this.element.dispatchEvent(UIInternal.createEvent("click", this.id));
      }
    }
  }

  private toggleDrop(): boolean {
    const beforeEvent = this.dropEl.isOpen ? "beforeopen" : "beforeclose";
    const afterEvent = this.dropEl.isOpen ? "close" : "open";
    if (this.element.dispatchEvent(UIInternal.createEvent(beforeEvent)) !== false) {
      this.dropEl.toggleDrop();
      this.element.dispatchEvent(UIInternal.createEvent(afterEvent));
    }
    event.stopPropagation();
    return true;
  }

  public isVisible(currentRecord: any) {
    if (this.buttonVisible != null) {
      if (typeof (this.buttonVisible) == 'boolean') {
        return this.buttonVisible;
      }
      if (currentRecord != null || this.current != null) {
        if (typeof (this.buttonVisible) == 'string') {
          const expression = this.parser.parse(this.buttonVisible);
          const scope = {
            bindingContext: { record: currentRecord ?? this.current },
            overrideContext: null
          };
          return expression.evaluate(scope);
        }
        if (typeof (this.buttonVisible) == 'function') {
          return this.buttonVisible({ record: (currentRecord ?? this.current) });
        }
      }
    }
    return true;
  }

  private handleShortcutsEvent: (event: KeyboardEvent) => Promise<any>;

  protected async handleShortcuts(event: KeyboardEvent) {
    if ((typeof (this.keyboardShortcut) === "string" && event.key == this.keyboardShortcut) || (Array.isArray(this.keyboardShortcut) && (this.keyboardShortcut as string[]).some(key => key == event.key))) {
      event.stopImmediatePropagation();
      event.preventDefault();
      return this.fireClick(event);
    }
  }
}
