import { PlusIcon } from '@radix-ui/react-icons'
import { flexRender } from '@tanstack/react-table'

import * as React from 'react'

import { DataGridColumnHeader } from '@components/ui/data-grid/data-grid-column-header'
import { DataGridContextMenu } from '@components/ui/data-grid/data-grid-context-menu'
import { DataGridRow } from '@components/ui/data-grid/data-grid-row'
import { DataGridSearch } from '@components/ui/data-grid/data-grid-search'
import { getCommonPinningStyles } from '@components/ui/data-grid/data-grid-utils'
import type { useDataGrid } from '@components/ui/data-grid/hooks/use-data-grid'
import { cn } from '@lib/utils'

type DataGridProps<TData> = {
  height?: number
} & ReturnType<typeof useDataGrid<TData>> &
  React.ComponentProps<'div'>

export function DataGrid<TData>({
  dataGridRef,
  headerRef,
  rowMapRef,
  footerRef,
  table,
  rowVirtualizer,
  height,
  searchState,
  columnSizeVars,
  onRowAdd,
  className,
  ...props
}: DataGridProps<TData>) {
  const rows = table.getRowModel().rows
  const columns = table.getAllColumns()

  const meta = table.options.meta
  const rowHeight = meta?.rowHeight ?? 'short'
  const focusedCell = meta?.focusedCell ?? null

  const onGridContextMenu = React.useCallback((event: React.MouseEvent<HTMLDivElement>) => {
    event.preventDefault()
  }, [])

  const onAddRowKeyDown = React.useCallback(
    async (event: React.KeyboardEvent<HTMLDivElement>) => {
      if (!onRowAdd) {
        return
      }

      if (event.key === 'Enter' || event.key === '') {
        event.preventDefault()
        await onRowAdd()
      }
    },
    [onRowAdd],
  )

  // calculate header/footer heights using simple measurements (no observers)
  // previous fallbacks were header ~58 and footer ~13
  const headerHeight = headerRef?.current?.clientHeight ?? 58
  const footerHeight = onRowAdd ? (footerRef?.current?.clientHeight ?? 12) + 1 : 0

  // if the grid has a horizontal scrollbar, measure its height so it doesn't overlap content
  let scrollbarHeight = 0
  if (dataGridRef?.current) {
    const el = dataGridRef.current
    if (el.scrollWidth > el.clientWidth) {
      // offsetHeight - clientHeight approximates the scrollbar thickness (includes borders)
      scrollbarHeight = Math.max(0, el.offsetHeight - el.clientHeight)
    }
  }

  return (
    <div data-slot="grid-wrapper" className={cn('relative flex w-full flex-col', className)} {...props}>
      {searchState && <DataGridSearch {...searchState} />}
      <DataGridContextMenu table={table} />
      <div
        role="grid"
        aria-label="Data grid"
        aria-rowcount={rows.length + (onRowAdd ? 1 : 0)}
        aria-colcount={columns.length}
        data-slot="grid"
        tabIndex={0}
        ref={dataGridRef}
        className="relative grid select-none overflow-auto rounded-md border border-neutral-200 focus:outline-none dark:border-neutral-800"
        style={{
          ...columnSizeVars,
          // dynamically include measured header/footer heights (falling back to previous magic numbers)
          // include horizontal scrollbar height when present, and add a small buffer of 3px
          height: `${Math.min(
            rowVirtualizer.getTotalSize() + headerHeight + footerHeight + scrollbarHeight + 3,
            height ?? Infinity,
          )}px`,
        }}
        onContextMenu={onGridContextMenu}
      >
        <div
          role="rowgroup"
          data-slot="grid-header"
          ref={headerRef}
          className="sticky top-0 z-10 grid border-b bg-white dark:bg-neutral-950"
        >
          {table.getHeaderGroups().map((headerGroup, rowIndex) => (
            <div
              key={headerGroup.id}
              role="row"
              aria-rowindex={rowIndex + 1}
              data-slot="grid-header-row"
              tabIndex={-1}
              className="flex w-full"
            >
              {headerGroup.headers.map((header, colIndex) => {
                const sorting = table.getState().sorting
                const currentSort = sorting.find((sort) => sort.id === header.column.id)
                const isSortable = header.column.getCanSort()

                return (
                  <div
                    key={header.id}
                    role="columnheader"
                    aria-colindex={colIndex + 1}
                    aria-sort={
                      currentSort?.desc === false
                        ? 'ascending'
                        : currentSort?.desc === true
                          ? 'descending'
                          : isSortable
                            ? 'none'
                            : undefined
                    }
                    data-slot="grid-header-cell"
                    tabIndex={-1}
                    className={cn('relative', {
                      'border-r': header.column.id !== 'select',
                    })}
                    style={{
                      ...getCommonPinningStyles({ column: header.column }),
                      width: `calc(var(--header-${header.id}-size) * 1px)`,
                    }}
                  >
                    {header.isPlaceholder ? null : typeof header.column.columnDef.header === 'function' ? (
                      <div className="size-full px-3 py-1.5 bg-sidebar-accent/80 hover:bg-neutral-100/40 dark:hover:bg-neutral-800/40">
                        {flexRender(header.column.columnDef.header, header.getContext())}
                      </div>
                    ) : (
                      <DataGridColumnHeader header={header} table={table} />
                    )}
                  </div>
                )
              })}
            </div>
          ))}
        </div>
        <div
          role="rowgroup"
          data-slot="grid-body"
          className="relative grid"
          style={{
            height: `${rowVirtualizer.getTotalSize()}px`,
          }}
        >
          {rowVirtualizer.getVirtualIndexes().map((virtualRowIndex) => {
            const row = rows[virtualRowIndex]
            if (!row) {
              return null
            }

            return (
              <DataGridRow
                key={row.id}
                row={row}
                rowMapRef={rowMapRef}
                virtualRowIndex={virtualRowIndex}
                rowVirtualizer={rowVirtualizer}
                rowHeight={rowHeight}
                focusedCell={focusedCell}
              />
            )
          })}
        </div>
        {onRowAdd && (
          <div
            role="rowgroup"
            data-slot="grid-footer"
            ref={footerRef}
            className="sticky bottom-0 z-10 grid border-t bg-white dark:bg-neutral-950"
          >
            <div
              role="row"
              aria-rowindex={rows.length + 2}
              data-slot="grid-add-row"
              tabIndex={-1}
              className="flex w-full"
            >
              <div
                role="gridcell"
                tabIndex={0}
                className="relative flex h-9 grow items-center bg-neutral-100/30 transition-colors hover:bg-neutral-100/50 focus:bg-neutral-100/50 focus:outline-none dark:bg-neutral-800/30 dark:hover:bg-neutral-800/50 dark:focus:bg-neutral-800/50"
                style={{
                  width: table.getTotalSize(),
                  minWidth: table.getTotalSize(),
                }}
                onClick={onRowAdd}
                onKeyDown={onAddRowKeyDown}
              >
                <div className="sticky left-0 flex items-center gap-2 px-3 text-neutral-500 dark:text-neutral-400">
                  <PlusIcon className="size-3.5" />
                  <span className="text-sm">Add row</span>
                </div>
              </div>
            </div>
          </div>
        )}
      </div>
    </div>
  )
}
