import { useState, useCallback } from 'react'
import axios from 'axios'

export const useTableData = ({
  url,
  pageSize,
  initialPage = 1,
  onDataChange,
  filters = {},
  sorter = {}
}) => {
  const [pages, setPages] = useState([])
  const [hasMore, setHasMore] = useState(true)
  const [hasPrevious, setHasPrevious] = useState(initialPage > 1)
  const [loading, setLoading] = useState(true)
  const [lazyLoading, setLazyLoading] = useState(false)
  const [totalPages, setTotalPages] = useState(0)
  const [loadedPages, setLoadedPages] = useState([])
  const [loadingPages, setLoadingPages] = useState(new Set())
  const [currentLoadedPageNumber, setCurrentLoadedPageNumber] =
    useState(initialPage)

  const createSkeletonData = useCallback(() => {
    return Array(pageSize)
      .fill(null)
      .map(() => ({
        _id: `skeleton-${Math.random().toString(36).substring(2, 15)}`,
        isSkeleton: true
      }))
  }, [pageSize])

  const fetchData = useCallback(
    async (pageNum = 1, append = false, prepend = false) => {
      if (loadingPages.has(pageNum)) {
        return
      }

      try {
        setLoadingPages((prev) => new Set([...prev, pageNum]))

        const response = await axios.get(url, {
          params: {
            page: pageNum,
            limit: pageSize,
            ...filters,
            sort: sorter.field,
            order: sorter.order
          },
          headers: {
            Accept: 'application/json'
          },
          withCredentials: true
        })

        const newData = response.data
        const totalCount = parseInt(
          response.headers['x-total-count'] || '0',
          10
        )
        setTotalPages(Math.ceil(totalCount / pageSize))

        setHasMore(newData.length >= pageSize)
        setHasPrevious(pageNum > 1)

        if (append) {
          setPages((prev) => {
            const filteredPages = prev.map((page) => ({
              ...page,
              items: page.items.filter((item) => !item.isSkeleton)
            }))
            const relevantPages = filteredPages.slice(-2)
            return [...relevantPages, { pageNum, items: newData }]
          })
          setLoadedPages((prev) => {
            const relevantPages = prev.slice(-2)
            return [...relevantPages, pageNum]
          })
        } else if (prepend) {
          setPages((prev) => {
            const filteredPages = prev.map((page) => ({
              ...page,
              items: page.items.filter((item) => !item.isSkeleton)
            }))
            const relevantPages = filteredPages.slice(0, 2)
            return [{ pageNum, items: newData }, ...relevantPages]
          })
          setLoadedPages((prev) => {
            const relevantPages = prev.slice(0, 2)
            return [pageNum, ...relevantPages]
          })
        } else {
          setPages([{ pageNum, items: newData }])
          setLoadedPages([pageNum])
        }

        if (onDataChange) {
          onDataChange(newData)
        }

        setLoading(false)
        setLazyLoading(false)
      } catch (error) {
        setPages((prev) =>
          prev.map((page) => ({
            ...page,
            items: page.items.filter((item) => !item.isSkeleton)
          }))
        )
        setLoading(false)
        setLazyLoading(false)
        throw error
      } finally {
        setLoadingPages((prev) => {
          const newSet = new Set(prev)
          newSet.delete(pageNum)
          return newSet
        })
      }
    },
    [url, pageSize, filters, sorter, onDataChange, loadingPages]
  )

  const reload = useCallback(() => {
    setCurrentLoadedPageNumber(1)
    setLoadedPages([1])
    return fetchData(1)
  }, [fetchData])

  const updateData = useCallback((_id, updatedData) => {
    setPages((prevPages) =>
      prevPages.map((page) => ({
        ...page,
        items: page.items.map((item) =>
          item._id === _id ? { ...item, ...updatedData } : item
        )
      }))
    )
  }, [])

  const goToPage = useCallback(
    (pageNum) => {
      if (pageNum > 0 && pageNum <= totalPages) {
        setCurrentLoadedPageNumber(pageNum)
        const pagesToLoad = [pageNum - 1, pageNum, pageNum + 1].filter(
          (p) => p > 0 && p <= totalPages
        )
        setLoadedPages(pagesToLoad)
        return Promise.all(
          pagesToLoad.map((p) => fetchData(p, p > pageNum, p < pageNum))
        )
      }
    },
    [fetchData, totalPages]
  )

  return {
    pages,
    hasMore,
    hasPrevious,
    loading,
    lazyLoading,
    totalPages,
    loadedPages,
    loadingPages,
    currentLoadedPageNumber,
    createSkeletonData,
    fetchData,
    reload,
    updateData,
    goToPage,
    setCurrentLoadedPageNumber,
    setLazyLoading,
    setPages,
    setLoadedPages
  }
}
