<script>
import {
  GlAlert,
  GlButton,
  GlFormGroup,
  GlFormInput,
  GlFormInputGroup,
  GlFormRadioGroup,
  GlInputGroupText,
  GlModal,
  GlSkeletonLoader,
  GlTruncate,
} from '@gitlab/ui';
import * as Sentry from '@sentry/browser';
import download from '~/lib/utils/downloader';
import { cleanLeadingSeparator, joinPaths, stripPathTail } from '~/lib/utils/url_utility';
import { __, s__ } from '~/locale';
import ModalCopyButton from '~/vue_shared/components/modal_copy_button.vue';
import {
  DAST_SITE_VALIDATION_MODAL_ID,
  DAST_SITE_VALIDATION_HTTP_HEADER_KEY,
  DAST_SITE_VALIDATION_METHOD_HTTP_HEADER,
  DAST_SITE_VALIDATION_METHOD_TEXT_FILE,
  DAST_SITE_VALIDATION_METHODS,
} from '../constants';
import dastSiteTokenCreateMutation from '../graphql/dast_site_token_create.mutation.graphql';
import dastSiteValidationCreateMutation from '../graphql/dast_site_validation_create.mutation.graphql';

export default {
  name: 'DastSiteValidationModal',
  DAST_SITE_VALIDATION_MODAL_ID,
  components: {
    GlAlert,
    ModalCopyButton,
    GlButton,
    GlFormGroup,
    GlFormInput,
    GlFormInputGroup,
    GlFormRadioGroup,
    GlInputGroupText,
    GlModal,
    GlSkeletonLoader,
    GlTruncate,
  },
  props: {
    fullPath: {
      type: String,
      required: true,
    },
    targetUrl: {
      type: String,
      required: true,
    },
  },
  data() {
    return {
      isCreatingToken: false,
      hasErrors: false,
      token: null,
      tokenId: null,
      validationMethod: DAST_SITE_VALIDATION_METHOD_TEXT_FILE,
      validationPath: '',
    };
  },
  computed: {
    modalProps() {
      return {
        id: DAST_SITE_VALIDATION_MODAL_ID,
        title: s__('DastSiteValidation|Validate target site'),
        primaryProps: {
          text: s__('DastSiteValidation|Validate'),
          attributes: [
            { disabled: this.hasErrors },
            { variant: 'success' },
            { category: 'primary' },
            { 'data-testid': 'validate-dast-site-button' },
          ],
        },
        cancelProps: {
          text: __('Cancel'),
        },
      };
    },
    validationMethodOptions() {
      return Object.values(DAST_SITE_VALIDATION_METHODS);
    },
    urlObject() {
      try {
        return new URL(this.targetUrl);
      } catch {
        return {};
      }
    },
    origin() {
      return this.urlObject.origin ? `${this.urlObject.origin}/` : '';
    },
    path() {
      return cleanLeadingSeparator(this.urlObject.pathname || '');
    },
    isTextFileValidation() {
      return this.validationMethod === DAST_SITE_VALIDATION_METHOD_TEXT_FILE;
    },
    isHttpHeaderValidation() {
      return this.validationMethod === DAST_SITE_VALIDATION_METHOD_HTTP_HEADER;
    },
    textFileName() {
      return `GitLab-DAST-Site-Validation-${this.token}.txt`;
    },
    locationStepLabel() {
      return DAST_SITE_VALIDATION_METHODS[this.validationMethod].i18n.locationStepLabel;
    },
    httpHeader() {
      return `${DAST_SITE_VALIDATION_HTTP_HEADER_KEY}: ${this.token}`;
    },
  },
  watch: {
    targetUrl: {
      immediate: true,
      handler: 'createValidationToken',
    },
  },
  created() {
    this.unsubscribe = this.$watch(
      () => [this.token, this.validationMethod],
      this.updateValidationPath,
      {
        immediate: true,
      },
    );
  },
  methods: {
    show() {
      this.$refs.modal.show();
    },
    updateValidationPath() {
      this.validationPath = this.isTextFileValidation
        ? this.getTextFileValidationPath()
        : this.path;
    },
    getTextFileValidationPath() {
      return joinPaths(stripPathTail(this.path), this.textFileName);
    },
    onValidationPathInput() {
      this.unsubscribe();
    },
    downloadTextFile() {
      download({ fileName: this.textFileName, fileData: btoa(this.token) });
    },
    async createValidationToken() {
      this.isCreatingToken = true;
      this.hasErrors = false;
      try {
        const {
          data: {
            dastSiteTokenCreate: { errors, id, token },
          },
        } = await this.$apollo.mutate({
          mutation: dastSiteTokenCreateMutation,
          variables: {
            fullPath: this.fullPath,
            targetUrl: this.targetUrl,
          },
        });
        if (errors?.length) {
          this.onError();
        } else {
          this.token = token;
          this.tokenId = id;
        }
      } catch (exception) {
        this.onError(exception);
      }
      this.isCreatingToken = false;
    },
    async validate() {
      try {
        await this.$apollo.mutate({
          mutation: dastSiteValidationCreateMutation,
          variables: {
            fullPath: this.fullPath,
            dastSiteTokenId: this.tokenId,
            validationPath: this.validationPath,
            validationStrategy: this.validationMethod,
          },
        });
      } catch (exception) {
        this.onError(exception);
      }
    },
    onError(exception = null) {
      if (exception !== null) {
        Sentry.captureException(exception);
      }
      this.hasErrors = true;
    },
  },
};
</script>

<template>
  <gl-modal
    ref="modal"
    :modal-id="modalProps.id"
    :title="modalProps.title"
    :action-primary="modalProps.primaryProps"
    :action-cancel="modalProps.cancelProps"
    v-bind="$attrs"
    v-on="$listeners"
    @primary="validate"
  >
    <template v-if="isCreatingToken">
      <gl-skeleton-loader :width="768" :height="206">
        <rect y="0" width="300" height="16" rx="4" />
        <rect y="25" width="200" height="16" rx="4" />
        <rect y="65" width="350" height="16" rx="4" />
        <rect y="90" width="535" height="24" rx="4" />
        <rect y="135" width="370" height="16" rx="4" />
        <rect y="160" width="460" height="32" rx="4" />
      </gl-skeleton-loader>
    </template>
    <gl-alert v-else-if="hasErrors" variant="danger" :dismissible="false">
      {{ s__('DastSiteValidation|Could not create validation token. Please try again.') }}
    </gl-alert>
    <template v-else>
      <gl-form-group :label="s__('DastSiteValidation|Step 1 - Choose site validation method')">
        <gl-form-radio-group v-model="validationMethod" :options="validationMethodOptions" />
      </gl-form-group>
      <gl-form-group
        v-if="isTextFileValidation"
        :label="s__('DastSiteValidation|Step 2 - Add following text to the target site')"
      >
        <gl-button
          variant="info"
          category="secondary"
          size="small"
          icon="download"
          class="gl-display-inline-flex gl-max-w-full"
          data-testid="download-dast-text-file-validation-button"
          :aria-label="s__('DastSiteValidation|Download validation text file')"
          @click="downloadTextFile()"
        >
          <gl-truncate :text="textFileName" position="middle" />
        </gl-button>
      </gl-form-group>
      <gl-form-group
        v-else-if="isHttpHeaderValidation"
        :label="s__('DastSiteValidation|Step 2 - Add following HTTP header to your site')"
      >
        <code class="gl-p-3 gl-bg-black gl-text-white">{{ httpHeader }}</code>
        <modal-copy-button
          :text="httpHeader"
          :title="s__('DastSiteValidation|Copy HTTP header to clipboard')"
          :modal-id="modalProps.id"
        />
      </gl-form-group>
      <gl-form-group :label="locationStepLabel" class="mw-460">
        <gl-form-input-group>
          <template #prepend>
            <gl-input-group-text data-testid="dast-site-validation-path-prefix">{{
              origin
            }}</gl-input-group-text>
          </template>
          <gl-form-input
            v-model="validationPath"
            class="gl-bg-white!"
            data-testid="dast-site-validation-path-input"
            @input="onValidationPathInput()"
          />
        </gl-form-input-group>
      </gl-form-group>
    </template>
  </gl-modal>
</template>
