import createStableHash from "@racletteCore/lib/createStableHash"
import log from "@racletteCore/lib/logger"
import { $store } from "@racletteCore"
import { isParentResultFresh } from "./queryCacheHelper"
const deleteTimeouts = {}
export type ParentQuery = {
  cacheKey: string
  loading?: boolean
  uuids?: Array<string>
  lastFetched?: number
  deleted?: boolean
}
const cacheMap = {}
const getAffectedCacheQuery = (state, actionId) => {
  const removedQuery = $store.getState(["queries", actionId])
  if (!removedQuery) {
    return null
  }
  const removedQueryCacheKey = createStableHash({
    target: removedQuery.target,
    params: removedQuery.params,
  })
  return state[removedQueryCacheKey]
}
const getChildQueries = (cacheKey: string): Array<Record<string, object>> => {
  const queries = $store.getState("queries")
  const childQueries = []
  Object.values(queries).forEach((query) => {
    const queryCacheKey = createStableHash({
      target: query.target,
      params: query.params,
    })
    if (cacheKey === queryCacheKey) {
      childQueries.push(query)
    }
  })
  return childQueries
}
const cacheAdd = (state, action) => {
  const newState = structuredClone(state)
  const { payload } = action
  const { target, params, cacheKey } = payload
  const parentQueryContent = {
    cacheKey,
    loading: true,
  }
  const hash =
    cacheKey ||
    createStableHash({
      target,
      params,
    })
  if (!newState[hash]) {
    newState[hash] = parentQueryContent
  } else {
    if (newState[hash].deleted) {
      if (!isParentResultFresh(hash)) {
        // it seems that this chache got requested again during it's grace delte time period. So Let's just set our loading state to true and delete the delete flag
        newState[hash].loading = true
      }

      delete newState[hash].deleted
    }
  }

  return newState
}
const cacheCheck = (state: Record<string, ParentQuery>, action) => {
  const newState = structuredClone(state)

  const { items, cacheKey, actionId } = action.payload
  cacheMap[actionId] = cacheKey
  const affectedCacheQuery = newState[cacheKey]
  if (affectedCacheQuery) {
    affectedCacheQuery.loading = false
    affectedCacheQuery.lastFetched = new Date().getTime()
    action.actionResult.relationships.resolved.push(`cacheQuery_${cacheKey}`)
    if (items) {
      affectedCacheQuery.uuids = Object.values(items).map((item) => item._id)
    }
  }

  return newState
}
const cacheRemove = (state, action) => {
  const newState = structuredClone(state)
  const { cacheKey } = action.payload
  const childQueries = getChildQueries(cacheKey)
  if (childQueries.length === 0) {
    // We have no further childs for this cache, so we will delete it and it's items
    $store.dispatch({
      type: "data/deleteFromCache",
      actionId: cacheKey,
    })
    delete newState[cacheKey]
  } else {
    // We still have child items so we will just delete the deleted flag in case we were called during grace deletion period
    log.store(
      "cacheRemove",
      `the cache with hash ${cacheKey} is still/again in use by ${childQueries.length} queries`,
    )
    delete newState[cacheKey].deleted
  }

  return newState
}

const cacheChildRemove = (state, action) => {
  const newState = structuredClone(state)
  const { actionId } = action.payload
  if (!actionId) return state

  const affectedCacheQuery = getAffectedCacheQuery(state, actionId)
  if (!affectedCacheQuery) {
    return state
  }
  const childQueries = getChildQueries(affectedCacheQuery.cacheKey)
  if (childQueries.length === 1) {
    // if the action which is currently deleted is the last one needing this cache, we will give it a 2sek grace period before we remove the cache
    newState[affectedCacheQuery.cacheKey].deleted = true
    if (deleteTimeouts[affectedCacheQuery.cacheKey]) {
      clearTimeout(deleteTimeouts[affectedCacheQuery.cacheKey])
      delete deleteTimeouts[affectedCacheQuery.cacheKey]
    }
    deleteTimeouts[affectedCacheQuery.cacheKey] = setTimeout(() => {
      $store.dispatch({
        type: "queriesCache/remove",
        payload: { cacheKey: affectedCacheQuery.cacheKey, actionId },
      })
    }, 500)
  }
  delete cacheMap[actionId]
  return newState
}
export { cacheAdd, cacheCheck, cacheChildRemove, cacheRemove }
