/**
 * @license
 * Copyright 2018 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */
import '@polymer/iron-input/iron-input';
import '../../shared/gr-button/gr-button';
import {getBaseUrl} from '../../../utils/url-util';
import {
  ServerInfo,
  GroupInfo,
  ContributorAgreementInfo,
} from '../../../types/common';
import {fireAlert, fireTitleChange} from '../../../utils/event-util';
import {getAppContext} from '../../../services/app-context';
import {fontStyles} from '../../../styles/gr-font-styles';
import {formStyles} from '../../../styles/gr-form-styles';
import {sharedStyles} from '../../../styles/shared-styles';
import {LitElement, html, css} from 'lit';
import {customElement, state} from 'lit/decorators.js';
import {BindValueChangeEvent} from '../../../types/events';
import {ifDefined} from 'lit/directives/if-defined.js';

declare global {
  interface HTMLElementTagNameMap {
    'gr-cla-view': GrClaView;
  }
}

@customElement('gr-cla-view')
export class GrClaView extends LitElement {
  // private but used in test
  @state() groups?: GroupInfo[];

  // private but used in test
  @state() serverConfig?: ServerInfo;

  @state() private agreementsText?: string;

  // private but used in test
  @state() agreementName?: string;

  // private but used in test
  @state() signedAgreements?: ContributorAgreementInfo[];

  @state() private showAgreements = false;

  @state() private agreementsUrl?: string;

  private readonly restApiService = getAppContext().restApiService;

  override connectedCallback() {
    super.connectedCallback();
    this.loadData();

    fireTitleChange(this, 'New Contributor Agreement');
  }

  static override get styles() {
    return [
      fontStyles,
      formStyles,
      sharedStyles,
      css`
        h1 {
          margin-bottom: var(--spacing-m);
        }
        h3 {
          margin-bottom: var(--spacing-m);
        }
        .agreementsUrl {
          border: 1px solid var(--border-color);
          margin-bottom: var(--spacing-xl);
          margin-left: var(--spacing-xl);
          margin-right: var(--spacing-xl);
          padding: var(--spacing-s);
        }
        #claNewAgreementsLabel {
          font-weight: var(--font-weight-bold);
        }
        .contributorAgreementButton {
          font-weight: var(--font-weight-bold);
        }
        .alreadySubmittedText {
          color: var(--error-text-color);
          margin: 0 var(--spacing-xxl);
          padding: var(--spacing-m);
        }
        main {
          margin: var(--spacing-xxl) auto;
          max-width: 50em;
        }
      `,
    ];
  }

  override render() {
    return html`
      <main>
        <h1 class="heading-1">New Contributor Agreement</h1>
        <h3 class="heading-3">Select an agreement type:</h3>
        ${(this.serverConfig?.auth.contributor_agreements ?? [])
          .filter(agreement => agreement.url)
          .map(item => this.renderAgreementsButton(item))}
        ${this.renderNewAgreement()}
      </main>
    `;
  }

  private renderAgreementsButton(item: ContributorAgreementInfo) {
    return html`
      <span class="contributorAgreementButton">
        <input
          id="claNewAgreementsInput${item.name}"
          name="claNewAgreementsRadio"
          type="radio"
          data-name=${ifDefined(item.name)}
          data-url=${ifDefined(item.url)}
          @click=${this.handleShowAgreement}
          ?disabled=${this.disableAgreements(item)}
        />
        <label id="claNewAgreementsLabel">${item.name}</label>
      </span>
      ${this.renderAlreadySubmittedText(item)}
      <div class="agreementsUrl">${item.description}</div>
    `;
  }

  private renderAlreadySubmittedText(item: ContributorAgreementInfo) {
    if (!this.disableAgreements(item)) return;

    return html`
      <div class="alreadySubmittedText">Agreement already submitted.</div>
    `;
  }

  private renderNewAgreement() {
    if (!this.showAgreements) return;
    return html`
      <div id="claNewAgreement">
        <h3 class="heading-3">Review the agreement:</h3>
        <div id="agreementsUrl" class="agreementsUrl">
          <a
            href=${ifDefined(this.agreementsUrl)}
            target="blank"
            rel="noopener"
          >
            Please review the agreement.</a
          >
        </div>
        ${this.renderAgreementsTextBox()}
      </div>
    `;
  }

  private renderAgreementsTextBox() {
    if (this.computeHideAgreementTextbox()) return;
    return html`
      <div class="agreementsTextBox">
        <h3 class="heading-3">Complete the agreement:</h3>
        <iron-input
          .bindValue=${this.agreementsText}
          @bind-value-changed=${this.handleBindValueChanged}
        >
          <input id="input-agreements" placeholder="Enter 'I agree' here" />
        </iron-input>
        <gr-button
          @click=${this.handleSaveAgreements}
          ?disabled=${this.agreementsText?.toLowerCase() !== 'i agree'}
        >
          Submit
        </gr-button>
      </div>
    `;
  }

  loadData() {
    const promises = [];
    promises.push(
      this.restApiService.getConfig(true).then(config => {
        this.serverConfig = config;
      })
    );

    promises.push(
      this.restApiService.getAccountGroups().then(groups => {
        if (!groups) return;
        this.groups = groups.sort((a, b) =>
          (a.name || '').localeCompare(b.name || '')
        );
      })
    );

    promises.push(
      this.restApiService
        .getAccountAgreements()
        .then((agreements: ContributorAgreementInfo[] | undefined) => {
          this.signedAgreements = agreements || [];
        })
    );

    return Promise.all(promises);
  }

  // private but used in test
  getAgreementsUrl(configUrl: string) {
    if (!configUrl) return '';

    if (configUrl.startsWith('http:') || configUrl.startsWith('https:')) {
      return configUrl;
    }

    return `${getBaseUrl()}/${configUrl}`;
  }

  private readonly handleShowAgreement = (e: Event) => {
    this.agreementName = (e.target as HTMLInputElement).getAttribute(
      'data-name'
    )!;
    const url = (e.target as HTMLInputElement).getAttribute('data-url')!;
    this.agreementsUrl = this.getAgreementsUrl(url);
    this.showAgreements = true;
  };

  private readonly handleSaveAgreements = () => {
    this.createToast('Agreement saving...');

    const name = this.agreementName;
    return this.restApiService.saveAccountAgreement({name}).then(res => {
      let message = 'Agreement failed to be submitted, please try again';
      if (res.status === 200) {
        message = 'Agreement has been successfully submitted.';
      }
      this.createToast(message);
      this.loadData();
      this.agreementsText = '';
      this.showAgreements = false;
    });
  };

  private createToast(message: string) {
    fireAlert(this, message);
  }

  // private but used in test
  disableAgreements(item: ContributorAgreementInfo) {
    for (const group of this.groups ?? []) {
      if (
        item?.auto_verify_group?.id === group.id ||
        this.signedAgreements?.find(i => i.name === item.name)
      ) {
        return true;
      }
    }
    return false;
  }

  // This checks for auto_verify_group,
  // if specified it returns 'true' which
  // then hides the text box and submit button.
  // private but used in test
  computeHideAgreementTextbox() {
    const contributorAgreements =
      this.serverConfig?.auth.contributor_agreements;
    if (!this.agreementName || !contributorAgreements) return false;
    return contributorAgreements.some(
      (contributorAgreement: ContributorAgreementInfo) =>
        this.agreementName === contributorAgreement.name &&
        !contributorAgreement.auto_verify_group
    );
  }

  private readonly handleBindValueChanged = (e: BindValueChangeEvent) => {
    this.agreementsText = e.detail.value;
  };
}
