/**
 * queries reducers
 */

import { fixQueryParamTypes } from "@racletteCore/lib/dataHelper"
import { difference, mergeDeepRight, filter } from "ramda"
import type { CustomStoreActionParams } from "@racletteCore/types"
import { $eventbus, $store } from "@racletteCore/main"
export type Query = {
  target: string
  actionId: string
  params: Record<string, any>
  options: Record<string, any>
  config: Record<string, any>
  loading?: boolean
  backend?: Record<string, any>
  uuids?: Array<string>
}

const updateQueryItems = (state, action) => {
  const { items, actionId } = action.payload
  if (!actionId || !state[actionId]) {
    return state
  }
  const queries = structuredClone(state)
  const query = queries[actionId]
  if (items) {
    const uuids = Object.values(items).map((item) => item._id)
    let dataItemsToRemove = []

    if (query.uuids) {
      dataItemsToRemove = difference(query.uuids, uuids)
    }

    query.loading = false
    query.uuids = uuids
    if (dataItemsToRemove.length > 0) {
      $store.dispatch({
        type: "data/deleteFromCache",
        payload: { uuids: dataItemsToRemove },
        actionId: actionId,
      })
    }
  }

  return queries
}

const updateQuery = (state, action) => {
  const payload = action.payload
  const actionId = payload.actionId
  if (!actionId || !state[actionId]) {
    return state
  }

  let queries = structuredClone(state)
  const query = queries[actionId]
  if (payload.target) {
    query.target = payload.target
  }
  if (payload.options) {
    query.options = payload.options
  }
  if (payload.config) {
    query.config = payload.config
  }
  if (payload.pageItems) {
    const uuids = []
    const results = payload.pageItems || null

    if (results) {
      results.forEach((item) => uuids.push(item._id))
    }

    if (query.uuids && query.uuids.length > 0) {
      query.uuids = query.uuids.concat(uuids)
    } else {
      query.uuids = uuids
    }
  }
  if (payload.hasOwnProperty("uuids")) {
    query.uuids = payload.uuids
  }
  if (payload.hasOwnProperty("loading")) {
    query.loading = payload.loading
  }
  if (payload.hasOwnProperty("params")) {
    if (payload.reset) {
      query.params = payload.params
    } else {
      query.params = mergeDeepRight(query.params || {}, payload.params)
    }
  }
  if (payload.cacheKey) {
    query.cacheKey = payload.cacheKey
  }
  if (payload.backend) {
    if (payload.reset) {
      query.backend = payload.backend
    } else {
      query.backend = mergeDeepRight(query.backend || {}, payload.backend)
    }
  }
  query.params = fixQueryParamTypes(query.params)
  if (query.backend) {
    query.backend = fixQueryParamTypes(query.backend)
  }

  return queries
}
const queryiesSetCacheKey = (state, action) => {
  let queries = structuredClone(state)
  const payload = action.payload
  const { actionId, cacheKey } = payload
  if (!actionId || !state[actionId]) {
    return state
  }

  queries[actionId] = { ...queries[actionId], cacheKey }
  action.actionResult.updated.itemBefore = state[actionId]
  action.actionResult.updated.item = queries[actionId]
  return queries
}
const queriesUpdateLoading = (state, action) => {
  const payload = action.payload
  const actionId = payload.actionId
  if (!actionId || !state[actionId]) {
    return state
  }
  const queries = structuredClone(state)
  const query = queries[actionId]
  if (payload.hasOwnProperty("loading")) {
    query.loading = payload.loading
  }
  return queries
}
/**
 * execute queries
 */
const queriesExecute = (state, action) => {
  if (Object.keys(state).length === 0) {
    return state
  }
  const { target, params, actionId, reset, type } = structuredClone(
    action.payload,
  )
  const isResetQuery = reset || params?.reset
  const queries = structuredClone(state)

  if (actionId && queries[actionId]) {
    const query = queries[actionId]

    query.loading = true
    action.actionResult.updated.itemsBefore[actionId] = state[actionId]

    if (params && !isResetQuery) {
      query.params = { ...query.params, ...params }
    }
    if (isResetQuery) {
      query.params = params
      delete query.params.reset
      delete query.reset
    }
    if (target) {
      query.target = target
    }
    query.params = fixQueryParamTypes(query.params)
    action.actionResult.updated.items[actionId] = query
    queries[actionId] = query
  } else {
    Object.keys(queries).forEach((actionId) => {
      const query = queries[actionId]
      action.actionResult.updated.itemsBefore[actionId] = state[actionId]
      if (
        !type ||
        (type === "find" && query.params.find) ||
        (type === "search" && query.params.search)
      ) {
        query.loading = true
        query.params = fixQueryParamTypes(query.params)
        action.actionResult.updated.itemsBefore[actionId] = state[actionId]
        action.actionResult.updated.items[actionId] = query
      }
    })
  }

  return queries
}
/**
 * add queries
 */
const queriesAdd = (state, action) => {
  if (!action.payload.actionId) {
    return state
  }
  const payload = structuredClone(action.payload)
  const params: CustomStoreActionParams = payload.params || {}
  const actionId = payload.actionId
  const target = payload.target
  const config = payload.config || {}
  const options = payload.options || {}

  const query: Query = {
    target,
    params,
    actionId,
    options,
    config,
  }

  action.actionResult.added.push(query)
  const queries = structuredClone(state)
  queries[actionId] = query

  return queries
}

/**
 * remove queries
 */
const queriesRemove = (state, action) => {
  let queries = structuredClone(state)
  const { actionId } = action.payload
  if (actionId && queries[actionId]) {
    const query = queries[actionId] || {}
    action.actionResult.deleted.hard[actionId] = query
    if (query?.uuids?.length) {
      // when we delete a query we will now have to remove all data Items which have been retrieven ONLY for this query. This will be determined in dataDelete reducer
      $store.dispatch({
        type: "data/deleteFromCache",
        payload: { uuids: query.uuids },
        actionId: actionId,
      })
    }

    $eventbus.emit("query_deleted_" + actionId, structuredClone(query))
    // check for parent query and if there are more than this actionId childs of it
    delete queries[actionId]
  }

  return queries || {}
}

export {
  queryiesSetCacheKey,
  queriesExecute,
  queriesAdd,
  queriesRemove,
  updateQueryItems,
  updateQuery,
  queriesUpdateLoading,
}
