/**
 * @license
 * Copyright 2021 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */
import '../shared/gr-icon/gr-icon';
import {fontStyles} from '../../styles/gr-font-styles';
import {customElement, property} from 'lit/decorators.js';
import './gr-checks-action';
import {CheckRun} from '../../models/checks/checks-model';
import {
  AttemptDetail,
  ChecksIcon,
  iconFor,
  runActions,
  worstCategory,
} from '../../models/checks/checks-util';
import {durationString, fromNow} from '../../utils/date-util';
import {RunStatus} from '../../api/checks';
import {ordinal} from '../../utils/string-util';
import {HovercardMixin} from '../../mixins/hovercard-mixin/hovercard-mixin';
import {css, html, LitElement} from 'lit';
import {checksStyles} from './gr-checks-styles';

// This avoids JSC_DYNAMIC_EXTENDS_WITHOUT_JSDOC closure compiler error.
const base = HovercardMixin(LitElement);

@customElement('gr-hovercard-run')
export class GrHovercardRun extends base {
  @property({type: Object})
  run?: CheckRun;

  static override get styles() {
    return [
      fontStyles,
      checksStyles,
      base.styles || [],
      css`
        #container {
          min-width: 356px;
          max-width: 356px;
          padding: var(--spacing-xl) 0 var(--spacing-m) 0;
        }
        .row {
          display: flex;
          margin-top: var(--spacing-s);
        }
        .attempts.row {
          flex-wrap: wrap;
        }
        .chipRow {
          display: flex;
          margin-top: var(--spacing-s);
        }
        .chip {
          background: var(--gray-background);
          color: var(--gray-foreground);
          border-radius: 20px;
          padding: var(--spacing-xs) var(--spacing-m) var(--spacing-xs)
            var(--spacing-s);
        }
        .title {
          color: var(--deemphasized-text-color);
          margin-right: var(--spacing-m);
        }
        div.section {
          margin: 0 var(--spacing-xl) var(--spacing-m) var(--spacing-xl);
          display: flex;
        }
        div.sectionIcon {
          flex: 0 0 30px;
        }
        div.chip gr-icon {
          font-size: 16px;
          /* Positioning of a 16px icon in the middle of a 20px line. */
          position: relative;
          top: 2px;
        }
        div.sectionIcon gr-icon {
          position: relative;
          top: 2px;
          font-size: 20px;
        }
        div.sectionIcon gr-icon.small {
          position: relative;
          top: 6px;
          font-size: 16px;
        }
        div.sectionContent gr-icon.link {
          color: var(--link-color);
        }
        div.sectionContent .attemptIcon gr-icon,
        div.sectionContent gr-icon.small {
          font-size: 16px;
          margin-right: var(--spacing-s);
          /* Positioning of a 16px icon in the middle of a 20px line. */
          position: relative;
          top: 2px;
        }
        div.sectionContent .attemptIcon gr-icon {
          margin-right: 0;
        }
        .attemptIcon,
        .attemptNumber {
          margin-right: var(--spacing-s);
          color: var(--deemphasized-text-color);
          text-align: center;
          width: 24px;
          font-size: var(--font-size-small);
        }
        div.action {
          border-top: 1px solid var(--border-color);
          margin-top: var(--spacing-m);
          padding: var(--spacing-m) var(--spacing-xl) 0;
        }
      `,
    ];
  }

  override render() {
    if (!this.run) return '';
    const icon = this.computeIcon();
    const chipIcon = this.computeChipIcon();
    return html`
      <div id="container" role="tooltip" tabindex="-1">
        <div class="section">
          <div
            ?hidden=${!this.run || this.run.status === RunStatus.RUNNABLE}
            class="chipRow"
          >
            <div class="chip">
              <gr-icon
                icon=${chipIcon.name}
                ?filled=${chipIcon.filled}
              ></gr-icon>
              <span>${this.run.status}</span>
            </div>
          </div>
        </div>
        <div class="section">
          <div class="sectionIcon" ?hidden=${icon.name.length === 0}>
            <gr-icon
              icon=${icon.name}
              class=${icon.name}
              ?filled=${icon.filled}
            ></gr-icon>
          </div>
          <div class="sectionContent">
            <h3 class="name heading-3">
              <span>${this.run.checkName}</span>
            </h3>
          </div>
        </div>
        ${this.renderStatusSection()} ${this.renderAttemptSection()}
        ${this.renderTimestampSection()} ${this.renderDescriptionSection()}
        ${this.renderActions()}
      </div>
    `;
  }

  private renderStatusSection() {
    if (!this.run || (!this.run.statusLink && !this.run.statusDescription))
      return;

    return html`
      <div class="section">
        <div class="sectionIcon">
          <gr-icon icon="info" class="small"></gr-icon>
        </div>
        <div class="sectionContent">
          ${this.run.statusLink
            ? html` <div class="row">
                <div class="title">Status</div>
                <div>
                  <a href=${this.run.statusLink} target="_blank"
                    ><gr-icon
                      icon="open_in_new"
                      aria-label="external link to check status"
                      class="small link"
                    ></gr-icon
                    >${this.computeHostName(this.run.statusLink)}
                  </a>
                </div>
              </div>`
            : ''}
          ${this.run.statusDescription
            ? html` <div class="row">
                <div class="title">Message</div>
                <div>${this.run.statusDescription}</div>
              </div>`
            : ''}
        </div>
      </div>
    `;
  }

  private renderAttemptSection() {
    if (this.hideAttempts()) return;
    const attempts = this.computeAttempts();
    return html`
      <div class="section">
        <div class="sectionIcon">
          <gr-icon icon="arrow_forward" class="small"></gr-icon>
        </div>
        <div class="sectionContent">
          <div class="attempts row">
            <div class="title">Attempt</div>
            ${attempts.map(a => this.renderAttempt(a))}
          </div>
        </div>
      </div>
    `;
  }

  private renderAttempt(attempt: AttemptDetail) {
    const attemptNumber = attempt.attempt;
    const icon = attempt.icon ?? {name: ''};
    if (attemptNumber !== undefined && typeof attemptNumber !== 'number') {
      return;
    }
    return html`
      <div>
        <div class="attemptIcon">
          <gr-icon class=${icon.name} icon=${icon.name} ?filled=${icon.filled}>
          </gr-icon>
        </div>
        <div class="attemptNumber">${ordinal(attemptNumber)}</div>
      </div>
    `;
  }

  private renderTimestampSection() {
    if (
      !this.run ||
      (!this.run.startedTimestamp &&
        !this.run.scheduledTimestamp &&
        !this.run.finishedTimestamp)
    )
      return;

    const scheduled =
      this.run.scheduledTimestamp && !this.run.startedTimestamp
        ? html`<div class="row">
            <div class="title">Scheduled</div>
            <div>${fromNow(this.run.scheduledTimestamp)}</div>
          </div>`
        : '';

    const started = this.run.startedTimestamp
      ? html`<div class="row">
          <div class="title">Started</div>
          <div>${fromNow(this.run.startedTimestamp)}</div>
        </div>`
      : '';

    const finished =
      this.run.finishedTimestamp && this.run.status === RunStatus.COMPLETED
        ? html`<div class="row">
            <div class="title">Ended</div>
            <div>${fromNow(this.run.finishedTimestamp)}</div>
          </div>`
        : '';

    const completed =
      this.run.startedTimestamp &&
      this.run.finishedTimestamp &&
      this.run.status === RunStatus.COMPLETED
        ? html`<div class="row">
            <div class="title">Completion</div>
            <div>
              ${durationString(
                this.run.startedTimestamp,
                this.run.finishedTimestamp,
                true
              )}
            </div>
          </div>`
        : '';

    const eta =
      this.run.finishedTimestamp && this.run.status === RunStatus.RUNNING
        ? html`<div class="row">
            <div class="title">ETA</div>
            <div>
              ${durationString(new Date(), this.run.finishedTimestamp, true)}
            </div>
          </div>`
        : '';

    return html`
      <div class="section">
        <div class="sectionIcon">
          <gr-icon icon="schedule" class="small"></gr-icon>
        </div>
        <div class="sectionContent">
          ${scheduled} ${started} ${finished} ${completed} ${eta}
        </div>
      </div>
    `;
  }

  private renderDescriptionSection() {
    if (!this.run || (!this.run.checkLink && !this.run.checkDescription))
      return;
    return html`
      <div class="section">
        <div class="sectionIcon">
          <gr-icon icon="link" class="small"></gr-icon>
        </div>
        <div class="sectionContent">
          ${this.run.checkDescription
            ? html` <div class="row">
                <div class="title">Description</div>
                <div>${this.run.checkDescription}</div>
              </div>`
            : ''}
          ${this.run.checkLink
            ? html` <div class="row">
                <div class="title">Documentation</div>
                <div>
                  <a href=${this.run.checkLink} target="_blank"
                    ><gr-icon
                      icon="open_in_new"
                      aria-label="external link to check documentation"
                      class="small link"
                    ></gr-icon
                    >${this.computeHostName(this.run.checkLink)}
                  </a>
                </div>
              </div>`
            : ''}
        </div>
      </div>
    `;
  }

  private renderActions() {
    const actions = runActions(this.run);
    return actions.map(
      action =>
        html`
          <div class="action">
            <gr-checks-action
              context="hovercard"
              .eventTarget=${this._target}
              .action=${action}
            ></gr-checks-action>
          </div>
        `
    );
  }

  computeIcon(): ChecksIcon {
    if (!this.run) return {name: ''};
    const category = worstCategory(this.run);
    if (category) return iconFor(category);
    return this.run.status === RunStatus.COMPLETED
      ? iconFor(RunStatus.COMPLETED)
      : {name: ''};
  }

  computeAttempts(): AttemptDetail[] {
    const details: AttemptDetail[] = this.run?.attemptDetails ?? [];
    const more: AttemptDetail[] =
      details.length > 7
        ? [{icon: {name: 'more_horiz'}, attempt: undefined}]
        : [];
    return [...more, ...details.slice(-7)];
  }

  private computeChipIcon(): ChecksIcon {
    if (this.run?.status === RunStatus.COMPLETED) {
      return {name: 'check'};
    }
    if (this.run?.status === RunStatus.RUNNING) {
      return iconFor(RunStatus.RUNNING);
    }
    if (this.run?.status === RunStatus.SCHEDULED) {
      return iconFor(RunStatus.SCHEDULED);
    }
    return {name: ''};
  }

  private computeHostName(link?: string) {
    return link ? new URL(link).hostname : '';
  }

  private hideAttempts() {
    const attemptCount = this.run?.attemptDetails?.length;
    return attemptCount === undefined || attemptCount < 2;
  }
}

declare global {
  interface HTMLElementTagNameMap {
    'gr-hovercard-run': GrHovercardRun;
  }
}
