"use client"
import { Virtualizer } from "@tanstack/react-virtual"
import React, { memo, useCallback, useState } from "react"
import {
  useCallbackRef,
  useIntersected,
  useSynchronizedScroll,
  useVirtualPagination,
} from "../../hooks"
import { classNames } from "../../utils/classNames"
import { CenterAligned } from "../CenterAligned"
import { FlexColumn } from "../FlexColumn"
import { RetryButton } from "../RetryButton"
import { TABLE_SIZES, TableRowRenderProps, useTable } from "../Table"
import { TableHeaderWrapper } from "../Table/TableHeader"
import { DEFAULT_PAGINATION_THRESHOLD } from "./constants"
import { VirtualizedTableProps } from "./VirtualizedTable"

type Props<T, C> = {
  rowVirtualizer:
    | Virtualizer<Window, Element>
    | Virtualizer<HTMLDivElement, Element>
  setTableRef: (ref: HTMLDivElement) => void
  tableRef: React.RefObject<HTMLDivElement | null>
  id?: string
  gap: number
} & VirtualizedTableProps<T, C>

type VirtualizedTableRowProps<T, C> = {
  index: number
  Row: Props<T, C>["renderRow"]
  context: Props<T, C>["context"]
  translateY: number
  item: Props<T, C>["items"][number]
}

const VirtualizedTableRow = memo(function VirtualizedTableRow<T, C>({
  index,
  item,
  Row,
  context,
  translateY,
}: VirtualizedTableRowProps<T, C>) {
  return (
    <div
      className="absolute left-0 top-0 grid w-full"
      data-index={index}
      style={{ transform: `translateY(${translateY}px)` }}
    >
      <Row context={context} index={index} item={item} />
    </div>
  )
})

export const VirtualizedTableView = <T, C>({
  className,
  contentClassName,
  items,
  hasNext,
  loadNext,
  paginationThreshold = DEFAULT_PAGINATION_THRESHOLD,
  header,
  renderRow: Row,
  rowVirtualizer,
  setTableRef,
  tableRef,
  id,
  context,
  scrollMode,
  headerBorderOnScroll: headerBorderOnScrollProp = true,
  style,
  contentStyle,
  paginationError,
  gap,
  itemKey,
}: Props<T, C>) => {
  const [headerRef, setHeaderRef] = useCallbackRef<HTMLDivElement>()
  const { size, dividers } = useTable()

  const headerRefChild = headerRef.current
    ?.firstElementChild as HTMLDivElement | null

  useSynchronizedScroll(headerRefChild, tableRef.current)

  const virtualItems = rowVirtualizer.getVirtualItems()
  const [hasLocalError, setHasLocalError] = useState(false)
  const hasError = paginationError || hasLocalError

  const onError = useCallback(() => {
    setHasLocalError(true)
  }, [])

  useVirtualPagination({
    items: virtualItems,
    count: items.length,
    loadNext,
    hasNext: hasNext && !hasError,
    paginationThreshold,
    onError,
  })

  const isScrolledDown = useIntersected(
    tableRef,
    { current: headerRefChild },
    (scrollMode === "table" && tableRef) || undefined,
  )

  const showBorder = (isScrolledDown && headerBorderOnScrollProp) || dividers

  return (
    <FlexColumn
      aria-rowcount={items.length}
      className={classNames("w-full min-w-0", className)}
      id={id}
      role="table"
      style={style}
    >
      {header && (
        // Display contents to allow header to become sticky with parent.
        <TableHeaderWrapper ref={setHeaderRef} showBorder={showBorder}>
          {header}
        </TableHeaderWrapper>
      )}
      <div
        className={classNames(
          "w-full flex-1 overflow-x-auto",
          contentClassName,
        )}
        ref={setTableRef}
        role="rowgroup"
        style={contentStyle}
      >
        <div
          className="relative"
          style={{
            height: rowVirtualizer.getTotalSize(),

            // Establish a min height of at least the number of items taking into account gap
            // rowVirtualizer.getTotalSize() doesn't seem to account for this correctly.
            minHeight: items.length * (TABLE_SIZES[size].rowHeight + gap),
          }}
        >
          {virtualItems.map(({ index, start }) => {
            return (
              <VirtualizedTableRow
                // memo breaking generics
                Row={
                  Row as React.ComponentType<
                    TableRowRenderProps<unknown, unknown>
                  >
                }
                context={context}
                index={index}
                item={items[index]}
                // virtual items default key is bugged
                key={itemKey?.(items[index], index) ?? index}
                translateY={start - (rowVirtualizer.options.scrollMargin ?? 0)}
              />
            )
          })}
        </div>

        {(paginationError || hasError) && (
          <CenterAligned className="my-4">
            <RetryButton
              onClick={async () => {
                await loadNext?.()
                setHasLocalError(false)
              }}
            />
          </CenterAligned>
        )}
      </div>
    </FlexColumn>
  )
}
