/**
 * data api for the store
 */
import getData from "@racletteCore/lib/data/fetchDataHandler"
import handleServerItem from "./responseTypeHandler"
import $queryApi from "./queryApi"
import writeData from "./writeDataHandler"
import type {
  CustomStoreActionsPayload,
  QueryParamsInput,
} from "@racletteCore/types"
import { resolveQueryParams } from "@racletteCore/lib/dataHelper"
import { BehaviorSubject } from "rxjs"
import { mergeDeepRight } from "ramda"

const $writeDataApi = (
  actionId: String,
  operationDefinition: object,
  { params = {}, options = {} }: CustomStoreActionsPayload = {},
) => {
  // Create BehaviorSubjects for state management
  const isLoadingSubject$ = new BehaviorSubject<boolean>(false)
  const dataSubject$ = new BehaviorSubject([])
  const errorSubject$ = new BehaviorSubject(null)
  const responseSubject$ = new BehaviorSubject({})
  const writeApi = {
    isLoading$: isLoadingSubject$.asObservable(),
    data$: dataSubject$.asObservable(),
    error$: errorSubject$.asObservable(),
    response$: responseSubject$.asObservable(),
    execute: async (executeParams?: QueryParamsInput | FormData | unknown[]) => {
      const isFormData =
        typeof FormData !== "undefined" && executeParams instanceof FormData
      errorSubject$.next(null)
      responseSubject$.next({})
      let queryParams
      if (isFormData) {
        queryParams = executeParams
      } else if (Array.isArray(executeParams)) {
        const baseParams = resolveQueryParams(params)
        queryParams = Array.isArray(baseParams)
          ? baseParams.concat(executeParams)
          : executeParams
      } else if (executeParams === undefined) {
        queryParams = resolveQueryParams(params)
      } else {
        queryParams = mergeDeepRight(
          resolveQueryParams(params),
          resolveQueryParams(executeParams as QueryParamsInput),
        )
      }

      if (options.responseType === "stream") {
        return writeData(actionId, operationDefinition, queryParams, options)
      }

      isLoadingSubject$.next(true)
      const resultData = await writeData(
        actionId,
        operationDefinition,
        queryParams,
        options,
      ).catch((e) => {
        errorSubject$.next(e.response)
        isLoadingSubject$.next(false)
      })

      // Update both boolean and observable on success
      responseSubject$.next(resultData?.response)
      isLoadingSubject$.next(false)

      // Update data if resultData is available
      if (resultData?.response) {
        const dataArray = Array.isArray(resultData.response)
          ? resultData.response
          : [resultData.response]
        dataSubject$.next(dataArray)
      }

      if (options.cb && typeof options.cb === "function") {
        options.cb(resultData)
      }
      return resultData
    },
    // Method to complete the subjects when cleanup is needed
    remove: () => {
      isLoadingSubject$.complete()
      dataSubject$.complete()
      errorSubject$.complete()
      responseSubject$.complete()
    },
    clear: () => {
      dataSubject$.next([])
      errorSubject$.next({})
      isLoadingSubject$.next(false)
    },
  }

  if (options.immediate === true) {
    writeApi.execute()
  }

  return writeApi
}
export { handleServerItem, getData, $writeDataApi, $queryApi }
