<template>
  <RowEditModal
    ref="modal"
    :read-only="readOnly"
    :database="database"
    :table="table"
    :rows="[]"
    :fields="fields"
    :visible-fields="fields"
    :fields-sortable="fieldsSortable"
    :can-modify-fields="canModifyFields"
    @hidden="$emit('hidden', $event)"
    @update="update"
  ></RowEditModal>
</template>

<script>
import { DatabaseApplicationType } from '@baserow/modules/database/applicationTypes'
import RowEditModal from '@baserow/modules/database/components/row/RowEditModal'
import FieldService from '@baserow/modules/database/services/field'
import RowService from '@baserow/modules/database/services/row'
import { populateField } from '@baserow/modules/database/store/field'
import { notifyIf } from '@baserow/modules/core/utils/error'
import {
  extractRowReadOnlyValues,
  prepareNewOldAndUpdateRequestValues,
} from '@baserow/modules/database/utils/row'

/**
 * This component can open the row edit modal having the fields of that table in the
 * fields store. It will make a request to the backend fetching the missing
 * information.
 */
export default {
  name: 'ForeignRowEditModal',
  components: { RowEditModal },
  props: {
    tableId: {
      type: Number,
      required: true,
    },
    fieldsSortable: {
      type: Boolean,
      required: false,
      default: () => true,
    },
    canModifyFields: {
      type: Boolean,
      required: false,
      default: () => true,
    },
    readOnly: {
      type: Boolean,
      required: false,
      default: false,
    },
  },
  data() {
    return {
      fetchedTableAndFields: false,
      table: {},
      fields: [],
    }
  },
  computed: {
    database() {
      const databaseType = DatabaseApplicationType.getType()
      for (const application of this.$store.getters['application/getAll']) {
        if (application.type !== databaseType) {
          continue
        }

        const foundTable = application.tables.find(
          ({ id }) => id === this.tableId
        )

        if (foundTable) {
          return application
        }
      }

      return undefined
    },
  },
  methods: {
    async fetchTableAndFields() {
      // Find the table in the applications to prevent a request to the backend and to
      // maintain reactivity with the real time updates.
      const databaseType = DatabaseApplicationType.getType()
      for (const application of this.$store.getters['application/getAll']) {
        if (application.type !== databaseType) {
          continue
        }

        const foundTable = application.tables.find(
          ({ id }) => id === this.tableId
        )

        if (foundTable) {
          this.table = foundTable
          break
        }
      }

      // Because we don't have the fields in the store we need to fetch those for this
      // table.
      const { data: fieldData } = await FieldService(this.$client).fetchAll(
        this.tableId
      )
      fieldData.forEach((part, index) => {
        populateField(fieldData[index], this.$registry)
      })
      this.fields = fieldData

      // Mark the table and fields as fetched, so that we don't have to do that a
      // second time when the user opens another row.
      this.fetchedTableAndFields = true
    },

    async show(rowId) {
      if (!this.fetchedTableAndFields) {
        await this.fetchTableAndFields()
      }

      const { data: rowData } = await RowService(this.$client).get(
        this.tableId,
        rowId
      )
      this.$refs.modal.show(rowData.id, rowData)
    },
    /**
     * A method which listens for `update` event emitted by other components
     * (f. ex. RowEditModalFieldsList). It updates the foreign row that's being
     * edited in a real-time manner.
     */
    async update({ field, row, value, oldValue, table }) {
      const { newRowValues, oldRowValues, updateRequestValues } =
        prepareNewOldAndUpdateRequestValues(
          row,
          this.fields,
          field,
          value,
          oldValue,
          this.$registry
        )

      this.$store.dispatch('rowModal/updated', {
        tableId: this.tableId,
        values: newRowValues,
      })

      // Attempt to call the `rowModal/updated` action in the store which is
      // usually called when we receive a real time row update event, this updates
      // the foreign row that's being edited in a real-time manner:
      try {
        const { data } = await RowService(this.$client).update(
          table.id,
          row.id,
          updateRequestValues
        )
        const readOnlyData = extractRowReadOnlyValues(
          data,
          this.fields,
          this.$registry
        )
        this.$store.dispatch('rowModal/updated', {
          tableId: this.tableId,
          values: readOnlyData,
        })
        this.$emit('refresh-row')
        // In case the above fails, previous row values should be restored:
      } catch (error) {
        this.$store.dispatch('rowModal/updated', {
          tableId: this.tableId,
          values: oldRowValues,
        })
        notifyIf(error, 'row')
      }
    },
  },
}
</script>