<template>
  <form @submit.prevent="submit" @input="$emit('formchange')">
    <div v-if="!isDeprecated">
      <Alert
        v-if="!values.active"
        simple
        type="primary"
        icon="iconoir-warning-triangle"
        :title="$t('webhookForm.deactivated.title')"
      >
        <p>{{ $t('webhookForm.deactivated.content') }}</p>
        <a
          class="button button--ghost margin-top-1"
          @click="values.active = true"
          >{{ $t('webhookForm.deactivated.activate') }}</a
        >
      </Alert>
      <div class="row">
        <div class="col col-12">
          <FormElement :error="fieldHasErrors('name')" class="control">
            <label class="control__label">
              {{ $t('webhookForm.inputLabels.name') }}
            </label>
            <div class="control__elements">
              <input
                v-model="values.name"
                class="input input--small"
                :class="{ 'input--error': fieldHasErrors('name') }"
                @blur="$v.values.name.$touch()"
              />
              <div v-if="fieldHasErrors('name')" class="error">
                {{ $t('error.requiredField') }}
              </div>
            </div>
          </FormElement>
        </div>
        <div class="col col-12">
          <div class="control">
            <label class="control__label">
              {{ $t('webhookForm.inputLabels.userFieldNames') }}
            </label>
            <div class="control__elements">
              <Checkbox v-model="values.use_user_field_names">{{
                $t('webhookForm.checkbox.sendUserFieldNames')
              }}</Checkbox>
            </div>
          </div>
        </div>
        <div class="col col-4">
          <div class="control">
            <div class="control__label">
              {{ $t('webhookForm.inputLabels.requestMethod') }}
            </div>
            <div class="control__elements">
              <Dropdown v-model="values.request_method" small>
                <DropdownItem name="GET" value="GET"></DropdownItem>
                <DropdownItem name="POST" value="POST"></DropdownItem>
                <DropdownItem name="PATCH" value="PATCH"></DropdownItem>
                <DropdownItem name="PUT" value="PUT"></DropdownItem>
                <DropdownItem name="DELETE" value="DELETE"></DropdownItem>
              </Dropdown>
            </div>
          </div>
        </div>
        <div class="col col-8">
          <FormElement :error="fieldHasErrors('url')" class="control">
            <label class="control__label">
              {{ $t('webhookForm.inputLabels.url') }}
            </label>
            <div class="control__elements">
              <input
                v-model="values.url"
                :placeholder="$t('webhookForm.inputLabels.url')"
                class="input input--small"
                :class="{ 'input--error': fieldHasErrors('url') }"
                @blur="$v.values.url.$touch()"
              />
              <div
                v-if="
                  fieldHasErrors('url') &&
                  (!$v.values.url.required ||
                    !$v.values.url.isValidURLWithHttpScheme)
                "
                class="error"
              >
                {{ $t('webhookForm.errors.urlField') }}
              </div>
              <div
                v-else-if="$v.values.url.$error && !$v.values.url.maxLength"
                class="error"
              >
                {{
                  $t('error.maxLength', {
                    max: $v.values.url.$params.maxLength.max,
                  })
                }}
              </div>
            </div>
          </FormElement>
        </div>
      </div>
      <div class="control">
        <label class="control__label">
          {{ $t('webhookForm.inputLabels.events') }}
        </label>
        <div class="control__elements">
          <Radio v-model="values.include_all_events" :value="true">{{
            $t('webhookForm.radio.allEvents')
          }}</Radio>
          <Radio v-model="values.include_all_events" :value="false">
            {{ $t('webhookForm.radio.customEvents') }}
          </Radio>
          <div v-if="!values.include_all_events" class="webhook__types">
            <Checkbox
              v-for="webhookEvent in webhookEventTypes"
              :key="webhookEvent.type"
              :value="values.events.includes(webhookEvent.type)"
              class="webhook__type"
              @input="
                $event
                  ? values.events.push(webhookEvent.type)
                  : values.events.splice(
                      values.events.indexOf(webhookEvent.type),
                      1
                    )
              "
              >{{ webhookEvent.getName() }}</Checkbox
            >
          </div>
        </div>
      </div>
      <div class="control">
        <div class="control__label">
          {{ $t('webhookForm.inputLabels.headers') }}
        </div>
        <div class="control__elements">
          <div
            v-for="(header, index) in headers.concat({
              name: '',
              value: '',
            })"
            :key="`header-input-${index}`"
            class="webhook__header"
          >
            <div class="webhook__header-row">
              <input
                v-model="header.name"
                class="input input--small webhook__header-key"
                :class="{
                  'input--error':
                    !lastHeader(index) && $v.headers.$each[index].name.$error,
                }"
                :placeholder="$t('webhookForm.inputLabels.name')"
                @input="
                  lastHeader(index) && addHeader(header.name, header.value)
                "
                @blur="
                  !lastHeader(index) && $v.headers.$each[index].name.$touch()
                "
              />
              <input
                v-model="header.value"
                class="input input--small webhook__header-value"
                :class="{
                  'input--error':
                    !lastHeader(index) && $v.headers.$each[index].value.$error,
                }"
                :placeholder="$t('webhookForm.inputLabels.value')"
                @input="
                  lastHeader(index) && addHeader(header.name, header.value)
                "
                @blur="
                  !lastHeader(index) && $v.headers.$each[index].value.$touch()
                "
              />
              <a
                v-if="!lastHeader(index)"
                class="button button--error webhook__header-delete"
                @click="removeHeader(index)"
              >
                <i class="iconoir-bin button__icon"></i>
              </a>
            </div>
          </div>
          <div v-if="$v.headers.$anyError" class="error">
            {{ $t('webhookForm.errors.invalidHeaders') }}
          </div>
        </div>
      </div>
      <div class="control">
        <div class="control__label">
          {{ $t('webhookForm.inputLabels.example') }}
        </div>
        <div class="control__elements">
          <div class="webhook__code-with-dropdown">
            <div class="webhook__code-dropdown">
              <Dropdown
                v-model="exampleWebhookEventType"
                class="dropdown--floating-left"
                small
              >
                <DropdownItem
                  v-for="webhookEvent in webhookEventTypes"
                  :key="webhookEvent.type"
                  :name="webhookEvent.getName()"
                  :value="webhookEvent.type"
                ></DropdownItem>
              </Dropdown>
            </div>
            <div class="webhook__code-container">
              <pre
                class="webhook__code"
              ><code>{{ JSON.stringify(testExample, null, 4)}}</code></pre>
            </div>
          </div>
        </div>
      </div>
      <a class="button button--ghost" @click="openTestModal()">{{
        $t('webhookForm.triggerButton')
      }}</a>
      <slot></slot>
      <TestWebhookModal ref="testModal" />
    </div>
    <div v-else>
      <div class="alert alert--error alert--has-icon">
        <div class="alert__icon">
          <i class="iconoir-warning-triangle"></i>
        </div>
        <div class="alert__title">
          {{ $t('webhookForm.deprecatedEventType.title') }}
        </div>
        <p class="alert__content">
          {{ $t('webhookForm.deprecatedEventType.description') }}
          <button
            class="button webhook__convert"
            @click="convertFromDeprecated"
          >
            {{ $t('webhookForm.deprecatedEventType.convert') }}
          </button>
        </p>
      </div>
    </div>
  </form>
</template>

<script>
import { mapGetters } from 'vuex'
import { required, maxLength } from 'vuelidate/lib/validators'

import form from '@baserow/modules/core/mixins/form'
import error from '@baserow/modules/core/mixins/error'
import Checkbox from '@baserow/modules/core/components/Checkbox'
import Radio from '@baserow/modules/core/components/Radio'
import TestWebhookModal from '@baserow/modules/database/components/webhook/TestWebhookModal'
import { isValidURLWithHttpScheme } from '@baserow/modules/core/utils/string'

export default {
  name: 'WebhookForm',
  components: {
    Checkbox,
    Radio,
    TestWebhookModal,
  },
  mixins: [form, error],
  props: {
    table: {
      type: Object,
      required: true,
    },
  },
  data() {
    return {
      allowedValues: [
        'name',
        'url',
        'request_method',
        'include_all_events',
        'use_user_field_names',
        'headers',
        'events',
        'active',
      ],
      values: {
        name: '',
        active: true,
        use_user_field_names: true,
        url: '',
        request_method: 'POST',
        include_all_events: true,
        events: [],
      },
      headers: [],
      exampleWebhookEventType: '',
    }
  },
  computed: {
    webhookEventTypes() {
      return this.$registry.getAll('webhookEvent')
    },
    isDeprecated() {
      return this.values.events.some((eventName) =>
        ['row.created', 'row.updated', 'row.deleted'].includes(eventName)
      )
    },
    /**
     * Generates an example payload of the webhook event based on the chosen webhook
     * event type.
     */
    testExample() {
      if (this.exampleWebhookEventType === '') {
        return {}
      }

      const rowExample = {
        id: 0,
        order: '1.00000000000000000000',
      }
      this.fields.forEach((field) => {
        const fieldType = this.$registry.get('field', field.type)
        const empty = fieldType.getEmptyValue(field)
        rowExample[
          this.values.use_user_field_names ? field.name : `field_${field.id}`
        ] = empty
      })
      const webhookEvent = this.$registry.get(
        'webhookEvent',
        this.exampleWebhookEventType
      )
      return webhookEvent.getExamplePayload(this.table, rowExample)
    },
    ...mapGetters({
      fields: 'field/getAll',
    }),
  },
  created() {
    const keys = Object.keys(this.webhookEventTypes)
    if (keys.length > 0) {
      this.exampleWebhookEventType = this.webhookEventTypes[keys[0]].type
    }

    // If an headers object is provided as default value, we need to populate the
    // internal headers representation that can be edited by this form. When the form
    // is submitted, it will be converted to the correct structure.
    if (this.defaultValues.headers) {
      Object.keys(this.defaultValues.headers).forEach((name) => {
        this.headers.push({
          name,
          value: this.defaultValues.headers[name],
        })
      })
    }
  },
  validations: {
    values: {
      name: { required },
      url: { required, maxLength: maxLength(2000), isValidURLWithHttpScheme },
    },
    headers: {
      $each: {
        name: {
          required,
          valid(value) {
            const regex = /[^:\\s][^:\\r\\n]*$/
            return !!value.match(regex)
          },
        },
        value: {
          required,
        },
      },
    },
  },
  methods: {
    prepareHeaders(headers) {
      const preparedHeaders = {}
      headers.forEach((header) => {
        preparedHeaders[header.name] = header.value
      })
      return preparedHeaders
    },
    getFormValues() {
      const values = form.methods.getFormValues.call(this)
      values.headers = this.prepareHeaders(this.headers)
      return values
    },
    openTestModal() {
      // The form must be valid we can show the test modal.
      if (!this.isFormValid()) {
        this.$v.$touch()
        return
      }

      const values = this.getFormValues()
      this.$refs.testModal.show(
        this.table.id,
        this.exampleWebhookEventType,
        values
      )
    },
    addHeader(name, value) {
      this.headers.push({ name, value })
    },
    removeHeader(index) {
      this.headers.splice(index, 1)
    },
    lastHeader(index) {
      return index === this.headers.length
    },
    convertFromDeprecated() {
      this.values.events = this.values.events.map((eventName) =>
        eventName.replace('row.', 'rows.')
      )
      this.submit()
    },
  },
}
</script>