"use client"

import * as React from "react"
import { rankItem } from "@tanstack/match-sorter-utils"
import {
  useReactTable,
  getCoreRowModel,
  getSortedRowModel,
  getFilteredRowModel,
  getPaginationRowModel,
  getGroupedRowModel,
  getExpandedRowModel,
  getFacetedRowModel,
  getFacetedUniqueValues,
  getFacetedMinMaxValues,
  ColumnDef,
  SortingState,
  ColumnFiltersState,
  flexRender,
  PaginationState,
  OnChangeFn,
  ColumnOrderState,
  VisibilityState,
  ColumnPinningState,
  FilterFn,
  Table,
  RowSelectionState,
  Row,
  Column,
  GroupingState,
  ExpandedState,
} from "@tanstack/react-table"
import { useVirtualizer } from "@tanstack/react-virtual"
import {
  DndContext,
  closestCenter,
  KeyboardSensor,
  PointerSensor,
  useSensor,
  useSensors,
  DragEndEvent,
} from "@dnd-kit/core"
import {
  arrayMove,
  SortableContext,
  horizontalListSortingStrategy,
} from "@dnd-kit/sortable"
import { cn } from "@/lib/utils"
import { Loader2, ChevronRight, ChevronDown } from "lucide-react"

import { ClientTableToolbar } from "../client-table/client-table-toolbar"
import { exportToExcel } from "../client-table/export-utils"
import { ClientDataTablePagination } from "@/components/client-data-table/data-table-pagination"
import { ClientTableColumnHeader } from "./client-table-column-header"
import { ClientTableViewOptions } from "../client-table/client-table-view-options"
import { ClientTablePreset } from "./client-table-preset"

// Moved outside for stability (Performance Optimization)
const fuzzyFilter: FilterFn<any> = (row, columnId, value, addMeta) => {
  const itemRank = rankItem(row.getValue(columnId), value)
  addMeta({ itemRank })
  return itemRank.passed
}

export interface ClientVirtualTableProps<TData, TValue> {
  data: TData[]
  columns: ColumnDef<TData, TValue>[]
  height?: string | number
  estimateRowHeight?: number
  className?: string
  actions?: React.ReactNode
  customToolbar?: React.ReactNode
  enableExport?: boolean
  onExport?: (data: TData[]) => void
  isLoading?: boolean

  /**
   * 데이터 페칭 모드
   * - client: 모든 데이터를 한번에 받아 클라이언트에서 처리 (기본값)
   * - server: 서버에서 필터링/정렬/페이지네이션 된 데이터를 받음
   */
  fetchMode?: "client" | "server"

  // --- User Preset Saving ---
  enableUserPreset?: boolean
  tableKey?: string

  // --- State Control (Controlled or Uncontrolled) ---

  // Pagination
  enablePagination?: boolean
  manualPagination?: boolean
  pageCount?: number
  rowCount?: number
  pagination?: PaginationState
  onPaginationChange?: OnChangeFn<PaginationState>

  // Sorting
  sorting?: SortingState
  onSortingChange?: OnChangeFn<SortingState>

  // Filtering
  columnFilters?: ColumnFiltersState
  onColumnFiltersChange?: OnChangeFn<ColumnFiltersState>
  globalFilter?: string
  onGlobalFilterChange?: OnChangeFn<string>

  // Visibility
  columnVisibility?: VisibilityState
  onColumnVisibilityChange?: OnChangeFn<VisibilityState>

  // Pinning
  columnPinning?: ColumnPinningState
  onColumnPinningChange?: OnChangeFn<ColumnPinningState>

  // Order
  columnOrder?: ColumnOrderState
  onColumnOrderChange?: OnChangeFn<ColumnOrderState>

  // Selection
  enableRowSelection?: boolean | ((row: Row<TData>) => boolean)
  enableMultiRowSelection?: boolean | ((row: Row<TData>) => boolean)
  rowSelection?: RowSelectionState
  onRowSelectionChange?: OnChangeFn<RowSelectionState>

  // Grouping
  enableGrouping?: boolean
  grouping?: GroupingState
  onGroupingChange?: OnChangeFn<GroupingState>
  expanded?: ExpandedState
  onExpandedChange?: OnChangeFn<ExpandedState>

  // --- Event Handlers ---
  onRowClick?: (row: Row<TData>, event: React.MouseEvent) => void

  // --- Styling ---
  getRowClassName?: (originalRow: TData, index: number) => string

  // --- Advanced ---
  meta?: Record<string, any>
  getRowId?: (originalRow: TData, index: number, parent?: any) => string

  // Custom Header Visual Feedback
  renderHeaderVisualFeedback?: (props: {
    column: Column<TData, TValue>
    isPinned: boolean | string
    isSorted: boolean | string
    isFiltered: boolean
    isGrouped: boolean
  }) => React.ReactNode
}

function ClientVirtualTableInner<TData, TValue>(
  {
    data,
    columns,
    height = "100%",
    estimateRowHeight = 40,
    className,
    actions,
    customToolbar,
    enableExport = true,
    onExport,
    isLoading = false,
    fetchMode = "client",

    // User Preset Saving
    enableUserPreset = false,
    tableKey,

    // Pagination
    enablePagination = false,
    manualPagination: propManualPagination,
    pageCount,
    rowCount,
    pagination: propPagination,
    onPaginationChange,

    // Sorting
    sorting: propSorting,
    onSortingChange,

    // Filtering
    columnFilters: propColumnFilters,
    onColumnFiltersChange,
    globalFilter: propGlobalFilter,
    onGlobalFilterChange,

    // Visibility
    columnVisibility: propColumnVisibility,
    onColumnVisibilityChange,

    // Pinning
    columnPinning: propColumnPinning,
    onColumnPinningChange,

    // Order
    columnOrder: propColumnOrder,
    onColumnOrderChange,

    // Selection
    enableRowSelection,
    enableMultiRowSelection,
    rowSelection: propRowSelection,
    onRowSelectionChange,

    // Grouping
    enableGrouping = false,
    grouping: propGrouping,
    onGroupingChange,
    expanded: propExpanded,
    onExpandedChange,

    // Style defaults
    getRowClassName,

    // Meta & RowID
    meta,
    getRowId,

    // Event Handlers
    onRowClick,

    // Custom Header Visual Feedback
    renderHeaderVisualFeedback,
  }: ClientVirtualTableProps<TData, TValue>,
  ref: React.Ref<Table<TData>>
) {
  // Internal States (used when props are undefined)
  const [internalSorting, setInternalSorting] = React.useState<SortingState>([])
  const [internalColumnFilters, setInternalColumnFilters] = React.useState<ColumnFiltersState>([])
  const [internalGlobalFilter, setInternalGlobalFilter] = React.useState("")
  const [internalColumnVisibility, setInternalColumnVisibility] = React.useState<VisibilityState>({})
  const [internalColumnPinning, setInternalColumnPinning] = React.useState<ColumnPinningState>({ left: [], right: [] })
  const [internalColumnOrder, setInternalColumnOrder] = React.useState<ColumnOrderState>(
    () => columns.map((c) => c.id || (c as any).accessorKey) as string[]
  )
  const [internalPagination, setInternalPagination] = React.useState<PaginationState>({
    pageIndex: 0,
    pageSize: 10,
  })
  const [internalRowSelection, setInternalRowSelection] = React.useState<RowSelectionState>({})
  const [internalGrouping, setInternalGrouping] = React.useState<GroupingState>([])
  const [internalExpanded, setInternalExpanded] = React.useState<ExpandedState>({})

  // Effective States
  const sorting = propSorting ?? internalSorting
  const setSorting = onSortingChange ?? setInternalSorting

  const columnFilters = propColumnFilters ?? internalColumnFilters
  const setColumnFilters = onColumnFiltersChange ?? setInternalColumnFilters

  const globalFilter = propGlobalFilter ?? internalGlobalFilter
  const setGlobalFilter = onGlobalFilterChange ?? setInternalGlobalFilter

  const columnVisibility = propColumnVisibility ?? internalColumnVisibility
  const setColumnVisibility = onColumnVisibilityChange ?? setInternalColumnVisibility

  const columnPinning = propColumnPinning ?? internalColumnPinning
  const setColumnPinning = onColumnPinningChange ?? setInternalColumnPinning

  const columnOrder = propColumnOrder ?? internalColumnOrder
  const setColumnOrder = onColumnOrderChange ?? setInternalColumnOrder

  const pagination = propPagination ?? internalPagination
  const setPagination = onPaginationChange ?? setInternalPagination

  const rowSelection = propRowSelection ?? internalRowSelection
  const setRowSelection = onRowSelectionChange ?? setInternalRowSelection

  const grouping = propGrouping ?? internalGrouping
  const setGrouping = onGroupingChange ?? setInternalGrouping

  const expanded = propExpanded ?? internalExpanded
  const setExpanded = onExpandedChange ?? setInternalExpanded

  // Server Mode Logic
  const isServer = fetchMode === "server"
  // If server mode is enabled, we default manual flags to true unless explicitly overridden
  const manualPagination = propManualPagination ?? isServer
  const manualSorting = isServer
  const manualFiltering = isServer
  const manualGrouping = isServer

  // Table Instance
  const table = useReactTable({
    data,
    columns,
    state: {
      sorting,
      columnFilters,
      globalFilter,
      pagination,
      columnVisibility,
      columnPinning,
      columnOrder,
      rowSelection,
      grouping,
      expanded,
    },
    manualPagination,
    manualSorting,
    manualFiltering,
    manualGrouping,
    pageCount: manualPagination ? pageCount : undefined,
    rowCount: manualPagination ? rowCount : undefined,
    onSortingChange: setSorting,
    onColumnFiltersChange: setColumnFilters,
    onGlobalFilterChange: setGlobalFilter,
    onPaginationChange: setPagination,
    onColumnVisibilityChange: setColumnVisibility,
    onColumnPinningChange: setColumnPinning,
    onColumnOrderChange: setColumnOrder,
    onRowSelectionChange: setRowSelection,
    onGroupingChange: setGrouping,
    onExpandedChange: setExpanded,
    enableRowSelection,
    enableMultiRowSelection,
    enableGrouping,
    getCoreRowModel: getCoreRowModel(),

    // Systematic Order of Operations:
    // If server-side, we skip client-side processing models to avoid double processing
    // and to ensure the table reflects exactly what the server returned.
    getFilteredRowModel: !isServer ? getFilteredRowModel() : undefined,
    getFacetedRowModel: !isServer ? getFacetedRowModel() : undefined,
    getFacetedUniqueValues: !isServer ? getFacetedUniqueValues() : undefined,
    getFacetedMinMaxValues: !isServer ? getFacetedMinMaxValues() : undefined,

    getSortedRowModel: !isServer ? getSortedRowModel() : undefined,

    getGroupedRowModel: (!isServer && enableGrouping) ? getGroupedRowModel() : undefined,
    getExpandedRowModel: (!isServer && enableGrouping) ? getExpandedRowModel() : undefined,

    getPaginationRowModel: (!isServer && enablePagination) ? getPaginationRowModel() : undefined,
    columnResizeMode: "onChange",
    filterFns: {
      fuzzy: fuzzyFilter,
    },
    globalFilterFn: fuzzyFilter,
    meta,
    getRowId,
  })

  // Expose table instance via ref
  React.useImperativeHandle(ref, () => table, [table])

  // DnD Sensors
  const sensors = useSensors(
    useSensor(PointerSensor, {
      activationConstraint: {
        distance: 8,
      },
    }),
    useSensor(KeyboardSensor)
  )

  // Handle Drag End
  const handleDragEnd = (event: DragEndEvent) => {
    const { active, over } = event
    if (active && over && active.id !== over.id) {
      const activeId = active.id as string
      const overId = over.id as string

      const activeColumn = table.getColumn(activeId)
      const overColumn = table.getColumn(overId)

      if (activeColumn && overColumn) {
        const activePinState = activeColumn.getIsPinned()
        const overPinState = overColumn.getIsPinned()

        // If dragging between different pin states, update the pin state of the active column
        if (activePinState !== overPinState) {
          activeColumn.pin(overPinState)
        }

        // Reorder the columns
        setColumnOrder((items) => {
          const currentItems = Array.isArray(items) ? items : []
          const oldIndex = items.indexOf(activeId)
          const newIndex = items.indexOf(overId)
          return arrayMove(items, oldIndex, newIndex)
        })
      }
    }
  }

  // Virtualization
  const tableContainerRef = React.useRef<HTMLDivElement>(null)
  const { rows } = table.getRowModel()

  const rowVirtualizer = useVirtualizer({
    count: rows.length,
    getScrollElement: () => tableContainerRef.current,
    estimateSize: () => estimateRowHeight,
    overscan: 10,
  })

  const virtualRows = rowVirtualizer.getVirtualItems()
  const totalSize = rowVirtualizer.getTotalSize()

  const paddingTop = virtualRows.length > 0 ? virtualRows?.[0]?.start || 0 : 0
  const paddingBottom =
    virtualRows.length > 0
      ? totalSize - (virtualRows?.[virtualRows.length - 1]?.end || 0)
      : 0

  // Export Handler
  const handleExport = async () => {
    if (onExport) {
      onExport(data)
      return
    }
    const currentData = table.getFilteredRowModel().rows.map((row) => row.original)
    await exportToExcel(currentData, columns, `export-${new Date().toISOString().slice(0, 10)}.xlsx`)
  }

  return (
    <div
      className={`flex flex-col gap-4 ${className || ""}`}
      style={{ height }}
    >
      <ClientTableToolbar
        globalFilter={globalFilter}
        setGlobalFilter={setGlobalFilter}
        totalRows={manualPagination ? (rowCount ?? data.length) : data.length}
        visibleRows={rows.length}
        onExport={enableExport ? handleExport : undefined}
        viewOptions={
          <>
            <ClientTableViewOptions table={table} />
            {enableUserPreset && tableKey && (
              <ClientTablePreset table={table} tableKey={tableKey} />
            )}
          </>
        }
        customToolbar={customToolbar}
        actions={actions}
      />

      <div
        ref={tableContainerRef}
        className="relative border rounded-md overflow-auto bg-background flex-1 min-h-0"
      >
        {isLoading && (
          <div className="absolute inset-0 z-50 flex items-center justify-center bg-background/50 backdrop-blur-sm">
            <Loader2 className="h-8 w-8 animate-spin text-primary" />
          </div>
        )}

        <DndContext
          sensors={sensors}
          collisionDetection={closestCenter}
          onDragEnd={handleDragEnd}
        >
          <table
            className="table-fixed border-collapse w-full min-w-full"
            style={{ width: table.getTotalSize() }}
          >
            <thead className="sticky top-0 z-40 bg-muted">
              {table.getHeaderGroups().map((headerGroup) => (
                <tr key={headerGroup.id}>
                  <SortableContext
                    items={headerGroup.headers.map((h) => h.id)}
                    strategy={horizontalListSortingStrategy}
                  >
                    {headerGroup.headers.map((header) => (
                      <ClientTableColumnHeader
                        key={header.id}
                        header={header}
                        enableReordering={true}
                        renderHeaderVisualFeedback={renderHeaderVisualFeedback}
                      />
                    ))}
                  </SortableContext>
                </tr>
              ))}
            </thead>
            <tbody>
              {paddingTop > 0 && (
                <tr>
                  <td style={{ height: `${paddingTop}px` }} />
                </tr>
              )}
              {virtualRows.length === 0 && !isLoading ? (
                <tr>
                  <td colSpan={columns.length} className="h-24 text-center">
                    No results.
                  </td>
                </tr>
              ) : (
                virtualRows.map((virtualRow) => {
                  const row = rows[virtualRow.index]

                  // --- Group Header Rendering ---
                  if (row.getIsGrouped()) {
                    const groupingColumnId = row.groupingColumnId ?? "";
                    const groupingValue = row.getGroupingValue(groupingColumnId);

                    return (
                      <tr
                        key={row.id}
                        className="hover:bg-muted/50 border-b bg-muted/30"
                        style={{ height: `${virtualRow.size}px` }}
                      >
                        <td
                          colSpan={columns.length}
                          className="px-4 py-2 text-left font-medium cursor-pointer"
                          onClick={row.getToggleExpandedHandler()}
                        >
                          <div className="flex items-center gap-2">
                            {row.getIsExpanded() ? (
                              <ChevronDown className="h-4 w-4" />
                            ) : (
                              <ChevronRight className="h-4 w-4" />
                            )}
                            <span className="flex items-center gap-2">
                              <span className="font-bold capitalize">
                                {groupingColumnId}:
                              </span>
                              <span>
                                {String(groupingValue)}
                              </span>
                              <span className="text-muted-foreground text-sm font-normal">
                                ({row.subRows.length})
                              </span>
                            </span>
                          </div>
                        </td>
                      </tr>
                    )
                  }

                  // --- Normal Row Rendering ---
                  return (
                    <tr
                      key={row.id}
                      className={cn(
                        "hover:bg-muted/50 border-b last:border-0",
                        getRowClassName ? getRowClassName(row.original, row.index) : "",
                        onRowClick ? "cursor-pointer" : ""
                      )}
                      style={{ height: `${virtualRow.size}px` }}
                      onClick={(e) => onRowClick?.(row, e)}
                    >
                      {row.getVisibleCells().map((cell) => {
                        // Handle pinned cells
                        const isPinned = cell.column.getIsPinned()
                        const isGrouped = cell.column.getIsGrouped()

                        const style: React.CSSProperties = {
                          width: cell.column.getSize(),
                        }
                        if (isPinned === "left") {
                          style.position = "sticky"
                          style.left = `${cell.column.getStart("left")}px`
                          style.zIndex = 20
                        } else if (isPinned === "right") {
                          style.position = "sticky"
                          style.right = `${cell.column.getAfter("right")}px`
                          style.zIndex = 20
                        }

                        return (
                          <td
                            key={cell.id}
                            className={cn(
                              "px-2 py-0 text-sm truncate border-b bg-background",
                              isGrouped ? "bg-muted/20" : ""
                            )}
                            style={style}
                          >
                            {cell.getIsGrouped() ? (
                              // If this cell is grouped, usually we don't render it here if we have a group header row,
                              // but if we keep it, it acts as the expander for the next level (if multi-level grouping).
                              // Since we used a full-width row for the group header, this branch might not be hit for the group row itself,
                              // but for nested groups it might?
                              // Wait, row.getIsGrouped() is true for the group row.
                              // The cells inside the group row are not rendered because we return early above.
                              // The cells inside the "leaf" rows (normal rows) are rendered here.
                              // So cell.getIsGrouped() checks if the COLUMN is currently grouped.
                              // If the column is grouped, the cell value is usually redundant or hidden in normal rows.
                              // Standard practice: hide the cell content or dim it.
                              null
                            ) : cell.getIsAggregated() ? (
                              // If this cell is an aggregation of the group
                              flexRender(
                                cell.column.columnDef.aggregatedCell ??
                                cell.column.columnDef.cell,
                                cell.getContext()
                              )
                            ) : (
                              // Normal cell
                              cell.getIsPlaceholder()
                                ? null
                                : flexRender(cell.column.columnDef.cell, cell.getContext())
                            )}
                          </td>
                        )
                      })}
                    </tr>
                  )
                })
              )}
              {paddingBottom > 0 && (
                <tr>
                  <td style={{ height: `${paddingBottom}px` }} />
                </tr>
              )}
            </tbody>
          </table>
        </DndContext>
      </div>

      {enablePagination && (
        <ClientDataTablePagination table={table} />
      )}
    </div>
  )
}

export const ClientVirtualTable = React.memo(
  React.forwardRef(ClientVirtualTableInner)
) as <TData, TValue>(
  props: ClientVirtualTableProps<TData, TValue> & { ref?: React.Ref<Table<TData>> }
) => React.ReactElement
