import {useCallback, useEffect, useMemo, useState} from 'react'

import debounce from 'lodash/debounce'

import {useShopActions} from '../../internal/useShopActions'
import {useShopActionsPaginatedDataFetching} from '../../internal/useShopActionsPaginatedDataFetching'
import {
  PaginatedDataHookOptionsBase,
  PaginatedDataHookReturnsBase,
  Product,
  ProductFilters,
  ProductSearchSortBy,
} from '../../types'

export interface UseProductSearchParams extends PaginatedDataHookOptionsBase {
  /**
   * The search query.
   */
  query: string
  /**
   * The filters to apply to the search.
   */
  filters?: ProductFilters
  /**
   * The sort order of the results.
   */
  sortBy?: ProductSearchSortBy
  /**
   * Whether to include sensitive products.
   */
  includeSensitive?: boolean
}

interface UseProductSearchReturns extends PaginatedDataHookReturnsBase {
  /**
   * The products returned from the query.
   */
  products: Product[] | null
  /**
   * Whether the user is typing.
   */
  isTyping: boolean
}

export const useProductSearch = (
  params: UseProductSearchParams
): UseProductSearchReturns => {
  const {getProductSearch} = useShopActions()
  const {
    query,
    filters,
    sortBy,
    includeSensitive = false,
    skip = false,
    ...restParams
  } = params ?? {}

  const [debouncedQuery, setDebouncedQuery] = useState<string>(query)

  // eslint-disable-next-line react-hooks/exhaustive-deps
  const debouncedSetQuery = useCallback(
    debounce((newQuery: string) => {
      setDebouncedQuery(newQuery)
    }, 300),
    []
  )

  useEffect(() => {
    debouncedSetQuery(query)

    return () => {
      debouncedSetQuery.cancel()
    }
  }, [query, debouncedSetQuery])

  const {data, ...rest} = useShopActionsPaginatedDataFetching(
    getProductSearch,
    {
      query: debouncedQuery,
      filters,
      sortBy,
      includeSensitive,
      ...restParams,
    },
    {
      skip,
      hook: 'useProductSearch',
    }
  )

  const products = useMemo(() => {
    if (debouncedQuery.trim().length === 0) {
      return null
    }

    return data ?? null
  }, [data, debouncedQuery])

  return {
    ...rest,
    products,
    isTyping: debouncedQuery !== query,
  }
}
