"use client"

import { useRef, useEffect, useMemo } from "react"
import { useVirtualizer } from "@tanstack/react-virtual"
import { useTimeline } from "@/lib/queries/use-timeline"
import { useSavedEntries } from "@/lib/queries/use-saved-entries"
import { useCustomFeedTimeline } from "@/lib/queries/use-custom-feed-timeline"
import { useUserInterfaceStore } from "@/lib/stores/user-interface-store"
import { EntryListItem } from "./entry-list-item"
import { usePrefetchEntryDetails } from "@/lib/hooks/use-prefetch-entry-details"

interface EntryListProperties {
  feedFilter: "all" | "saved"
  folderIdentifier?: string | null
  feedIdentifier?: string | null
  customFeedIdentifier?: string | null
}

function useEntryData(
  feedFilter: "all" | "saved",
  folderIdentifier?: string | null,
  feedIdentifier?: string | null,
  customFeedIdentifier?: string | null,
  prioritiseUnread?: boolean
) {
  const timelineQuery = useTimeline(
    feedFilter === "all" && !customFeedIdentifier ? folderIdentifier : undefined,
    feedFilter === "all" && !customFeedIdentifier ? feedIdentifier : undefined,
    false,
    prioritiseUnread
  )
  const savedQuery = useSavedEntries()
  const customFeedQuery = useCustomFeedTimeline(
    feedFilter === "all" ? (customFeedIdentifier ?? null) : null
  )

  if (feedFilter === "saved") {
    return savedQuery
  }

  if (customFeedIdentifier) {
    return customFeedQuery
  }

  return timelineQuery
}

export function EntryList({
  feedFilter,
  folderIdentifier,
  feedIdentifier,
  customFeedIdentifier,
}: EntryListProperties) {
  const prioritiseUnreadEntries = useUserInterfaceStore(
    (state) => state.prioritiseUnreadEntries
  )

  const { data, fetchNextPage, hasNextPage, isFetchingNextPage, isLoading } =
    useEntryData(feedFilter, folderIdentifier, feedIdentifier, customFeedIdentifier, prioritiseUnreadEntries)

  const entryListViewMode = useUserInterfaceStore(
    (state) => state.entryListViewMode
  )
  const selectedEntryIdentifier = useUserInterfaceStore(
    (state) => state.selectedEntryIdentifier
  )
  const setSelectedEntryIdentifier = useUserInterfaceStore(
    (state) => state.setSelectedEntryIdentifier
  )
  const focusedEntryIdentifier = useUserInterfaceStore(
    (state) => state.focusedEntryIdentifier
  )
  const setFocusedEntryIdentifier = useUserInterfaceStore(
    (state) => state.setFocusedEntryIdentifier
  )

  const setNavigableEntryIdentifiers = useUserInterfaceStore(
    (state) => state.setNavigableEntryIdentifiers
  )

  const allEntries = useMemo(() => {
    return data?.pages.flatMap((page) => page) ?? []
  }, [data])
  const scrollContainerReference = useRef<HTMLDivElement>(null)

  const firstEntryIdentifier = allEntries[0]?.entryIdentifier
  const lastEntryIdentifier = allEntries[allEntries.length - 1]?.entryIdentifier

  const prefetchIdentifiers = useMemo(
    () => allEntries.map((entry) => entry.entryIdentifier),
    // eslint-disable-next-line react-hooks/exhaustive-deps
    [firstEntryIdentifier, lastEntryIdentifier, allEntries.length]
  )
  usePrefetchEntryDetails(prefetchIdentifiers)

  useEffect(() => {
    setNavigableEntryIdentifiers(prefetchIdentifiers)
  }, [prefetchIdentifiers, setNavigableEntryIdentifiers])

  function getEstimatedItemSize() {
    switch (entryListViewMode) {
      case "compact":
        return 40
      case "comfortable":
        return 60
      case "expanded":
        return 108
    }
  }

  const virtualizer = useVirtualizer({
    count: hasNextPage ? allEntries.length + 1 : allEntries.length,
    getScrollElement: () => scrollContainerReference.current,
    estimateSize: getEstimatedItemSize,
    overscan: 10,
  })

  const virtualItems = virtualizer.getVirtualItems()

  useEffect(() => {
    const lastItem = virtualItems[virtualItems.length - 1]

    if (!lastItem) return

    if (
      lastItem.index >= allEntries.length - 1 &&
      hasNextPage &&
      !isFetchingNextPage
    ) {
      fetchNextPage()
    }
  }, [
    virtualItems,
    allEntries.length,
    hasNextPage,
    isFetchingNextPage,
    fetchNextPage,
  ])

  const setIsEntryListAtTop = useUserInterfaceStore(
    (state) => state.setIsEntryListAtTop
  )

  useEffect(() => {
    const scrollElement = scrollContainerReference.current
    if (!scrollElement) return

    function handleScroll() {
      if (!scrollElement) return
      useUserInterfaceStore.getState().setIsEntryListAtTop(scrollElement.scrollTop < 50)
    }

    handleScroll()
    scrollElement.addEventListener("scroll", handleScroll, { passive: true })
    return () => scrollElement.removeEventListener("scroll", handleScroll)
  }, [setIsEntryListAtTop])

  const allEntriesReference = useRef(allEntries)
  allEntriesReference.current = allEntries

  useEffect(() => {
    const activeIdentifier = focusedEntryIdentifier ?? selectedEntryIdentifier
    if (!activeIdentifier) return

    const activeIndex = allEntriesReference.current.findIndex(
      (entry) => entry.entryIdentifier === activeIdentifier
    )

    if (activeIndex !== -1) {
      virtualizer.scrollToIndex(activeIndex, { align: "auto" })
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [focusedEntryIdentifier, allEntries])

  if (isLoading) {
    return (
      <div className="space-y-2 p-4">
        {Array.from({ length: 8 }).map((_, skeletonIndex) => (
          <div
            key={skeletonIndex}
            className="h-10 animate-[skeleton-shimmer_1.5s_ease-in-out_infinite] bg-background-tertiary"
          />
        ))}
      </div>
    )
  }

  if (allEntries.length === 0) {
    return (
      <div className="flex h-full items-center justify-center">
        <p className="text-text-tertiary">
          {feedFilter === "saved"
            ? "no saved entries yet"
            : "no entries yet \u2014 add a feed to get started"}
        </p>
      </div>
    )
  }

  return (
    <div ref={scrollContainerReference} data-entry-list-scroll className="h-full overflow-auto">
      <div
        style={{
          height: `${virtualizer.getTotalSize()}px`,
          width: "100%",
          position: "relative",
        }}
      >
        {virtualItems.map((virtualItem) => {
          const entry = allEntries[virtualItem.index]

          if (!entry) {
            return (
              <div
                key="loader"
                data-index={virtualItem.index}
                ref={virtualizer.measureElement}
                className="absolute left-0 top-0 w-full"
                style={{
                  transform: `translateY(${virtualItem.start}px)`,
                }}
              >
                <p className="p-4 text-center text-text-dim">loading ...</p>
              </div>
            )
          }

          return (
            <EntryListItem
              key={entry.entryIdentifier}
              entry={entry}
              isSelected={
                entry.entryIdentifier === selectedEntryIdentifier
              }
              isFocused={
                entry.entryIdentifier === focusedEntryIdentifier
              }
              viewMode={entryListViewMode}
              onSelect={() => {
                setFocusedEntryIdentifier(entry.entryIdentifier)
                setSelectedEntryIdentifier(entry.entryIdentifier)
              }}
              measureReference={virtualizer.measureElement}
              virtualItem={virtualItem}
            />
          )
        })}
      </div>
    </div>
  )
}
