import { updateRowMetadataType } from '@baserow/modules/database/utils/row'

/**
 * This store exists to always keep a copy of the row that's being edited via the
 * row edit modal. It sometimes happen that row from the original source, where it was
 * reactive with doesn't exist anymore. To make sure the modal still works in that
 * case, we always store a copy here and if it doesn't exist in the original data
 * source it accepts real time updates. This store can handle multiple row edit
 * modals being open because the rows are divided by the unique component id.
 */
export const state = () => ({
  // The key of the rows property is the unique component id indicating to which row
  // edit modal the entry is related to. The value looks like:
  // {
  //   tableId: -1,
  //   // row id
  //   id: -1,
  //   // Indicates whether the row exists in the `rows` property in the row edit modal.
  //   exists: true,
  //   // The values of the row.
  //   row: {}
  // }
  rows: {},
})

export const mutations = {
  CLEAR(state, componentId) {
    delete state.rows[componentId]
  },
  OPEN(state, { componentId, tableId, id, exists, row }) {
    state.rows = {
      ...state.rows,
      ...{
        [componentId]: {
          tableId,
          id,
          exists,
          row,
        },
      },
    }
  },
  SET_EXISTS(state, { componentId, value }) {
    state.rows[componentId] = {
      ...state.rows[componentId],
      ...{ exists: value },
    }
  },
  REPLACE_ROW(state, { componentId, row }) {
    state.rows[componentId] = {
      ...state.rows[componentId],
      ...{ row },
    }
  },
  UPDATE_ROW(state, { componentId, row }) {
    Object.assign(state.rows[componentId].row, row)
  },
  UPDATE_ROW_METADATA(state, { rowId, rowMetadataType, updateFunction }) {
    Object.values(state.rows)
      .filter((data) => data.row.id === rowId)
      .forEach((data) =>
        updateRowMetadataType(data.row, rowMetadataType, updateFunction)
      )
  },
}

export const actions = {
  clear({ commit }, { componentId }) {
    commit('CLEAR', componentId)
  },
  /**
   * Is called when the row edit modal is being opened. It will register the row
   * values in this store so that it can also receive real time updates if it's
   * managed by the `rows` prop in the row edit modal.
   */
  open({ commit }, { componentId, tableId, id, exists, row }) {
    commit('OPEN', { componentId, tableId, id, exists, row })
  },
  /**
   * Marking the row as does not exist makes it managed by this store instead of the
   * provided rows. This will make sure that it accepts real time update events.
   */
  doesNotExist({ commit }, { componentId }) {
    commit('SET_EXISTS', { componentId, value: false })
  },
  doesExist({ commit }, { componentId, row }) {
    commit('SET_EXISTS', { componentId, value: true })
    commit('REPLACE_ROW', { componentId, row })
  },
  replace({ commit }, { componentId, row }) {
    commit('REPLACE_ROW', { componentId, row })
  },
  /**
   * Called when we receive a real time row update event. It loops over all the rows
   * we have in memory here and checks if the updated row exists and if it's not
   * managed by the `rows` prop in the row edit modal. If so, it will make the
   * update. If the row is managed by the `rows` prop we don't have to do the update
   * because it will be done via `rows` property.
   */
  updated({ commit, getters }, { tableId, values }) {
    const rows = getters.getRows
    Object.keys(rows).forEach((key) => {
      const value = rows[key]
      if (
        value !== null &&
        value.tableId === tableId &&
        value.id === values.id &&
        !value.exists
      ) {
        commit('UPDATE_ROW', { componentId: key, row: values })
      }
    })
  },
  /**
   * If a row is open in the modal but it's not present in the buffer, we need to
   * manually update the metadata of the row. This is used for example to update the
   * notification_mode setting of a row.
   */
  updateRowMetadata({ commit }, { rowId, rowMetadataType, updateFunction }) {
    commit('UPDATE_ROW_METADATA', { rowId, rowMetadataType, updateFunction })
  },
}

export const getters = {
  getRows(state) {
    return state.rows
  },
  get: (state) => (componentId) => {
    if (!Object.prototype.hasOwnProperty.call(state.rows, componentId)) {
      return {
        id: -1,
        tableId: -1,
        exists: false,
        row: {},
      }
    }

    return state.rows[componentId]
  },
}

export default {
  namespaced: true,
  state,
  getters,
  actions,
  mutations,
}