import { DEFAULT_PAGE_SIZE, OffsetPagination, PAGE_SIZE_OPTIONS } from "@ivtui/base"
import {
  type ColumnFiltersState,
  flexRender,
  getCoreRowModel,
  getFilteredRowModel,
  getPaginationRowModel,
  getSortedRowModel,
  type SortingState,
  useReactTable,
  type VisibilityState,
} from "@tanstack/react-table"
import clsx from "clsx"
import type { JSX } from "react"
import { useCallback, useMemo, useState } from "react"
import {
  LoadingOverlay,
  TableCellComponent,
  TableHeaderComponent,
  TableRowComponent,
} from "./components"
import { DEFAULT_EMPTY_MESSAGE, DEFAULT_SEARCH_PLACEHOLDER } from "./DataTable.const"
import type { DataTableProps, DataTableRowAction } from "./DataTable.types"
import { Table, TableBody, TableFooter, TableHeader, TableRow } from "./Table"
import { TableRowSelection } from "./TableRowSelection"
import { TableToolbar } from "./TableToolbar"
import { createAllColumns, getRowId, getWidthStyle } from "./utils/tableUtils"

export function DataTable<TData extends Record<string, any>, TValue>({
  columns,
  data,
  searchValue,
  onSearchChange,
  searchPlaceholder = DEFAULT_SEARCH_PLACEHOLDER,
  filters = [],
  selectedRowActions = [],
  rowActions = [],
  pageSize: initialPageSize = DEFAULT_PAGE_SIZE,
  pageSizeOptions = PAGE_SIZE_OPTIONS,
  showPageSizeSelector = true,
  className,
  emptyMessage = DEFAULT_EMPTY_MESSAGE,
  tableClassName,
  headerClassName,
  rowClassName,
  cellClassName,
  footerClassName,
  footer,
  // Toolbar visibility control
  showToolbar,
  showSearchInput,
  showFilters,
  showColumnVisibility,
  showResetButton,
  // External reset control
  onReset,
  // Row selection control
  rowSelection: rowSelectionProp,
  onRowSelectionChange,
  showBuiltInSelection,
  // Controlled/server-side props
  sorting: sortingProp,
  onSortingChange,
  columnFilters: columnFiltersProp,
  onColumnFiltersChange,
  columnVisibility: columnVisibilityProp,
  onColumnVisibilityChange,
  globalFilter: globalFilterProp,
  onGlobalFilterChange,
  pageIndex: pageIndexProp,
  onPageIndexChange,
  pageSizeControlled,
  onPageSizeChange,
  totalItems,
  isLoading,
}: Readonly<DataTableProps<TData, TValue>>): JSX.Element {
  // State management
  const [sortingState, setSortingState] = useState<SortingState>([])
  const [columnFiltersState, setColumnFiltersState] = useState<ColumnFiltersState>([])
  const [globalFilterState, setGlobalFilterState] = useState("")
  const [pageIndexState, setPageIndexState] = useState(0)
  const [pageSizeState, setPageSizeState] = useState(initialPageSize)
  const [columnVisibilityState, setColumnVisibilityState] = useState<VisibilityState>({})
  const [rowSelectionState, setRowSelectionState] = useState<Record<string, boolean>>({})

  // Controlled vs uncontrolled state
  const sorting = sortingProp ?? sortingState
  const columnFilters = columnFiltersProp ?? columnFiltersState
  const columnVisibility = columnVisibilityProp ?? columnVisibilityState
  const globalFilter = globalFilterProp ?? globalFilterState
  const pageIndex = pageIndexProp ?? pageIndexState
  const pageSize = pageSizeControlled ?? pageSizeState
  const rowSelection = rowSelectionProp ?? rowSelectionState

  const setSorting = onSortingChange ?? setSortingState
  const setColumnFilters = onColumnFiltersChange ?? setColumnFiltersState
  const setGlobalFilter = onGlobalFilterChange ?? setGlobalFilterState
  const setPageIndex = onPageIndexChange ?? setPageIndexState
  const setPageSize = onPageSizeChange ?? setPageSizeState

  // Memoized values
  const allColumns = useMemo(
    () => createAllColumns(columns, selectedRowActions, rowActions),
    [columns, selectedRowActions, rowActions],
  )

  // Search behavior logic
  const hasSearch = onSearchChange !== undefined

  // Table instance
  const table = useReactTable({
    data,
    columns: allColumns,
    onSortingChange: (updater: any) => {
      const value = typeof updater === "function" ? updater(sorting) : updater
      setSorting(value)
    },
    onColumnFiltersChange: (updater: any) => {
      const value = typeof updater === "function" ? updater(columnFilters) : updater
      setColumnFilters(value)
    },
    onGlobalFilterChange: setGlobalFilter,
    state: {
      sorting,
      columnFilters,
      globalFilter,
      columnVisibility,
      rowSelection,
      pagination: { pageIndex, pageSize },
    },
    getCoreRowModel: getCoreRowModel(),
    getSortedRowModel: getSortedRowModel(),
    getFilteredRowModel: getFilteredRowModel(),
    getPaginationRowModel: getPaginationRowModel(),
    onColumnVisibilityChange: (updater: any) => {
      const value = typeof updater === "function" ? updater(columnVisibility) : updater
      if (onColumnVisibilityChange) {
        onColumnVisibilityChange(value)
      } else {
        setColumnVisibilityState(value)
      }
    },
    onRowSelectionChange: (updater: any) => {
      const value = typeof updater === "function" ? updater(rowSelection) : updater

      // Update internal state if not controlled
      if (!rowSelectionProp) {
        setRowSelectionState(value)
      }

      // Call external callback immediately with correct data
      if (onRowSelectionChange) {
        // Use the value directly to calculate selected rows
        const selectedRowIds = Object.keys(value).filter(id => value[id])
        const selectedRows = data.filter((row, index) => {
          // Get row ID using the same logic as the table
          let rowId: string
          if (row.id !== undefined) rowId = String(row.id)
          else if (row.ID !== undefined) rowId = String(row.ID)
          else if (row._id !== undefined) rowId = String(row._id)
          else if (row.key !== undefined) rowId = String(row.key)
          else rowId = String(index)

          return selectedRowIds.includes(rowId)
        })
        onRowSelectionChange(value, selectedRows)
      }
    },
    globalFilterFn: "includesString",
    getRowId,
    initialState: {
      pagination: {
        pageSize: initialPageSize,
      },
    },
    manualPagination: totalItems !== undefined,
    pageCount: totalItems !== undefined ? Math.ceil(totalItems / pageSize) : undefined,
    manualSorting: !!sortingProp,
    manualFiltering: !!columnFiltersProp || !!globalFilterProp,
  })

  const toolbarColumns = table
    .getAllLeafColumns()
    .filter(col => col.getCanHide())
    .map(col => ({
      id: col.id,
      label: (col.columnDef.header as string) ?? col.id,
      visible: col.getIsVisible(),
      toggle: () => col.toggleVisibility(),
      canHide: col.getCanHide(),
    }))

  // Callbacks
  const handleSearchChange = useCallback(
    (value: string) => {
      if (onSearchChange) {
        onSearchChange(value)
      }
    },
    [onSearchChange],
  )

  const getSelectedRowsData = useCallback((): TData[] => {
    const selectedRows = table.getFilteredSelectedRowModel().rows
    return selectedRows.map(row => row.original)
  }, [table])

  const handleRowActionClick = useCallback(
    (action: DataTableRowAction<TData>) => {
      return () => action.onClick(getSelectedRowsData())
    },
    [getSelectedRowsData],
  )

  const handleSelectAll = useCallback(() => {
    table.toggleAllPageRowsSelected(true)
  }, [table])

  const handleClearSelection = useCallback(() => {
    table.toggleAllPageRowsSelected(false)
  }, [table])

  const handleReset = useCallback(() => {
    if (onReset) {
      // Use external reset handler if provided
      onReset()
    } else {
      // Fallback to internal reset
      if (onSearchChange) {
        onSearchChange("")
      }

      // Reset filters
      for (const filter of filters) {
        if (filter.onValueChange) {
          filter.onValueChange([])
        }
      }
    }
  }, [onReset, onSearchChange, filters])

  const renderHeaderContent = useCallback((header: any) => {
    if (header.isPlaceholder) return null
    try {
      return flexRender(header.column.columnDef.header, header.getContext()) as React.ReactNode
    } catch (error) {
      console.warn("Error rendering header content:", error)
      return header.column.id
    }
  }, [])

  const renderDataCell = useCallback(
    (cell: any) => (
      <TableCellComponent cell={cell} cellClassName={cellClassName} getWidthStyle={getWidthStyle} />
    ),
    [cellClassName],
  )

  const renderDataRow = useCallback(
    (row: any) => (
      <TableRowComponent row={row} rowClassName={rowClassName} renderDataCell={renderDataCell} />
    ),
    [rowClassName, renderDataCell],
  )

  const renderHeaderRow = useCallback(
    (headerGroup: any) => (
      <TableRow key={headerGroup.id} className="hover:bg-transparent">
        {headerGroup.headers.map((header: any) => (
          <TableHeaderComponent
            key={header.id}
            header={header}
            getWidthStyle={getWidthStyle}
            renderHeaderContent={renderHeaderContent}
          />
        ))}
      </TableRow>
    ),
    [renderHeaderContent],
  )

  // Computed values
  const selectedRowsCount = table.getFilteredSelectedRowModel().rows.length
  const totalCount = totalItems ?? table.getFilteredRowModel().rows.length

  // Built-in selection visibility logic
  const shouldShowBuiltInSelection =
    showBuiltInSelection !== false && // Not explicitly disabled
    selectedRowsCount > 0 && // Has selected rows
    (showBuiltInSelection === true || !onRowSelectionChange) // Explicitly enabled OR no external selection

  // Toolbar visibility logic
  const shouldShowToolbar =
    showToolbar !== false &&
    (showToolbar === true ||
      (hasSearch && showSearchInput !== false) ||
      (filters.length > 0 && showFilters !== false) ||
      (toolbarColumns.length > 0 && showColumnVisibility !== false) ||
      (showResetButton !== false &&
        (searchValue || filters.some(f => f.value && f.value.length > 0))))

  const shouldShowSearch = showSearchInput !== false && hasSearch
  const shouldShowFilters = showFilters !== false && filters.length > 0
  const shouldShowColumnVisibility = showColumnVisibility !== false && toolbarColumns.length > 0
  const shouldShowResetButton =
    showResetButton !== false && (searchValue || filters.some(f => f.value && f.value.length > 0))

  return (
    <div className={clsx("w-full", className)}>
      {shouldShowToolbar && (
        <div className="mb-4">
          <TableToolbar
            searchValue={searchValue ?? ""}
            onSearchChange={shouldShowSearch ? handleSearchChange : undefined}
            searchPlaceholder={searchPlaceholder}
            filters={shouldShowFilters ? filters : []}
            columns={shouldShowColumnVisibility ? toolbarColumns : []}
            showResetButton={!!shouldShowResetButton}
            onReset={handleReset}
          />
        </div>
      )}

      {shouldShowBuiltInSelection && (
        <div className="mb-4">
          <TableRowSelection
            selectedCount={selectedRowsCount}
            totalCount={totalCount}
            onSelectAll={handleSelectAll}
            onClearSelection={handleClearSelection}
            actions={selectedRowActions.map(action => ({
              ...action,
              onClick: handleRowActionClick(action),
            }))}
          />
        </div>
      )}

      <div className="rounded-md border overflow-auto relative">
        {isLoading ? (
          <div className="min-h-[200px] flex items-center justify-center">
            <LoadingOverlay />
          </div>
        ) : table.getRowModel().rows.length === 0 ? (
          <div className="min-h-[200px] flex items-center justify-center">
            <div className="text-gray-500">{emptyMessage}</div>
          </div>
        ) : (
          <Table className={clsx("table-fixed w-full", tableClassName)}>
            <TableHeader className={clsx(headerClassName, "sticky top-0 z-10 bg-inherit")}>
              {table.getHeaderGroups().map(renderHeaderRow)}
            </TableHeader>
            <TableBody>{table.getRowModel().rows.map(renderDataRow)}</TableBody>
            {footer && <TableFooter className={footerClassName}>{footer}</TableFooter>}
          </Table>
        )}
      </div>

      {table.getRowModel().rows.length > 0 && (
        <div className="py-4">
          <OffsetPagination
            currentPage={pageIndex + 1}
            totalPages={table.getPageCount()}
            totalItems={totalCount}
            pageSize={pageSize}
            onPageChange={(page: number) => setPageIndex(page - 1)}
            onPageSizeChange={
              showPageSizeSelector ? (pageSize: number) => setPageSize(pageSize) : undefined
            }
            pageSizeOptions={pageSizeOptions}
          />
        </div>
      )}
    </div>
  )
}
