/**
 * @license
 * Copyright 2016 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */
import '../gr-account-label/gr-account-label';
import '../gr-button/gr-button';
import '../gr-icon/gr-icon';
import {
  AccountInfo,
  ApprovalInfo,
  ChangeInfo,
  LabelInfo,
} from '../../../types/common';
import {getAppContext} from '../../../services/app-context';
import {LitElement, css, html} from 'lit';
import {customElement, property} from 'lit/decorators.js';
import {ClassInfo, classMap} from 'lit/directives/class-map.js';
import {getLabelStatus, hasVoted, LabelStatus} from '../../../utils/label-util';

@customElement('gr-account-chip')
export class GrAccountChip extends LitElement {
  /**
   * Fired to indicate a key was pressed while this chip was focused.
   *
   * @event account-chip-keydown
   */

  /**
   * Fired to indicate this chip should be removed, i.e. when the x button is
   * clicked or when the remove function is called.
   *
   * @event remove
   */

  @property({type: Object})
  account?: AccountInfo;

  /**
   * Optional ChangeInfo object, typically comes from the change page or
   * from a row in a list of search results. This is needed for some change
   * related features like adding the user as a reviewer.
   */
  @property({type: Object})
  change?: ChangeInfo;

  /**
   * Should this user be considered to be in the attention set, regardless
   * of the current state of the change object?
   */
  @property({type: Boolean})
  forceAttention = false;

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

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

  /**
   * Should attention set related features be shown in the component? Note
   * that the information whether the user is in the attention set or not is
   * part of the ChangeInfo object in the change property.
   */
  @property({type: Boolean})
  highlightAttention = false;

  @property({type: Boolean, reflect: true})
  showAvatar?: boolean;

  @property({type: Object})
  vote?: ApprovalInfo;

  @property({type: Object})
  label?: LabelInfo;

  private readonly restApiService = getAppContext().restApiService;

  static override get styles() {
    return [
      css`
        :host {
          display: block;
          overflow: hidden;
        }
        .container {
          align-items: center;
          background-color: var(--background-color-primary);
          /** round */
          border-radius: var(--account-chip-border-radius, 20px);
          border: 1px solid var(--border-color);
          display: inline-flex;
          padding: 0 1px;
          /* Any outermost circular icon would fit neatly in the border-radius
             and won't need padding, but the exact outermost elements will
             depend on account state and the context gr-account-chip is used.
             So, these values are passed down to gr-account-label and any
             outermost elements will use the value and then override it. */
          --account-label-padding-left: 6px;
          --account-label-padding-right: 6px;
          --account-label-circle-padding-left: 0;
          --account-label-circle-padding-right: 0;
        }
        :host:focus {
          border-color: transparent;
          box-shadow: none;
          outline: none;
        }
        :host:focus .container,
        :host:focus gr-button {
          background: #ccc;
        }
        :host([disabled]) {
          opacity: 0.6;
          pointer-events: none;
        }
        gr-icon {
          font-size: 1.2rem;
        }
        .container gr-account-label::part(gr-account-label-text) {
          color: var(--deemphasized-text-color);
        }
        .container.disliked {
          border: 1px solid var(--vote-outline-disliked);
        }
        .container.recommended {
          border: 1px solid var(--vote-outline-recommended);
        }
        .container.disliked,
        .container.recommended {
          --account-label-padding-right: var(--spacing-xs);
          --account-label-circle-padding-right: var(--spacing-xs);
        }
        .container.closeShown {
          --account-label-padding-right: 3px;
          --account-label-circle-padding-right: 3px;
        }
        gr-button.remove::part(paper-button),
        gr-button.remove:hover::part(paper-button),
        gr-button.remove:focus::part(paper-button) {
          border-top-width: 0;
          border-right-width: 0;
          border-bottom-width: 0;
          border-left-width: 0;
          color: var(--deemphasized-text-color);
          font-weight: var(--font-weight-normal);
          height: 0.6em;
          line-height: 10px;
          /* This cancels most of the --account-label-padding-horizontal. */
          margin-left: -4px;
          padding: 0 2px 0 1px;
          text-decoration: none;
        }
      `,
    ];
  }

  override render() {
    return html`<div
      class=${classMap({
        ...this.computeVoteClasses(),
        container: true,
        closeShown: this.removable,
      })}
    >
      <div>
        <gr-account-label
          .account=${this.account}
          .change=${this.change}
          ?forceAttention=${this.forceAttention}
          ?highlightAttention=${this.highlightAttention}
          clickable
        >
        </gr-account-label>
      </div>
      <slot name="vote-chip"></slot>
      <gr-button
        id="remove"
        link=""
        ?hidden=${!this.removable}
        aria-label="Remove"
        class="remove"
        @click=${this.handleRemoveTap}
      >
        <gr-icon icon="close"></gr-icon>
      </gr-button>
    </div>`;
  }

  constructor() {
    super();
    this.getHasAvatars().then(hasAvatars => {
      this.showAvatar = hasAvatars;
    });
  }

  private handleRemoveTap(e: MouseEvent) {
    e.preventDefault();
    this.dispatchEvent(
      new CustomEvent('remove', {
        detail: {account: this.account},
        composed: true,
        bubbles: true,
      })
    );
  }

  private getHasAvatars() {
    return this.restApiService
      .getConfig()
      .then(cfg =>
        Promise.resolve(!!(cfg && cfg.plugin && cfg.plugin.has_avatars))
      );
  }

  private computeVoteClasses(): ClassInfo {
    if (!this.label || !this.account || !hasVoted(this.label, this.account)) {
      return {};
    }
    const status = getLabelStatus(this.label, this.vote?.value);
    if ([LabelStatus.APPROVED, LabelStatus.RECOMMENDED].includes(status)) {
      return {recommended: true};
    } else if ([LabelStatus.REJECTED, LabelStatus.DISLIKED].includes(status)) {
      return {disliked: true};
    } else {
      return {};
    }
  }
}

declare global {
  interface HTMLElementTagNameMap {
    'gr-account-chip': GrAccountChip;
  }
}
