import log from "@racletteCore/lib/logger"
import {
  isAnswerToOwnRequest,
  transformOperationResults,
} from "@racletteCore/lib/dataHelper"
import * as userNotifier from "@racletteCore/lib/userNotifier"
import { $store, markDataAsModified } from "@racletteCore"

const handledRequests = {}

const resolveDeletedIds = (data) => {
  if (data.deleted?.length) {
    return data.deleted
  }
  return (data.items || [])
    .map((item) => (typeof item === "string" ? item : item?._id))
    .filter(Boolean)
}
const hasBeenHandled = (requestId, appRequestCode) => {
  if (requestId === "system") {
    return false
  }
  if (!handledRequests[requestId]) {
    handledRequests[requestId] = appRequestCode

    return false
  }
  if (handledRequests[requestId] === appRequestCode) {
    delete handledRequests[requestId]
    log.api("Call has already been handled", {
      requestId,
      appRequestCode,
      handledRequests,
    })

    return true
  }
}
const handler = {
  dataCreate: (
    data,
    isOwnRequest,
    appRequestCode = "dataCreate",
    requestId,
    direct,
    notify,
    isSystemAction,
  ) => {
    log.api("dataCreate", {
      direct,
      isOwnRequest,
      isSystemAction,
      appRequestCode,
      requestId,
      data,
    })
    $store.dispatch({ type: "data/push", payload: data.items || [] })
    if (notify) {
      userNotifier.dataCreate(isOwnRequest, isSystemAction, data.items)
    }

    return data.items
  },
  dataUpdate: (
    data,
    isOwnRequest,
    appRequestCode = "dataUpdate",
    requestId,
    direct,
    notify,
    isSystemAction,
  ) => {
    log.api("dataUpdate", {
      direct,
      isOwnRequest,
      isSystemAction,
      appRequestCode,
      requestId,
      data,
    })
    $store.dispatch({ type: "data/push", payload: data.items || [] })
    if (notify) {
      userNotifier.dataUpdate(isOwnRequest, isSystemAction, data.items)
    }

    return data.items
  },
  dataRead: (
    data,
    isOwnRequest,
    appRequestCode = "dataRead",
    requestId,
    direct,
    notify,
    isSystemAction,
  ) => {
    log.api("dataRead", {
      direct,
      isOwnRequest,
      isSystemAction,
      appRequestCode,
      requestId,
      data,
    })

    $store.dispatch({ type: "data/push", payload: data.items || [] })
    return data.items
  },
  dataReadModified: (
    data,
    isOwnRequest,
    appRequestCode = "dataReadModified",
    requestId,
    direct,
    notify,
    isSystemAction,
  ) => {
    const modifiedData = markDataAsModified(data)

    log.api("dataReadModified", {
      direct,
      isOwnRequest,
      isSystemAction,
      appRequestCode,
      requestId,
      modifiedData,
    })

    $store.dispatch({ type: "data/push", payload: modifiedData.items || [] })
    return data.items
  },
  dataDelete: (
    data,
    isOwnRequest,
    appRequestCode = "dataDelete",
    requestId,
    direct,
    notify,
    isSystemAction,
  ) => {
    log.api("dataDelete", {
      direct,
      isOwnRequest,
      isSystemAction,
      appRequestCode,
      requestId,
      data,
    })
    const deletedIds = resolveDeletedIds(data)
    $store.dispatch({ type: "data/hardDelete", payload: deletedIds })
    if (notify) {
      userNotifier.dataDelete(isOwnRequest, isSystemAction, data.items || [])
    }

    return deletedIds
  },
  dataHardDeleted: (
    data,
    isOwnRequest,
    appRequestCode = "dataHardDeleted",
    requestId,
    direct,
    notify,
    isSystemAction,
  ) => {
    log.api("dataHardDeleted", {
      direct,
      isOwnRequest,
      isSystemAction,
      appRequestCode,
      requestId,
      data,
    })
    const deletedIds = resolveDeletedIds(data)
    $store.dispatch({ type: "data/hardDelete", payload: deletedIds })
    if (notify) {
      userNotifier.dataDelete(isOwnRequest, isSystemAction, data.items || [])
    }
    return deletedIds
  },
  dataMove: (
    data,
    isOwnRequest,
    appRequestCode = "dataMove",
    requestId,
    direct,
    notify,
    isSystemAction,
  ) => {
    log.api("dataMove", {
      direct,
      isOwnRequest,
      isSystemAction,
      appRequestCode,
      requestId,
      data,
    })
    $store.dispatch({ type: "data/push", payload: data.items })
    if (notify) {
      userNotifier.dataMove(isOwnRequest, isSystemAction, data.items)
    }
    return data.items
  },
  dataRestore: (
    data,
    isOwnRequest,
    appRequestCode = "dataRestore",
    requestId,
    direct,
    notify,
    isSystemAction,
  ) => {
    log.api("dataRestore", {
      direct,
      isOwnRequest,
      isSystemAction,
      appRequestCode,
      requestId,
      data,
    })
    console.warn("not implemented yet")
    return data
  },
  projectUpdate: (
    data,
    isOwnRequest,
    appRequestCode = "projectUpdate",
    requestId,
    direct,
    notify,
    isSystemAction,
  ) => {
    log.api("projectUpdate", {
      direct,
      isOwnRequest,
      isSystemAction,
      appRequestCode,
      requestId,
      data,
    })
    const projectId = $store.getState(["project", "_id"])

    data.items.forEach((item) => {
      if (projectId === item._id) {
        // as this is the same project as we are on at the moment, let's set the updated project state
        $store.dispatch({
          type: "projects/update",
          projectId: item._id,
          payload: item,
        })
      }
      /* reenable this if we want to directly update the project values
      if (projectId === item._id) {
        // as this is the same project as we are on at the moment, let's set the updated project state
        store.dispatch({
          type: "project/set",
          payload: item._source,
        })
      }
      store.dispatch({
        type: "data/push",
        projectOverride: item._project,
        payload: [item],
      })
        */

      if (notify) {
        userNotifier.projectUpdate(isOwnRequest, isSystemAction, item)
      }
      return data.items
    })
  },
  projectConfigUpdate: (
    data,
    isOwnRequest,
    appRequestCode = "projectConfigUpdate",
    requestId,
    direct,
    notify,
    isSystemAction,
  ) => {
    log.api("projectConfigUpdate", {
      direct,
      isOwnRequest,
      isSystemAction,
      appRequestCode,
      requestId,
      data,
    })
    const projectId = $store.getState(["project", "_id"])
    data.items.forEach((item) => {
      if (item._project === projectId) {
        $store.dispatch({
          type: "project/addConfig",
          payload: item,
        })
      }

      if (notify) {
        userNotifier.projectConfigUpdate(isOwnRequest, isSystemAction, item)
      }
      return data.items
    })
  },
  userConfigUpdate: (
    data,
    isOwnRequest,
    appRequestCode = "userConfigUpdate",
    requestId,
    direct,
    notify,
    isSystemAction,
  ) => {
    log.api("userConfigUpdate", {
      direct,
      isOwnRequest,
      isSystemAction,
      appRequestCode,
      requestId,
      data,
    })
    data.items.forEach((item) => {
      $store.dispatch({
        type: "data/push",
        projectOverride: item._project,
        payload: [item],
      })
      if (notify) {
        userNotifier.userConfigUpdate(isOwnRequest, isSystemAction, item)
      }
    })
    return data.items
  },
  dataSync: (
    data,
    isOwnRequest,
    appRequestCode = "dataSendOffline",
    requestId,
    direct,
    notify,
    isSystemAction,
  ) => {
    log.api("dataSync", {
      direct,
      isOwnRequest,
      isSystemAction,
      appRequestCode,
      requestId,
      data,
    })

    const syncItemStatus = transformOperationResults(data.operations)

    log.api("dataSync implement conflict handling!", syncItemStatus)

    if (notify) {
      userNotifier.dataMergeResponse(isOwnRequest, data)
    }
    return data
  },
}
// Lets you properly add data to the dataStore for incoming items from the backend
// we distinquish between system calls, which usually don't trigger notifications, and direct own requests which usually trigger a snackbar
// indirect own which also dont trigger notifications as they are duplicates of the direct ones, and indirect and not own, which trigger notification in the notification widget
// note that appRequestCode always tells you what the frontend requested, while the function you are in is the action performed by the backend as a response to the frontend request

const handleServerItem = (data, notify, direct = false) => {
  const { appSessionId, appRequestCode, type, requestId } = data
  const isOwnRequest = isAnswerToOwnRequest(appSessionId)
  if (!handler[type]) {
    return false
  }
  if (hasBeenHandled(requestId, appRequestCode)) {
    return
  }
  return handler[type](
    data.body,
    isOwnRequest,
    appRequestCode,
    requestId,
    direct,
    notify,
    appSessionId === "system",
  )
}
export default handleServerItem
