/**
 * @license
 * Copyright 2017 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */
import '../gr-icon/gr-icon';
import '../gr-tooltip-content/gr-tooltip-content';
import '../../../styles/shared-styles';
import {ChangeInfo} from '../../../types/common';
import {ParsedChangeInfo} from '../../../types/types';
import {sharedStyles} from '../../../styles/shared-styles';
import {LitElement, PropertyValues, html, css} from 'lit';
import {customElement, property, state} from 'lit/decorators.js';
import {createSearchUrl} from '../../../models/views/search';
import {GeneratedWebLink} from '../../../utils/weblink-util';

export enum ChangeStates {
  ABANDONED = 'Abandoned',
  ACTIVE = 'Active',
  MERGE_CONFLICT = 'Merge Conflict',
  GIT_CONFLICT = 'Git Conflict',
  MERGED = 'Merged',
  PRIVATE = 'Private',
  READY_TO_SUBMIT = 'Ready to submit',
  REVERT_CREATED = 'Revert Created',
  REVERT_SUBMITTED = 'Revert Submitted',
  WIP = 'WIP',
}

export const WIP_TOOLTIP =
  "This change isn't ready to be reviewed or submitted. " +
  'It will not appear on dashboards unless you are in the attention set, ' +
  'and email notifications will be silenced until the review is started.';

export const MERGE_CONFLICT_TOOLTIP =
  'This change has merge conflicts. ' +
  'Rebase on the upstream branch (e.g. "git pull --rebase"). ' +
  'Upload a new patchset after resolving all merge conflicts.';

export const GIT_CONFLICT_TOOLTIP =
  'A file contents of the change contain git conflict markers' +
  'to indicate the conflicts.';

const PRIVATE_TOOLTIP =
  'This change is only visible to its owner and ' +
  'current reviewers (or anyone with "View Private Changes" permission).';

@customElement('gr-change-status')
export class GrChangeStatus extends LitElement {
  @property({type: Boolean, reflect: true})
  flat = false;

  @property({type: Object})
  change?: ChangeInfo | ParsedChangeInfo;

  @property({type: String})
  status?: ChangeStates;

  // Private but used in tests.
  @state()
  tooltipText = '';

  @property({type: Object})
  revertedChange?: ChangeInfo;

  @property({type: Object})
  resolveWeblinks?: GeneratedWebLink[] = [];

  static override get styles() {
    return [
      sharedStyles,
      css`
        .chip {
          border-radius: var(--border-radius);
          background-color: var(--chip-background-color);
          padding: 0 var(--spacing-m);
          white-space: nowrap;
        }
        :host(.merged) .chip {
          background-color: var(--status-merged);
          color: var(--status-merged);
        }
        :host(.abandoned) .chip {
          background-color: var(--status-abandoned);
          color: var(--status-abandoned);
        }
        :host(.wip) .chip {
          background-color: var(--status-wip);
          color: var(--status-wip);
        }
        :host(.private) .chip {
          background-color: var(--status-private);
          color: var(--status-private);
        }
        :host(.merge-conflict) .chip,
        :host(.git-conflict) .chip {
          background-color: var(--status-conflict);
          color: var(--status-conflict);
        }
        :host(.active) .chip {
          background-color: var(--status-active);
          color: var(--status-active);
        }
        :host(.ready-to-submit) .chip {
          background-color: var(--status-ready);
          color: var(--status-ready);
        }
        :host(.revert-created) .chip {
          background-color: var(--status-revert-created);
          color: var(--status-revert-created);
        }
        :host(.revert-submitted) .chip {
          background-color: var(--status-revert-created);
          color: var(--status-revert-created);
        }
        .status-link {
          text-decoration: none;
        }
        :host(.custom) .chip {
          background-color: var(--status-custom);
          color: var(--status-custom);
        }
        :host([flat]) .chip {
          background-color: transparent;
          padding: 0;
        }
        :host(:not([flat])) .chip,
        :host(:not([flat])) .chip gr-icon {
          color: var(--status-text-color);
        }
      `,
    ];
  }

  override render() {
    return html`
      <gr-tooltip-content
        has-tooltip
        position-below
        .title=${this.tooltipText}
        .maxWidth=${'40em'}
      >
        ${this.renderStatusLink()}
      </gr-tooltip-content>
    `;
  }

  private renderStatusLink() {
    if (!this.hasStatusLink()) {
      return html`
        <div class="chip" aria-label="Label: ${this.status}">
          ${this.computeStatusString()}
        </div>
      `;
    }

    return html`
      <a class="status-link" href=${this.getStatusLink()}>
        <div class="chip" aria-label="Label: ${this.status}">
          ${this.computeStatusString()}
          ${this.showResolveIcon()
            ? html`<gr-icon icon="edit" filled small></gr-icon>`
            : ''}
        </div>
      </a>
    `;
  }

  override willUpdate(changedProperties: PropertyValues) {
    if (changedProperties.has('status')) {
      this.updateChipDetails(changedProperties.get('status') as ChangeStates);
    }
  }

  private computeStatusString() {
    if (this.status === ChangeStates.WIP && !this.flat) {
      return 'Work in Progress';
    }
    return this.status ?? '';
  }

  private toClassName(str?: ChangeStates) {
    return str ? str.toLowerCase().replace(/\s/g, '-') : '';
  }

  // private but used in test
  hasStatusLink(): boolean {
    const isRevertCreatedOrSubmitted =
      (this.status === ChangeStates.REVERT_SUBMITTED ||
        this.status === ChangeStates.REVERT_CREATED) &&
      this.revertedChange !== undefined;
    return (
      isRevertCreatedOrSubmitted ||
      !!(
        this.status === ChangeStates.MERGE_CONFLICT &&
        this.resolveWeblinks?.length
      )
    );
  }

  // private but used in test
  getStatusLink(): string {
    if (this.revertedChange) {
      return createSearchUrl({query: `${this.revertedChange._number}`});
    }
    if (
      this.status === ChangeStates.MERGE_CONFLICT &&
      this.resolveWeblinks?.length
    ) {
      return this.resolveWeblinks[0].url ?? '';
    }
    return '';
  }

  // private but used in test
  showResolveIcon(): boolean {
    return (
      this.status === ChangeStates.MERGE_CONFLICT &&
      !!this.resolveWeblinks?.length
    );
  }

  private updateChipDetails(previousStatus?: ChangeStates) {
    if (previousStatus) {
      this.classList.remove(this.toClassName(previousStatus));
    }
    this.classList.add(this.toClassName(this.status));

    switch (this.status) {
      case ChangeStates.WIP:
        this.tooltipText = WIP_TOOLTIP;
        break;
      case ChangeStates.PRIVATE:
        this.tooltipText = PRIVATE_TOOLTIP;
        break;
      case ChangeStates.MERGE_CONFLICT:
        this.tooltipText = MERGE_CONFLICT_TOOLTIP;
        break;
      case ChangeStates.GIT_CONFLICT:
        this.tooltipText = GIT_CONFLICT_TOOLTIP;
        break;
      default:
        this.tooltipText = '';
        break;
    }
  }
}

declare global {
  interface HTMLElementTagNameMap {
    'gr-change-status': GrChangeStatus;
  }
}
