<script>
import {
  GlAlert,
  GlButton,
  GlForm,
  GlFormInput,
  GlFormGroup,
  GlLink,
  GlLoadingIcon,
  GlSprintf,
} from '@gitlab/ui';
import {
  I18N_BUTTON_REGISTER,
  I18N_BUTTON_SETUP,
  I18N_BUTTON_TRY_AGAIN,
  I18N_DEVICE_NAME,
  I18N_DEVICE_NAME_DESCRIPTION,
  I18N_DEVICE_NAME_PLACEHOLDER,
  I18N_ERROR_HTTP,
  I18N_ERROR_UNSUPPORTED_BROWSER,
  I18N_INFO_TEXT,
  I18N_NOTICE,
  I18N_PASSWORD,
  I18N_PASSWORD_DESCRIPTION,
  I18N_STATUS_SUCCESS,
  I18N_STATUS_WAITING,
  STATE_ERROR,
  STATE_READY,
  STATE_SUCCESS,
  STATE_UNSUPPORTED,
  STATE_WAITING,
  WEBAUTHN_DOCUMENTATION_PATH,
  WEBAUTHN_REGISTER,
} from '~/authentication/webauthn/constants';
import WebAuthnError from '~/authentication/webauthn/error';
import {
  convertCreateParams,
  convertCreateResponse,
  isHTTPS,
  supported,
} from '~/authentication/webauthn/util';
import csrf from '~/lib/utils/csrf';

export default {
  name: 'WebAuthnRegistration',
  components: {
    GlAlert,
    GlButton,
    GlForm,
    GlFormInput,
    GlFormGroup,
    GlLink,
    GlLoadingIcon,
    GlSprintf,
  },
  I18N_BUTTON_REGISTER,
  I18N_BUTTON_SETUP,
  I18N_BUTTON_TRY_AGAIN,
  I18N_DEVICE_NAME,
  I18N_DEVICE_NAME_DESCRIPTION,
  I18N_DEVICE_NAME_PLACEHOLDER,
  I18N_ERROR_HTTP,
  I18N_ERROR_UNSUPPORTED_BROWSER,
  I18N_INFO_TEXT,
  I18N_NOTICE,
  I18N_PASSWORD,
  I18N_PASSWORD_DESCRIPTION,
  I18N_STATUS_SUCCESS,
  I18N_STATUS_WAITING,
  STATE_ERROR,
  STATE_READY,
  STATE_SUCCESS,
  STATE_UNSUPPORTED,
  STATE_WAITING,
  WEBAUTHN_DOCUMENTATION_PATH,
  inject: ['initialError', 'passwordRequired', 'targetPath'],
  data() {
    return {
      csrfToken: csrf.token,
      form: { deviceName: '', password: '' },
      state: STATE_UNSUPPORTED,
      errorMessage: this.initialError,
      credentials: null,
    };
  },
  computed: {
    disabled() {
      const isEmptyDeviceName = this.form.deviceName.trim() === '';
      const isEmptyPassword = this.form.password.trim() === '';

      if (this.passwordRequired === false) {
        return isEmptyDeviceName;
      }

      return isEmptyDeviceName || isEmptyPassword;
    },
  },
  created() {
    if (this.errorMessage) {
      this.state = STATE_ERROR;
      return;
    }

    if (supported()) {
      this.state = STATE_READY;
      return;
    }

    this.errorMessage = isHTTPS() ? I18N_ERROR_UNSUPPORTED_BROWSER : I18N_ERROR_HTTP;
  },
  methods: {
    isCurrentState(state) {
      return this.state === state;
    },
    async onRegister() {
      this.state = STATE_WAITING;

      try {
        const credentials = await navigator.credentials.create({
          publicKey: convertCreateParams(gon.webauthn.options),
        });

        this.credentials = JSON.stringify(convertCreateResponse(credentials));
        this.state = STATE_SUCCESS;
      } catch (error) {
        this.errorMessage = new WebAuthnError(error, WEBAUTHN_REGISTER).message();
        this.state = STATE_ERROR;
      }
    },
  },
};
</script>

<template>
  <div>
    <template v-if="isCurrentState($options.STATE_UNSUPPORTED)">
      <gl-alert variant="danger" :dismissible="false">{{ errorMessage }}</gl-alert>
    </template>

    <template v-else-if="isCurrentState($options.STATE_READY)">
      <div class="row">
        <div class="col-md-5">
          <gl-button variant="confirm" @click="onRegister">{{
            $options.I18N_BUTTON_SETUP
          }}</gl-button>
        </div>
        <div class="col-md-7">
          <p>{{ $options.I18N_INFO_TEXT }}</p>
        </div>
      </div>
    </template>

    <template v-else-if="isCurrentState($options.STATE_WAITING)">
      <gl-alert :dismissible="false">
        {{ $options.I18N_STATUS_WAITING }}
        <gl-loading-icon />
      </gl-alert>
    </template>

    <template v-else-if="isCurrentState($options.STATE_SUCCESS)">
      <p>{{ $options.I18N_STATUS_SUCCESS }}</p>
      <gl-alert :dismissible="false" class="gl-mb-5">
        <gl-sprintf :message="$options.I18N_NOTICE">
          <template #link="{ content }">
            <gl-link :href="$options.WEBAUTHN_DOCUMENTATION_PATH" target="_blank">{{
              content
            }}</gl-link>
          </template>
        </gl-sprintf>
      </gl-alert>

      <div class="row">
        <gl-form method="post" :action="targetPath" class="col-md-9" data-testid="create-webauthn">
          <gl-form-group
            v-if="passwordRequired"
            :description="$options.I18N_PASSWORD_DESCRIPTION"
            :label="$options.I18N_PASSWORD"
            label-for="webauthn-registration-current-password"
          >
            <gl-form-input
              id="webauthn-registration-current-password"
              v-model="form.password"
              name="current_password"
              type="password"
              autocomplete="current-password"
              data-testid="current-password-input"
            />
          </gl-form-group>

          <gl-form-group
            :description="$options.I18N_DEVICE_NAME_DESCRIPTION"
            :label="$options.I18N_DEVICE_NAME"
            label-for="device-name"
          >
            <gl-form-input
              id="device-name"
              v-model="form.deviceName"
              name="device_registration[name]"
              :placeholder="$options.I18N_DEVICE_NAME_PLACEHOLDER"
              data-testid="device-name-input"
            />
          </gl-form-group>

          <input type="hidden" name="device_registration[device_response]" :value="credentials" />
          <input :value="csrfToken" type="hidden" name="authenticity_token" />

          <gl-button type="submit" :disabled="disabled" variant="confirm">{{
            $options.I18N_BUTTON_REGISTER
          }}</gl-button>
        </gl-form>
      </div>
    </template>

    <template v-else-if="isCurrentState($options.STATE_ERROR)">
      <gl-alert
        variant="danger"
        :dismissible="false"
        class="gl-mb-5"
        :secondary-button-text="$options.I18N_BUTTON_TRY_AGAIN"
        @secondaryAction="onRegister"
      >
        {{ errorMessage }}
      </gl-alert>
    </template>
  </div>
</template>
