<template>
  <div
    v-if="field.type === 'verification'"
    class="field-settings-verification-method py-1.5 px-1 relative"
    @click.stop
  >
    <select
      :placeholder="t('method')"
      class="select select-bordered select-xs font-normal w-full max-w-xs !h-7 !outline-0 bg-transparent"
      @change="[field.preferences ||= {}, field.preferences.method = $event.target.value, $emit('save')]"
    >
      <option
        v-for="method in verificationMethods"
        :key="method"
        :value="method.toLowerCase()"
        :selected="method.toLowerCase() === field.preferences?.method || (method === 'QeS' && !field.preferences?.method)"
      >
        {{ method }}
      </option>
    </select>
    <label
      :style="{ backgroundColor }"
      class="absolute -top-1 left-2.5 px-1 h-4"
      style="font-size: 8px"
    >
      {{ t('method') }}
    </label>
  </div>
  <div
    v-if="['select', 'radio'].includes(field.type) && !defaultField"
    class="field-settings-default-value py-1.5 px-1 relative"
    @click.stop
  >
    <select
      :placeholder="t('default_value')"
      dir="auto"
      class="select select-bordered select-xs w-full max-w-xs h-7 !outline-0 font-normal bg-transparent"
      @change="[field.default_value = $event.target.value, !field.default_value && delete field.default_value, $emit('save')]"
    >
      <option
        value=""
        :selected="!field.default_value"
      >
        {{ t('none') }}
      </option>
      <option
        v-for="(option, index) in field.options || []"
        :key="option.uuid"
        :value="option.value || `${t('option')} ${index + 1}`"
        :selected="field.default_value === (option.value || `${t('option')} ${index + 1}`)"
      >
        {{ option.value || `${t('option')} ${index + 1}` }}
      </option>
    </select>
    <label
      :style="{ backgroundColor }"
      class="absolute -top-1 left-2.5 px-1 h-4"
      style="font-size: 8px"
    >
      {{ t('default_value') }}
    </label>
  </div>
  <div
    v-if="['text', 'number'].includes(field.type) && !defaultField"
    class="field-settings-default-value py-1.5 px-1 relative"
    @click.stop
  >
    <input
      v-model="field.default_value"
      :placeholder="t('default_value')"
      dir="auto"
      :type="field.type"
      class="input input-bordered input-xs w-full max-w-xs h-7 !outline-0 bg-transparent"
      @blur="$emit('save')"
    >
    <label
      v-if="field.default_value"
      :style="{ backgroundColor }"
      class="absolute -top-1 left-2.5 px-1 h-4"
      style="font-size: 8px"
    >
      {{ t('default_value') }}
    </label>
  </div>
  <div
    v-if="['text', 'cells'].includes(field.type)"
    class="field-settings-validation py-1.5 px-1 relative"
    @click.stop
  >
    <select
      class="select select-bordered select-xs w-full max-w-xs h-7 !outline-0 font-normal bg-transparent"
      @change="onChangeValidation"
    >
      <option
        :selected="!field.validation"
        value=""
      >
        {{ t('none') }}
      </option>
      <option
        v-for="(key, value) in validations"
        :key="key"
        :selected="lengthValidation ? key == 'length' : (field.validation?.pattern ? value === field.validation.pattern : key === 'none')"
        :value="key"
      >
        {{ t(key) }}
      </option>
      <option
        :selected="field.validation && !validations[field.validation.pattern] && !lengthValidation"
        :value="validations[field.validation?.pattern] || !field.validation?.pattern ? 'custom' : field.validation?.pattern"
      >
        {{ t('custom') }}
      </option>
    </select>
    <label
      :style="{ backgroundColor }"
      class="absolute -top-1 left-2.5 px-1 h-4"
      style="font-size: 8px"
    >
      {{ t('validation') }}
    </label>
  </div>
  <div
    v-if="['text', 'cells'].includes(field.type) && field.validation && lengthValidation"
    class="field-settings-length-validation py-1.5 px-1 relative flex space-x-1"
    @click.stop
  >
    <div class="w-1/2 relative">
      <input
        :placeholder="t('min')"
        type="number"
        min="0"
        :value="lengthValidation.min"
        class="input input-bordered w-full input-xs h-7 !outline-0 bg-transparent"
        @input="field.validation.pattern = `.{${$event.target.value || 0},${lengthValidation.max || ''}}`"
        @blur="$emit('save')"
      >
      <label
        v-if="lengthValidation.min"
        :style="{ backgroundColor }"
        class="absolute -top-2.5 left-1.5 px-1 h-4"
        style="font-size: 8px"
      >
        {{ t('min') }}
      </label>
    </div>
    <div class="w-1/2 relative">
      <input
        :placeholder="t('max')"
        type="number"
        min="1"
        class="input input-bordered w-full input-xs h-7 !outline-0 bg-transparent"
        :value="lengthValidation.max"
        @input="field.validation.pattern = `.{${lengthValidation.min},${$event.target.value || ''}}`"
        @blur="$emit('save')"
      >
      <label
        v-if="lengthValidation.max"
        :style="{ backgroundColor }"
        class="absolute -top-2.5 left-1.5 px-1 h-4"
        style="font-size: 8px"
      >
        {{ t('max') }}
      </label>
    </div>
  </div>
  <div
    v-if="field.type === 'number'"
    class="field-settings-number-range py-1.5 px-1 relative flex space-x-1"
    @click.stop
  >
    <div class="w-1/2 relative">
      <input
        :placeholder="t('min')"
        type="number"
        min="0"
        :value="field.validation?.min"
        class="input input-bordered w-full input-xs h-7 !outline-0 bg-transparent"
        @input="[field.validation ||= {}, $event.target.value ? field.validation.min = $event.target.value : delete field.validation.min]"
        @blur="$emit('save')"
      >
      <label
        v-if="field.validation?.min"
        :style="{ backgroundColor }"
        class="absolute -top-2.5 left-1.5 px-1 h-4"
        style="font-size: 8px"
      >
        {{ t('min') }}
      </label>
    </div>
    <div class="w-1/2 relative">
      <input
        :placeholder="t('max')"
        type="number"
        min="1"
        class="input input-bordered w-full input-xs h-7 !outline-0 bg-transparent"
        :value="field.validation?.max"
        @input="[field.validation ||= {}, $event.target.value ? field.validation.max = $event.target.value : delete field.validation.max]"
        @blur="$emit('save')"
      >
      <label
        v-if="field.validation?.max"
        :style="{ backgroundColor }"
        class="absolute -top-2.5 left-1.5 px-1 h-4"
        style="font-size: 8px"
      >
        {{ t('max') }}
      </label>
    </div>
  </div>
  <div
    v-if="field.type === 'number'"
    class="field-settings-number-format py-1.5 px-1 relative"
    @click.stop
  >
    <select
      :placeholder="t('format')"
      class="select select-bordered select-xs font-normal w-full max-w-xs !h-7 !outline-0 bg-transparent"
      @change="[field.preferences ||= {}, field.preferences.format = $event.target.value, $emit('save')]"
    >
      <option
        v-for="format in numberFormats"
        :key="format"
        :value="format"
        :selected="format === field.preferences?.format || (format === 'none' && !field.preferences?.format)"
      >
        {{ formatNumber(123456789.567, format) }}
      </option>
    </select>
    <label
      :style="{ backgroundColor }"
      class="absolute -top-1 left-2.5 px-1 h-4"
      style="font-size: 8px"
    >
      {{ t('format') }}
    </label>
  </div>
  <div
    v-if="['text', 'cells'].includes(field.type) && field.validation && !validations[field.validation.pattern] && !lengthValidation"
    class="field-settings-custom-validation py-1.5 px-1 relative"
    @click.stop
  >
    <input
      ref="validationCustom"
      v-model="field.validation.pattern"
      :placeholder="t('regexp_validation')"
      dir="auto"
      class="input input-bordered input-xs w-full max-w-xs h-7 !outline-0 bg-transparent"
      @blur="$emit('save')"
    >
    <label
      v-if="field.validation.pattern"
      :style="{ backgroundColor }"
      class="absolute -top-1 left-2.5 px-1 h-4"
      style="font-size: 8px"
    >
      {{ t('regexp_validation') }}
    </label>
  </div>
  <div
    v-if="['text', 'cells'].includes(field.type) && field.validation && !validations[field.validation.pattern] && !lengthValidation"
    class="field-settings-error-message py-1.5 px-1 relative"
    @click.stop
  >
    <input
      v-model="field.validation.message"
      :placeholder="t('error_message')"
      dir="auto"
      class="input input-bordered input-xs w-full max-w-xs h-7 !outline-0 bg-transparent"
      @blur="$emit('save')"
    >
    <label
      v-if="field.validation.message"
      :style="{ backgroundColor }"
      class="absolute -top-1 left-2.5 px-1 h-4"
      style="font-size: 8px"
    >
      {{ t('error_message') }}
    </label>
  </div>
  <div
    v-if="field.type === 'date'"
    class="field-settings-date-format py-1.5 px-1 relative"
    @click.stop
  >
    <select
      v-model="field.preferences.format"
      :placeholder="t('format')"
      class="select select-bordered select-xs font-normal w-full max-w-xs !h-7 !outline-0 bg-transparent"
      @change="$emit('save')"
    >
      <option
        v-for="format in availableDateFormats"
        :key="format"
        :value="format"
      >
        {{ formatDate(new Date(), format) }}
      </option>
    </select>
    <label
      :style="{ backgroundColor }"
      class="absolute -top-1 left-2.5 px-1 h-4"
      style="font-size: 8px"
    >
      {{ t('format') }}
    </label>
  </div>
  <div
    v-if="field.type === 'signature'"
    class="field-settings-signature-format py-1.5 px-1 relative"
    @click.stop
  >
    <select
      :placeholder="t('format')"
      class="select select-bordered select-xs font-normal w-full max-w-xs !h-7 !outline-0 bg-transparent"
      @change="[field.preferences.format = $event.target.value, $emit('save')]"
    >
      <option
        value="any"
        :selected="!field.preferences?.format || field.preferences.format === 'any'"
      >
        {{ t('any') }}
      </option>
      <option
        v-for="type in signatureFormats"
        :key="type"
        :value="type"
        :selected="field.preferences?.format === type"
      >
        {{ t(type) }}
      </option>
    </select>
    <label
      :style="{ backgroundColor }"
      class="absolute -top-1 left-2.5 px-1 h-4"
      style="font-size: 8px"
    >
      {{ t('format') }}
    </label>
  </div>
  <li
    v-if="[true, false].includes(withSignatureId) && field.type === 'signature'"
    class="field-settings-signature-id"
    @click.stop
  >
    <label class="cursor-pointer py-1.5">
      <input
        :checked="field.preferences?.with_signature_id"
        type="checkbox"
        :disabled="!editable || (defaultField && [true, false].includes(defaultField.required))"
        class="toggle toggle-xs"
        @change="[field.preferences ||= {}, field.preferences.with_signature_id = $event.target.checked, $emit('save')]"
      >
      <span class="label-text">{{ t('signature_id') }}</span>
    </label>
  </li>
  <li
    v-if="withRequired && field.type !== 'phone' && field.type !== 'stamp' && field.type !== 'verification' && field.type !== 'strikethrough' && field.type !== 'heading'"
    class="field-settings-required"
    @click.stop
  >
    <label class="cursor-pointer py-1.5">
      <input
        v-model="field.required"
        type="checkbox"
        :disabled="!editable || (defaultField && [true, false].includes(defaultField.required))"
        class="toggle toggle-xs"
        @update:model-value="$emit('save')"
      >
      <span class="label-text">{{ t('required') }}</span>
    </label>
  </li>
  <li
    v-if="field.type == 'stamp'"
    class="field-settings-with-logo"
    @click.stop
  >
    <label class="cursor-pointer py-1.5">
      <input
        :checked="field.preferences?.with_logo != false"
        type="checkbox"
        class="toggle toggle-xs"
        @change="[field.preferences ||= {}, field.preferences.with_logo = field.preferences.with_logo == false, $emit('save')]"
      >
      <span class="label-text">{{ t('with_logo') }}</span>
    </label>
  </li>
  <li
    v-if="field.type == 'checkbox'"
    class="field-settings-checked"
    @click.stop
  >
    <label class="cursor-pointer py-1.5">
      <input
        v-model="field.default_value"
        type="checkbox"
        class="toggle toggle-xs"
        @update:model-value="[field.default_value = $event, field.readonly = $event, $emit('save')]"
      >
      <span class="label-text">{{ t('checked') }}</span>
    </label>
  </li>
  <li
    v-if="field.type == 'date'"
    class="field-settings-set-signing-date"
    @click.stop
  >
    <label class="cursor-pointer py-1.5">
      <input
        v-model="field.readonly"
        type="checkbox"
        class="toggle toggle-xs"
        @update:model-value="[field.default_value = $event ? '{{date}}' : null, field.readonly = $event, $emit('save')]"
      >
      <span class="label-text">{{ t('set_signing_date') }}</span>
    </label>
  </li>
  <li
    v-if="['text', 'number', 'radio', 'multiple', 'select'].includes(field.type)"
    class="field-settings-read-only"
    @click.stop
  >
    <label class="cursor-pointer py-1.5">
      <input
        v-model="field.readonly"
        type="checkbox"
        class="toggle toggle-xs"
        :disabled="!editable || (defaultField && [true, false].includes(defaultField.readonly))"
        @update:model-value="$emit('save')"
      >
      <span class="label-text">{{ t('read_only') }}</span>
    </label>
  </li>
  <li
    v-if="withPrefillable && prefillableFieldTypes.includes(field['type'])"
    class="field-settings-prefillable"
    @click.stop
  >
    <label class="cursor-pointer py-1.5">
      <input
        v-model="field.prefillable"
        type="checkbox"
        :disabled="!editable || (defaultField && [true, false].includes(defaultField.prefillable))"
        class="toggle toggle-xs"
        @update:model-value="$emit('save')"
      >
      <span class="label-text">{{ t('prefillable') }}</span>
    </label>
  </li>
  <hr
    v-if="field.type != 'stamp'"
    class="pb-0.5 mt-0.5"
  >
  <li
    v-if="['text', 'number', 'date', 'select', 'heading', 'cells'].includes(field.type)"
    class="field-settings-font"
  >
    <label
      class="label-text cursor-pointer text-center w-full flex items-center"
      @click="$emit('click-font')"
    >
      <IconTypography
        width="18"
      />
      <span class="text-sm">
        {{ t('font') }}
      </span>
    </label>
  </li>
  <li
    v-if="field.type != 'stamp' && field.type != 'heading' && field.type != 'strikethrough'"
    class="field-settings-description"
  >
    <label
      class="label-text cursor-pointer text-center w-full flex items-center"
      @click="$emit('click-description')"
    >
      <IconInfoCircle
        width="18"
      />
      <span class="text-sm">
        {{ t('description') }}
      </span>
    </label>
  </li>
  <li
    v-if="withCondition && field.type != 'stamp' && field.type != 'heading'"
    class="field-settings-condition"
  >
    <label
      class="label-text cursor-pointer text-center w-full flex items-center"
      @click="$emit('click-condition')"
    >
      <IconRouteAltLeft
        width="18"
      />
      <span class="text-sm">
        {{ t('condition') }}
      </span>
    </label>
  </li>
  <li
    v-if="field.type == 'number'"
    class="field-settings-formula"
  >
    <label
      class="label-text cursor-pointer text-center w-full flex items-center"
      @click="$emit('click-formula')"
    >
      <IconMathFunction
        width="18"
      />
      <span class="text-sm">
        {{ t('formula') }}
      </span>
    </label>
  </li>
  <hr
    v-if="(withCopyToAllPages && canCopyToAllPages) || withAreas || withCustomFields"
    class="pb-0.5 mt-0.5"
  >
  <template v-if="withAreas">
    <li
      v-for="(area, index) in sortedAreas"
      :key="index"
      class="field-settings-area"
    >
      <a
        href="#"
        class="text-sm py-1 px-2 group/1"
        @click.prevent="$emit('scroll-to', area)"
      >
        <IconShape
          :width="20"
          :stroke-width="1.6"
        />
        {{ t('page') }}
        <template v-if="template.schema.length > 1">{{ template.schema.findIndex((item) => item.attachment_uuid === area.attachment_uuid) + 1 }}-</template>{{ area.page + 1 }}
        <IconX
          :width="12"
          class="group-hover/1:inline hidden"
          @click.prevent.stop="[$emit('remove-area', area), $event.target.closest('.dropdown').querySelector('label').focus()]"
        />
      </a>
    </li>
    <li
      v-if="!field.areas?.length || !['radio', 'multiple'].includes(field.type)"
      class="field-settings-draw-new-area"
    >
      <a
        href="#"
        class="text-sm py-1 px-2"
        @click.prevent="$emit('set-draw', { field })"
      >
        <IconNewSection
          :width="20"
          :stroke-width="1.6"
        />
        {{ t('draw_new_area') }}
      </a>
    </li>
  </template>
  <li
    v-if="withCopyToAllPages && canCopyToAllPages && field.areas?.length === 1 && ['date', 'signature', 'initials', 'text', 'cells', 'stamp'].includes(field.type)"
    class="field-settings-copy-to-all-pages"
  >
    <a
      href="#"
      class="text-sm py-1 px-2"
      @click.prevent="copyToAllPages(field)"
    >
      <IconCopy
        :width="20"
        :stroke-width="1.6"
      />
      {{ t('copy_to_all_pages') }}
    </a>
  </li>
  <li
    v-if="withCustomFields"
    class="field-settings-save-as-custom-field"
  >
    <a
      href="#"
      class="text-sm py-1 px-2"
      @click.prevent="$emit('add-custom-field', field)"
    >
      <IconForms
        :width="20"
        :stroke-width="1.6"
      />
      {{ t('save_as_custom_field') }}
    </a>
  </li>
</template>

<script>
import { IconRouteAltLeft, IconTypography, IconShape, IconX, IconMathFunction, IconNewSection, IconInfoCircle, IconCopy, IconForms } from '@tabler/icons-vue'

export default {
  name: 'FieldSettings',
  components: {
    IconShape,
    IconInfoCircle,
    IconMathFunction,
    IconRouteAltLeft,
    IconForms,
    IconCopy,
    IconNewSection,
    IconTypography,
    IconX
  },
  inject: ['template', 't', 'dateFormats', 'locale'],
  props: {
    field: {
      type: Object,
      required: true
    },
    withCondition: {
      type: Boolean,
      required: false,
      default: true
    },
    withCustomFields: {
      type: Boolean,
      required: false,
      default: false
    },
    withCopyToAllPages: {
      type: Boolean,
      required: false,
      default: true
    },
    withSignatureId: {
      type: Boolean,
      required: false,
      default: null
    },
    backgroundColor: {
      type: String,
      required: false,
      default: null
    },
    defaultField: {
      type: Object,
      required: false,
      default: null
    },
    withPrefillable: {
      type: Boolean,
      required: false,
      default: false
    },
    withRequired: {
      type: Boolean,
      required: false,
      default: true
    },
    withAreas: {
      type: Boolean,
      required: false,
      default: true
    },
    editable: {
      type: Boolean,
      required: false,
      default: true
    }
  },
  emits: ['set-draw', 'scroll-to', 'click-formula', 'click-description', 'click-condition', 'click-font', 'remove-area', 'save', 'add-custom-field'],
  data () {
    return {
      selectedValidation: ''
    }
  },
  computed: {
    schemaAttachmentsIndexes () {
      return (this.template.schema || []).reduce((acc, item, index) => {
        acc[item.attachment_uuid] = index

        return acc
      }, {})
    },
    canCopyToAllPages () {
      const firstArea = this.field.areas[0]

      if (firstArea) {
        return firstArea.page !== null && firstArea.page !== undefined
      } else {
        return false
      }
    },
    numberFormats () {
      const formats = ['none', 'usd', 'eur', 'gbp', 'comma', 'dot', 'space']
      const spaceLocales = ['fr-FR', 'es-ES', 'pt-PT', 'de-DE', 'it-IT', 'nl-NL']

      formats.push(spaceLocales.includes(this.locale) ? 'percent_space' : 'percent')

      const selectedFormat = this.field.preferences?.format

      if (selectedFormat && !formats.includes(selectedFormat)) {
        formats.push(selectedFormat)
      }

      return formats
    },
    availableDateFormats () {
      const formats = this.dateFormats.length
        ? [...this.dateFormats]
        : [
            'MM/DD/YYYY',
            'DD/MM/YYYY',
            'YYYY-MM-DD',
            'DD-MM-YYYY',
            'DD.MM.YYYY',
            'MMM D, YYYY',
            'MMMM D, YYYY',
            'MMMM YYYY',
            'D MMM YYYY',
            'D MMMM YYYY'
          ]

      if (!this.dateFormats.length && (Intl.DateTimeFormat().resolvedOptions().timeZone?.includes('Seoul') || navigator.language?.startsWith('ko'))) {
        formats.push('YYYY년 MM월 DD일')
      }

      if (this.field.preferences?.format && !formats.includes(this.field.preferences.format)) {
        formats.unshift(this.field.preferences.format)
      }

      return formats
    },
    lengthValidation () {
      if (this.field.validation?.pattern && this.selectedValidation !== 'custom') {
        return this.parseLengthPattern(this.field.validation.pattern)
      } else {
        return null
      }
    },
    validations () {
      return {
        '.{0,}': 'length',
        '^[0-9]{3}-[0-9]{2}-[0-9]{4}$': 'ssn',
        '^[0-9]{2}-[0-9]{7}$': 'ein',
        '^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$': 'email',
        '^https?://.*': 'url',
        '^[0-9]{5}(?:-[0-9]{4})?$': 'zip',
        '^[0-9]+$': 'numbers_only',
        '^[a-zA-Z]+$': 'letters_only'
      }
    },
    signatureFormats () {
      return ['drawn', 'typed', 'drawn_or_typed', 'drawn_or_upload', 'upload']
    },
    verificationMethods () {
      return ['QeS', 'AeS']
    },
    prefillableFieldTypes () {
      return ['text', 'number', 'cells', 'date', 'checkbox', 'select', 'radio', 'phone']
    },
    sortedAreas () {
      return (this.field.areas || []).filter((e) => e.page !== null && e.page !== undefined).sort((a, b) => {
        return this.schemaAttachmentsIndexes[a.attachment_uuid] - this.schemaAttachmentsIndexes[b.attachment_uuid]
      })
    }
  },
  methods: {
    onChangeValidation (event) {
      if (event.target.value === 'custom') {
        this.selectedValidation = 'custom'

        this.field.validation = { pattern: '', message: '' }

        this.$nextTick(() => this.$refs.validationCustom.focus())
      } else if (event.target.value) {
        this.field.validation ||= {}
        this.field.validation.pattern =
          Object.keys(this.validations).find(key => this.validations[key] === event.target.value)

        this.selectedValidation = event.target.value
        delete this.field.validation.message
      } else {
        this.selectedValidation = ''

        delete this.field.validation
      }

      this.$emit('save')
    },
    copyToAllPages (field) {
      const areaString = JSON.stringify(field.areas[0])
      const newAreas = []
      const existingAreasIndex = field.areas.reduce((acc, area) => {
        acc[`${area.attachment_uuid}-${area.page}`] = area

        return acc
      }, {})

      this.template.schema.forEach((item) => {
        const attachment = this.template.documents.find((d) => d.uuid === item.attachment_uuid)

        const numberOfPages = attachment.metadata?.pdf?.number_of_pages || attachment.preview_images.length

        for (let page = 0; page <= numberOfPages - 1; page++) {
          const existing = existingAreasIndex[`${attachment.uuid}-${page}`]

          newAreas.push(existing || { ...JSON.parse(areaString), attachment_uuid: attachment.uuid, page })
        }
      })

      field.areas = newAreas

      this.$emit('scroll-to', this.field.areas[this.field.areas.length - 1])

      this.$emit('save')
    },
    formatNumber (number, format) {
      if (format === 'comma') {
        return new Intl.NumberFormat('en-US').format(number)
      } else if (format === 'usd') {
        return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', minimumFractionDigits: 2, maximumFractionDigits: 2 }).format(number)
      } else if (format === 'gbp') {
        return new Intl.NumberFormat('en-GB', { style: 'currency', currency: 'GBP', minimumFractionDigits: 2, maximumFractionDigits: 2 }).format(number)
      } else if (format === 'eur') {
        return new Intl.NumberFormat('fr-FR', { style: 'currency', currency: 'EUR', minimumFractionDigits: 2, maximumFractionDigits: 2 }).format(number)
      } else if (format === 'dot') {
        return new Intl.NumberFormat('de-DE').format(number)
      } else if (format === 'space') {
        return new Intl.NumberFormat('fr-FR').format(number)
      } else if (format === 'percent') {
        return `${number}%`
      } else if (format === 'percent_space') {
        return `${String(number).replace('.', ',')} %`
      } else {
        return number
      }
    },
    parseLengthPattern (pattern) {
      return pattern?.match(/^\.{(?<min>\d+),(?<max>\d+)?}$/)?.groups || null
    },
    formatDate (date, format) {
      const monthFormats = { M: 'numeric', MM: '2-digit', MMM: 'short', MMMM: 'long' }
      const dayFormats = { D: 'numeric', DD: '2-digit' }
      const yearFormats = { YYYY: 'numeric', YYY: 'numeric', YY: '2-digit' }
      const hourFormats = { H: 'numeric', HH: '2-digit', h: 'numeric', hh: '2-digit' }
      const minuteFormats = { m: 'numeric', mm: '2-digit' }
      const secondFormats = { s: 'numeric', ss: '2-digit' }

      const opts = {
        day: dayFormats[format.match(/D+/)],
        month: monthFormats[format.match(/M+/)],
        year: yearFormats[format.match(/Y+/)]
      }

      if (format.match(/H+/)) { opts.hour = hourFormats[format.match(/H+/)[0]]; opts.hour12 = false }
      if (format.match(/h+/)) { opts.hour = hourFormats[format.match(/h+/)[0]]; opts.hour12 = true }
      if (/[Aa]/.test(format) && opts.hour12 === undefined) opts.hour12 = true
      if (format.match(/m+/)) opts.minute = minuteFormats[format.match(/m+/)[0]]
      if (format.match(/s+/)) opts.second = secondFormats[format.match(/s+/)[0]]
      if (/z/.test(format)) opts.timeZoneName = 'short'

      const partTypes = {
        M: 'month',
        D: 'day',
        Y: 'year',
        H: 'hour',
        h: 'hour',
        m: 'minute',
        s: 'second',
        z: 'timeZoneName',
        A: 'dayPeriod',
        a: 'dayPeriod'
      }

      const parts = new Intl.DateTimeFormat([], opts).formatToParts(date)

      return format.replace(/MMMM|MMM|MM|M|DD|D|YYYY|YYY|YY|HH|hh|H|h|mm|m|ss|s|A|a|z/g, (token) => {
        const value = parts.find((p) => p.type === partTypes[token[0]])?.value

        if (token === 'A') return (value || '').toUpperCase()
        if (token === 'a') return (value || '').toLowerCase()

        return value
      })
    }
  }
}
</script>
