import { useState, useEffect } from "react"

/**
 * Hook for managing URL query parameters
 * @template TParams - The shape of the query parameters.
 * @param {object} [options] - Configuration options.
 * @param {boolean} [options.deleteFalseValues=false] - Whether to delete the parameter if the parameter value is a false value.
 * @param {'replace' | 'push'} [options.method='push'] - Method to use for history updates.
 * @returns {object} - An object containing methods to set, append, get, delete query parameters and searchParams.
 */
export const useStatefulParams = <TParams extends Record<string, any>>(
  options: {
    method?: "replace" | "push"
    deleteFalseValues?: boolean
  } = {}
) => {
  const { method = "push", deleteFalseValues = true } = options
  const [searchParams, setSearchParams] = useState<URLSearchParams>(() => {
    return new URLSearchParams(window.location.search)
  })

  useEffect(() => {
    const handlePopState = () => {
      const newParams = new URLSearchParams(window.location.search)
      setSearchParams(newParams)
    }
    window.addEventListener("popstate", handlePopState)
    return () => window.removeEventListener("popstate", handlePopState)
  }, [])

  const updateUrl = (newParams: URLSearchParams) => {
    const newParamsString = newParams.toString()
    const currentParamsString = searchParams.toString()

    if (newParamsString !== currentParamsString) {
      const newUrl = new URL(window.location.href)
      newUrl.search = newParamsString

      if (method === "replace") {
        window.history.replaceState(null, "", newUrl)
      } else {
        window.history.pushState(null, "", newUrl)
      }

      setSearchParams(newParams)
    }
  }

  const set = (params: Partial<TParams>) => {
    const newSearchParams = new URLSearchParams(searchParams.toString())

    for (const [key, value] of Object.entries(params)) {
      if (
        value === null ||
        value === undefined ||
        (deleteFalseValues && !value)
      ) {
        newSearchParams.delete(key)
      } else if (Array.isArray(value)) {
        newSearchParams.delete(key)
        value.forEach(val => newSearchParams.append(key, String(val)))
      } else {
        newSearchParams.set(key, String(value))
      }
    }

    updateUrl(newSearchParams)
    return Object.fromEntries(newSearchParams.entries())
  }

  const append = (params: Partial<TParams>) => {
    const newSearchParams = new URLSearchParams(searchParams.toString())

    for (const [key, value] of Object.entries(params)) {
      if (Array.isArray(value)) {
        value.forEach(val => newSearchParams.append(key, String(val)))
      } else {
        newSearchParams.append(key, String(value))
      }
    }

    updateUrl(newSearchParams)
    return Object.fromEntries(newSearchParams.entries())
  }

  const deleteParam = (keys: keyof TParams | (keyof TParams)[]) => {
    const newSearchParams = new URLSearchParams(searchParams.toString())

    if (Array.isArray(keys)) {
      keys.forEach(key => newSearchParams.delete(key as string))
    } else {
      newSearchParams.delete(keys as string)
    }

    updateUrl(newSearchParams)
    return Object.fromEntries(newSearchParams.entries())
  }

  const clear = () => {
    const newSearchParams = new URLSearchParams()
    updateUrl(newSearchParams)
    return Object.fromEntries(newSearchParams.entries())
  }

  const get = <K extends keyof TParams>(key: K): TParams[K] | null => {
    return searchParams.get(key as string) as TParams[K] | null
  }

  const searchParamsObj = Object.fromEntries(searchParams.entries()) as TParams

  return {
    set,
    append,
    deleteParam,
    clear,
    get,
    searchParams: searchParamsObj
  }
}
