import {
  QueryKey,
  useMutation,
  UseMutationResult,
  useQueryClient,
  useSuspenseQuery,
  UseSuspenseQueryResult,
} from '@tanstack/react-query'
import {IDrink} from '../models/IDrink.ts'
import {
  DRINK_STORAGE_KEY,
  persistToDatabase,
  retrieveFromDatabase,
} from './NIET_OPENEN_WORDT_GEBRUIKT_DOOR_DE_API_FILES/databaseSimulation.ts'

interface MutationContext<T> {
  oldData: T
  queryKey: QueryKey
}

//region Mutations & queries

/**
 * ---------------------------------------------------------------------------------------------------------------------
 *                                          MUTATIONS & QUERIES
 * ---------------------------------------------------------------------------------------------------------------------
 */

export const useGetDrinks = (): UseSuspenseQueryResult<IDrink[], Error> => {
  return useSuspenseQuery({
    queryKey: ['drinks'],
    queryFn: getDrinks,
  })
}

export const useGetDrink = (id: number): UseSuspenseQueryResult<IDrink, Error> => {
  return useSuspenseQuery({
    queryKey: ['drinks', id],
    queryFn: () => getDrink(id),
  })
}

export const useDeleteDrink = (): UseMutationResult<void, Error, DeleteDrinkParams, MutationContext<IDrink[]>> => {
  const queryClient = useQueryClient()
  return useMutation({
    mutationFn: deleteDrink,
    onSettled: () => queryClient.invalidateQueries({queryKey: ['drinks']}),
  })
}

export const useUpdateDrink = (): UseMutationResult<IDrink, Error, IDrink, MutationContext<IDrink>> => {
  const queryClient = useQueryClient()
  return useMutation({
    mutationFn: updateDrink,
    onMutate: async (drink: IDrink) => {
      const queryKey = ['drinks', drink.id]
      await queryClient.cancelQueries({queryKey})
      const oldData = queryClient.getQueryData<IDrink>(queryKey)!
      queryClient.setQueryData<IDrink>(queryKey, {...drink})
      return {oldData, queryKey}
    },
    onError: (_error, _variables, context) => {
      if (context) {
        queryClient.setQueryData<IDrink>(context.queryKey, context.oldData)
      }
    },
  })
}

//endregion

//region API functions

/**
 * ---------------------------------------------------------------------------------------------------------------------
 *                                          API functions
 * ---------------------------------------------------------------------------------------------------------------------
 */

/**
 * Retrieve all drinks from the database.
 */
const getDrinks = (): Promise<IDrink[]> => {
  return retrieveFromDatabase<IDrink[]>(DRINK_STORAGE_KEY)
}

/**
 * Retrieve a specific drink from the database.
 *
 * @param id The id of the book to retrieve.
 */
const getDrink = async (id: number): Promise<IDrink> => {
  const drinks = await getDrinks()
  return drinks.find(s => s.id === id)!
}

interface DeleteDrinkParams {
  ids: number[]
}

/**
 * Delete one or more drinks from the database.

 * @param ids An array containing the ids of the drinks to delete.
 */
const deleteDrink = async ({ids}: DeleteDrinkParams): Promise<void> => {
  const drinks = await getDrinks()
  const drinkIds = new Set(ids)
  const newDrinks = drinks.filter(d => !drinkIds.has(d.id))
  await persistToDatabase(DRINK_STORAGE_KEY, newDrinks)
}

/**
 * Update a drink in the database.
 *
 * @param drink The drink to update.
 * @return The updated drink.
 */
const updateDrink = async (drink: IDrink): Promise<IDrink> => {
  const drinks = await retrieveFromDatabase<IDrink[]>(DRINK_STORAGE_KEY, false)
  const drinkIndex = drinks.findIndex(d => d.id === drink.id)
  if (drinkIndex !== -1) {
    drinks[drinkIndex] = drink
    await persistToDatabase(DRINK_STORAGE_KEY, drinks, false)
  }
  return structuredClone(drink)
}

//endregion
