/**
 * @license
 * Copyright 2016 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */
import '@polymer/paper-button/paper-button';
import {spinnerStyles} from '../../../styles/gr-spinner-styles';
import {votingStyles} from '../../../styles/gr-voting-styles';
import {css, html, LitElement, nothing, PropertyValues} from 'lit';
import {customElement, property} from 'lit/decorators.js';
import {addShortcut, getEventPath, Key} from '../../../utils/dom-util';
import {getAppContext} from '../../../services/app-context';
import {classMap} from 'lit/directives/class-map.js';

declare global {
  interface HTMLElementTagNameMap {
    'gr-button': GrButton;
  }
}
/**
 * @attr {Boolean} no-uppercase - text in button is not uppercased
 * @attr {Boolean} position-below
 * @attr {Boolean} primary - set primary button color
 * @attr {Boolean} secondary - set secondary button color
 */
@customElement('gr-button')
export class GrButton extends LitElement {
  // Private but used in tests.
  readonly reporting = getAppContext().reportingService;

  /**
   * Should this button be rendered as a vote chip? Then we are applying
   * the .voteChip class (see gr-voting-styles) to the paper-button.
   */
  @property({type: Boolean, reflect: true})
  voteChip = false;

  // Note: don't assign a value to this, since constructor is called
  // after created, the initial value maybe overridden by this
  private initialTabindex?: string;

  @property({type: Boolean, attribute: 'down-arrow'})
  downArrow = false;

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

  // If flattened then the button will not be shown as raised.
  @property({type: Boolean, reflect: true})
  flatten = false;

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

  @property({type: Boolean, reflect: true})
  disabled: boolean | null = null;

  static override get styles() {
    return [
      votingStyles,
      spinnerStyles,
      css`
        /* general styles for all buttons */
        :host {
          --background-color: var(
            --button-background-color,
            var(--default-button-background-color)
          );
          --text-color: var(
            --gr-button-text-color,
            var(--default-button-text-color)
          );
          display: inline-block;
          position: relative;
        }
        :host([hidden]) {
          display: none;
        }
        :host([no-uppercase]) paper-button {
          text-transform: none;
        }
        paper-button {
          /* paper-button sets this to anti-aliased, which appears different than
            bold font elsewhere on macOS. */
          -webkit-font-smoothing: initial;
          align-items: center;
          background-color: var(--background-color);
          color: var(--text-color);
          display: flex;
          font-family: var(--font-family, inherit);
          /** Without this '.keyboard-focus' buttons will get bolded. */
          font-weight: var(--font-weight-normal, inherit);
          justify-content: center;
          margin: var(--margin, 0);
          min-width: var(--border, 0);
          padding: var(--gr-button-padding, var(--spacing-s) var(--spacing-m));
        }
        paper-button[elevation='1'] {
          box-shadow: var(--elevation-level-1);
        }
        paper-button[elevation='2'] {
          box-shadow: var(--elevation-level-2);
        }
        paper-button[elevation='3'] {
          box-shadow: var(--elevation-level-3);
        }
        paper-button[elevation='4'] {
          box-shadow: var(--elevation-level-4);
        }
        paper-button[elevation='5'] {
          box-shadow: var(--elevation-level-5);
        }
        paper-button:hover {
          background: linear-gradient(rgba(0, 0, 0, 0.12), rgba(0, 0, 0, 0.12)),
            var(--background-color);
        }

        /* Some mobile browsers treat focused element as hovered element.
        As a result, element remains hovered after click (has grey background in default theme).
        Use @media (hover:none) to remove background if
        user's primary input mechanism can't hover over elements.
        See: https://developer.mozilla.org/en-US/docs/Web/CSS/@media/hover

        Note 1: not all browsers support this media query
        (see https://caniuse.com/#feat=css-media-interaction).
        If browser doesn't support it, then the whole content of @media .. is ignored.
        This is why the default behavior is placed outside of @media.
        */
        @media (hover: none) {
          paper-button:hover {
            background: transparent;
          }
        }

        :host([primary]) {
          --background-color: var(--primary-button-background-color);
          --text-color: var(--primary-button-text-color);
        }
        :host([link][primary]) {
          --text-color: var(--primary-button-background-color);
        }

        /* Keep below color definition for primary so that this takes precedence
          when disabled. */
        :host([disabled]),
        :host([loading]) {
          --background-color: var(--disabled-button-background-color);
          --text-color: var(--deemphasized-text-color);
          cursor: default;
        }

        :host([disabled][flatten]) {
          --background-color: transparent;
          --text-color: var(--disabled-foreground);
        }

        /* Styles for link buttons specifically */
        :host([link]) {
          --background-color: transparent;
          --margin: 0;
        }
        :host([link]) paper-button {
          padding: var(--gr-button-padding, var(--spacing-s));
        }
        :host([disabled][link]),
        :host([loading][link]) {
          --background-color: transparent;
          --text-color: var(--disabled-foreground);
          cursor: default;
        }
        gr-icon.downArrow {
          color: inherit;
        }
        .newVoteChip {
          border: 1px solid var(--border-color);
          box-shadow: none;
          box-sizing: border-box;
          min-width: 3em;
          color: var(--vote-text-color);
        }
      `,
    ];
  }

  override render() {
    return html`<paper-button
      ?raised=${!this.link && !this.flatten}
      ?disabled=${this.disabled || this.loading}
      role="button"
      tabindex="-1"
      part="paper-button"
      class=${classMap({
        newVoteChip: this.voteChip,
      })}
    >
      ${this.loading ? html`<span class="loadingSpin"></span>` : ''}
      <slot></slot>
      ${this.renderArrowIcon()}
    </paper-button>`;
  }

  renderArrowIcon() {
    if (!this.downArrow) return nothing;
    return html`<gr-icon icon="arrow_drop_down" class="downArrow"></gr-icon>`;
  }

  constructor() {
    super();
    this.initialTabindex = this.getAttribute('tabindex') || '0';
    this.addEventListener('click', e => this._handleAction(e));
    addShortcut(this, {key: Key.ENTER}, () => this.click());
    addShortcut(this, {key: Key.SPACE}, () => this.click());
  }

  override updated(changedProperties: PropertyValues) {
    if (changedProperties.has('disabled')) {
      this.setAttribute(
        'tabindex',
        this.disabled ? '-1' : this.initialTabindex || '0'
      );
    }
    if (changedProperties.has('loading') || changedProperties.has('disabled')) {
      this.setAttribute(
        'aria-disabled',
        this.disabled || this.loading ? 'true' : 'false'
      );
    }
  }

  override connectedCallback() {
    super.connectedCallback();
    if (!this.getAttribute('role')) {
      this.setAttribute('role', 'button');
    }
    if (!this.getAttribute('tabindex')) {
      this.setAttribute('tabindex', '0');
    }
  }

  _handleAction(e: MouseEvent) {
    if (this.disabled || this.loading) {
      e.preventDefault();
      e.stopPropagation();
      e.stopImmediatePropagation();
      return;
    }

    this.reporting.reportInteraction('button-click', {path: getEventPath(e)});
  }
}
