<template>
  <DefaultField
    :field="currentField"
    :errors="errors"
    :full-width-content="
      fullWidthContent || ['modal', 'action-modal'].includes(mode)
    "
    :show-help-text="showHelpText"
  >
    <template #field>
      <FormKeyValueTable
        :edit-mode="!currentlyIsReadonly"
        :can-delete-row="currentField.canDeleteRow"
      >
        <FormKeyValueHeader
          :key-label="currentField.keyLabel"
          :value-label="currentField.valueLabel"
        />

        <div class="bg-white dark:bg-gray-800 overflow-hidden key-value-items">
          <FormKeyValueItem
            v-for="(item, index) in theData"
            :index="index"
            @remove-row="removeRow"
            :item.sync="item"
            :key="item.id"
            :ref="item.id"
            :read-only="currentlyIsReadonly"
            :read-only-keys="currentField.readonlyKeys"
            :can-delete-row="currentField.canDeleteRow"
          />
        </div>
      </FormKeyValueTable>

      <div class="flex items-center justify-center">
        <Button
          v-if="
            !currentlyIsReadonly &&
            !currentField.readonlyKeys &&
            currentField.canAddRow
          "
          @click="addRowAndSelect"
          :dusk="`${field.attribute}-add-key-value`"
          leading-icon="plus-circle"
          variant="link"
        >
          {{ currentField.actionText }}
        </Button>
      </div>
    </template>
  </DefaultField>
</template>

<script>
import findIndex from 'lodash/findIndex'
import fromPairs from 'lodash/fromPairs'
import map from 'lodash/map'
import reject from 'lodash/reject'
import tap from 'lodash/tap'
import { DependentFormField, HandlesValidationErrors } from '@/mixins'
import { Button } from 'laravel-nova-ui'

function guid() {
  var S4 = function () {
    return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1)
  }
  return (
    S4() +
    S4() +
    '-' +
    S4() +
    '-' +
    S4() +
    '-' +
    S4() +
    '-' +
    S4() +
    S4() +
    S4()
  )
}

export default {
  mixins: [HandlesValidationErrors, DependentFormField],

  components: {
    Button,
  },

  data: () => ({ theData: [] }),

  mounted() {
    this.populateKeyValueData()
  },

  methods: {
    /*
     * Set the initial value for the field
     */
    populateKeyValueData() {
      this.theData = map(Object.entries(this.value || {}), ([key, value]) => ({
        id: guid(),
        key: `${key}`,
        value,
      }))

      if (this.theData.length === 0) {
        this.addRow()
      }
    },

    /**
     * Provide a function that fills a passed FormData object with the
     * field's internal value attribute.
     */
    fill(formData) {
      this.fillIfVisible(
        formData,
        this.fieldAttribute,
        JSON.stringify(this.finalPayload)
      )
    },

    /**
     * Add a row to the table.
     */
    addRow() {
      return tap(guid(), id => {
        this.theData = [...this.theData, { id, key: '', value: '' }]
        return id
      })
    },

    /**
     * Add a row to the table and select its first field.
     */
    addRowAndSelect() {
      return this.selectRow(this.addRow())
    },

    /**
     * Remove the row from the table.
     */
    removeRow(id) {
      return tap(
        findIndex(this.theData, row => row.id === id),
        index => this.theData.splice(index, 1)
      )
    },

    /**
     * Select the first field in a row with the given ref ID.
     */
    selectRow(refId) {
      return this.$nextTick(() => {
        this.$refs[refId][0].handleKeyFieldFocus()
      })
    },

    onSyncedField() {
      this.populateKeyValueData()
    },
  },

  computed: {
    /**
     * Return the final filtered json object
     */
    finalPayload() {
      return fromPairs(
        reject(
          map(this.theData, row =>
            row && row.key ? [row.key, row.value] : undefined
          ),
          row => row === undefined
        )
      )
    },
  },
}
</script>
