<template>
  <div>
    <div v-if="selectedSheetIndex === null && spreadsheet">
      <form @submit.prevent="[selectedSheetIndex = $refs.selectWorksheet.value, buildDefaultMappings()]">
        <label class="label">
          {{ t('select_worksheet') }}
        </label>
        <select
          ref="selectWorksheet"
          class="base-select"
        >
          <option
            v-for="(sheet, index) in spreadsheet"
            :key="index"
            :value="index"
          >
            {{ sheet[0] || index }}
          </option>
        </select>
        <button class="base-button mt-4 w-full">
          {{ t('open') }}
        </button>
      </form>
    </div>
    <div v-else-if="selectedSheetIndex !== null">
      <div
        v-for="submitter in submitters"
        :key="submitter.uuid"
        class="mb-4"
      >
        <div
          v-if="submitters.length > 1"
          class="px-3 border-y py-2 border-base-300 text-center w-full"
        >
          {{ submitter.name }}
        </div>
        <div class="flex">
          <div class="relative w-full py-2 px-2 text-sm">
            {{ t('recipient_field') }}
          </div>
          <div class="relative w-full py-2 pl-4 text-sm">
            {{ t('spreadsheet_column') }}
          </div>
        </div>
        <div
          v-for="mapping in mappings.filter((m) => m.submitter_uuid === submitter.uuid)"
          :key="mapping.uuid"
          class="mb-2"
        >
          <div class="flex">
            <select
              class="base-select !select-sm !h-10"
              :class="{ '!text-gray-300': !mapping.field_name }"
              required
              @change="mapping.field_name = $event.target.value"
            >
              <option
                disabled
                value=""
                :selected="!mapping.field_name"
              >
                {{ t('select_field') }}
              </option>
              <option
                v-for="(field, index) in selectFieldsForSubmitter(submitter)"
                :key="index"
                :value="field.name"
                :selected="mapping.field_name === field.name"
                class="text-base-content"
              >
                {{ field.name }}
              </option>
            </select>
            <div class="flex items-center px-1">
              <IconArrowsHorizontal style="width: 19px; height: 19px" />
            </div>
            <div class="w-full relative">
              <select
                class="base-select !select-sm !h-10"
                :class="{ '!text-gray-300': !mapping.column_index && mapping.column_index != 0 }"
                required
                @change="mapping.column_index = parseInt($event.target.value)"
              >
                <option
                  disabled
                  value=""
                  :selected="mapping.column_index == null"
                >
                  {{ t('select_column') }}
                </option>
                <template
                  v-for="(column, index) in columns"
                  :key="index"
                >
                  <option
                    v-if="column"
                    :value="index"
                    :selected="index === mapping.column_index"
                    class="text-base-content"
                  >
                    {{ column }}
                  </option>
                </template>
              </select>
              <div
                v-if="mapping.column_index != null"
                class="absolute top-0 bottom-0 right-1 flex items-center"
              >
                <span
                  class="tooltip tooltip-bottom-end pr-1 tooltip-pre"
                  style="padding-top: 2px"
                  :data-tip="[0, 1, 2].map((i) => rows[i]?.[mapping.column_index] ?? '---').join('\n')"
                >
                  <button
                    class="btn btn-xs btn-circle bg-white border-0 border-gray-300"
                    @click.prevent
                  >
                    <IconInfoCircle class="h-4 w-4" />
                  </button>
                </span>
              </div>
            </div>
            <div class="flex items-center pl-1">
              <span
                class="tooltip tooltip-left"
                :data-tip="t('remove')"
              >
                <button
                  :disabled="mappings.filter((m) => m.submitter_uuid === submitter.uuid).length < 2"
                  class="btn btn-xs btn-circle"
                  @click.prevent="mappings.splice(mappings.indexOf(mapping), 1)"
                >
                  <IconX class="h-3.5 w-3.5" />
                </button>
              </span>
            </div>
          </div>
        </div>
        <div>
          <button
            class="btn btn-sm btn-primary w-full !normal-case font-medium"
            @click.prevent="addMapping(submitter)"
          >
            <IconPlus class="w-4 h-4" />
            {{ t('new_field_mapping') }}
          </button>
        </div>
      </div>
      <div>
        <input
          name="submissions_json"
          hidden
          :value="multitenant ? JSON.stringify(submissionsData.slice(0, 1100)) : JSON.stringify(submissionsData)"
        >
      </div>
      <div
        class="px-3 border-y py-2 border-base-300 text-center w-full text-sm font-semibold"
      >
        {{ t('total_entries') }}: {{ submissionsData.length }}
        <template v-if="multitenant && submissionsData.length >= 1000">
          / 1000
        </template>
      </div>
    </div>
    <div
      v-else
    >
      <div
        class="flex h-52 w-full"
        @dragover.prevent
        @drop.prevent="onDropFiles"
      >
        <label
          class="w-full relative bg-base-200/20  hover:bg-base-200/30 rounded-md border border-2 border-base-content/10 border-dashed"
          for="import_list_file"
          :class="{ 'opacity-50': isLoading }"
        >
          <div class="absolute top-0 right-0 left-0 bottom-0 flex items-center justify-center">
            <div class="flex flex-col items-center">
              <IconInnerShadowTop
                v-if="isLoading"
                class="animate-spin"
                :width="40"
                :height="40"
              />
              <IconCloudUpload
                v-else
                :width="40"
                :height="40"
              />
              <div
                class="font-medium text-lg mb-1"
              >
                {{ t('upload_csv_or_xlsx_spreadsheet') }}
              </div>
              <div class="text-sm">
                <span class="font-medium">{{ t('click_to_upload') }}</span> {{ t('or_drag_and_drop_files') }}
              </div>
            </div>
          </div>
          <form
            ref="form"
            class="hidden"
          >
            <input
              id="import_list_file"
              ref="input"
              type="file"
              name="file"
              accept=".xlsx, .xls, .csv"
              @change="onSelectFile"
            >
          </form>
        </label>
      </div>
      <div class="text-center mt-2">
        {{ t('or') }} <a
          :download="`${template.name}.csv`"
          :href="`data:text/csv;base64,${csvBase64}`"
          class="link font-medium"
        >{{ t('download') }}</a> {{ t('a_sample_spreadsheet_to_fill_and_import') }}
      </div>
    </div>
  </div>
</template>

<script>
import { IconCloudUpload, IconX, IconPlus, IconArrowsHorizontal, IconInfoCircle, IconInnerShadowTop } from '@tabler/icons-vue'
import { v4 } from 'uuid'

export default {
  name: 'FileDropzone',
  components: {
    IconCloudUpload,
    IconX,
    IconArrowsHorizontal,
    IconPlus,
    IconInfoCircle,
    IconInnerShadowTop
  },
  props: {
    template: {
      type: Object,
      required: true
    },
    multitenant: {
      type: Boolean,
      required: false,
      default: false
    },
    authenticityToken: {
      type: String,
      required: false,
      default: ''
    },
    i18n: {
      type: Object,
      required: false,
      default: () => ({})
    }
  },
  data () {
    return {
      isLoading: false,
      spreadsheet: null,
      selectedSheetIndex: null,
      mappings: []
    }
  },
  computed: {
    table () {
      return this.spreadsheet[this.selectedSheetIndex][1]
    },
    submissionsData () {
      const submissions = []

      this.rows.forEach((row) => {
        const submittersIndex = {}

        this.mappings.forEach((mapping) => {
          if (mapping.field_name && mapping.column_index != null) {
            submittersIndex[mapping.submitter_uuid] ||= {
              uuid: mapping.submitter_uuid,
              role: this.submitters.find((s) => s.uuid === mapping.submitter_uuid).name,
              fields: []
            }

            if (['name', 'email', 'phone', 'external_id'].includes(mapping.field_name.toLowerCase())) {
              submittersIndex[mapping.submitter_uuid][mapping.field_name.toLowerCase()] = row[mapping.column_index]
            }

            const fieldType = this.fieldTypesIndex[mapping.submitter_uuid]?.[mapping.field_name]

            if (fieldType && fieldType !== 'phone') {
              submittersIndex[mapping.submitter_uuid].fields.push({
                name: mapping.field_name, default_value: row[mapping.column_index], readonly: true
              })
            }
          }
        })

        if (Object.keys(submittersIndex).length !== 0) {
          submissions.push({ submitters: this.submitters.map((s) => submittersIndex[s.uuid]).filter(Boolean) })
        }
      })

      return submissions
    },
    csvBase64 () {
      const rows = []

      this.submitters.forEach((submitter) => {
        this.selectFieldsForSubmitter(submitter).forEach((field) => {
          rows.push(this.submitters.length > 1 ? `${submitter.name} - ${field.name}` : field.name)
        })
      })

      const csv = rows.map(item => {
        if (/[",\n]/.test(item)) {
          return `"${item.replace(/"/g, '""')}"`
        } else {
          return item
        }
      }).join(',')

      return window.btoa(window.unescape(window.encodeURIComponent(csv + '\n' + rows.map(() => '').join(',') + '\n')))
    },
    submitters () {
      return this.template.submitters
    },
    fieldTypesIndex () {
      return this.template.fields.reduce((acc, field) => {
        acc[field.submitter_uuid] ||= {}

        if (field.name) {
          acc[field.submitter_uuid][field.name] = field.type
        }

        return acc
      }, {})
    },
    columns () {
      return this.table[0]
    },
    form () {
      return this.$el.closest('form')
    },
    fieldTypes () {
      return ['text', 'cells', 'date', 'number', 'radio', 'select', 'checkbox', 'image']
    },
    defaultFields () {
      return [
        { name: 'Name' },
        { name: 'Email' },
        { name: 'Phone' }
      ]
    },
    rows () {
      return this.table.slice(1)
    }
  },
  watch: {
    selectedSheetIndex (value) {
      if (value !== null) {
        document.getElementById('list_form_buttons')?.classList?.remove('hidden')
      }
    }
  },
  methods: {
    t (key) {
      return this.i18n[key] || key
    },
    onDropFiles (e) {
      this.uploadFile(e.dataTransfer.files[0])
    },
    onSelectFile (e) {
      this.uploadFile(e.target.files[0])
    },
    addMapping (submitter) {
      this.mappings.push({ uuid: v4(), field_name: '', column_index: null, submitter_uuid: submitter.uuid })
    },
    selectFieldsForSubmitter (submitter) {
      const templateFields = this.template.fields.filter((field) => {
        return field.submitter_uuid === submitter.uuid &&
          field.name &&
          this.fieldTypes.includes(field.type) &&
          this.defaultFields.every((f) => field.name?.toLowerCase() !== f.name?.toLowerCase())
      })

      const fields = [...this.defaultFields, ...templateFields]

      if (this.spreadsheet && this.columns.includes('external_id')) {
        fields.push({ name: 'external_id' })
      }

      return fields
    },
    buildDefaultMappings () {
      this.submitters.forEach((submitter) => {
        const fields = this.selectFieldsForSubmitter(submitter)

        fields.forEach((field) => {
          const columnIndex = this.columns.findIndex((column, index) => {
            return column &&
              column.toString().toLowerCase().includes(field.name?.toLowerCase()) &&
              this.mappings.every((m) => m.column_index !== index)
          })

          if (columnIndex !== -1 && this.rows.some((row) => row[columnIndex])) {
            this.mappings.push({ uuid: v4(), field_name: field.name, column_index: columnIndex, submitter_uuid: submitter.uuid })
          }
        })

        if (!this.mappings.some((m) => m.field_name.toLowerCase() === 'name' && m.submitter_uuid === submitter.uuid)) {
          this.mappings.unshift({ uuid: v4(), field_name: 'Name', submitter_uuid: submitter.uuid })
        }

        if (!this.mappings.some((m) => m.field_name.toLowerCase() === 'email' && m.submitter_uuid === submitter.uuid)) {
          this.mappings.unshift({ uuid: v4(), field_name: 'Email', submitter_uuid: submitter.uuid })
        }
      })
    },
    uploadFile (file) {
      this.isLoading = true

      const formData = new FormData()

      formData.append('file', file)

      return fetch('/upload_spreadsheet', {
        method: 'POST',
        body: formData,
        headers: {
          'X-CSRF-Token': this.authenticityToken
        }
      }).then(resp => resp.json()).then((data) => {
        if (data.error) {
          return alert(data.error)
        }

        this.spreadsheet = data

        if (data.length === 1) {
          this.selectedSheetIndex = 0

          this.buildDefaultMappings()
        }
      }).finally(() => {
        this.isLoading = false
      })
    }
  }
}
</script>
