'use client'

import * as React from 'react'
import { ChevronDownIcon } from '@heroicons/react/20/solid'

type Align = 'left' | 'center' | 'right'

export type SortDirection = 'asc' | 'desc'
export type SortState = { key: string; direction: SortDirection } | null

export type Column<T> = {
  key: string
  header: React.ReactNode
  widthClassName?: string
  align?: Align
  className?: string
  headerClassName?: string

  /** Wenn gesetzt: so wird die Zelle gerendert */
  cell?: (row: T, rowIndex: number) => React.ReactNode
  /** Standard: row[col.key] */
  accessor?: (row: T) => React.ReactNode
  /** Optional: sr-only Header (z.B. für Action-Spalte) */
  srOnlyHeader?: boolean

  /** Sortieren aktivieren (Header wird klickbar) */
  sortable?: boolean
  /**
   * Wert, nach dem sortiert werden soll (empfohlen!),
   * z.B. number/string/Date/boolean.
   * Fallback ist row[col.key].
   */
  sortValue?: (row: T) => string | number | boolean | Date | null | undefined
  /** Optional: komplett eigene Vergleichsfunktion */
  sortFn?: (a: T, b: T) => number
}

export type TableProps<T> = {
  columns: Array<Column<T>>
  rows: T[]
  getRowKey?: (row: T, index: number) => string
  /** Optionaler Titelbereich über der Tabelle */
  title?: React.ReactNode
  description?: React.ReactNode
  actions?: React.ReactNode

  /** Styling / Layout */
  fullWidth?: boolean
  card?: boolean
  striped?: boolean
  stickyHeader?: boolean
  compact?: boolean

  /** States */
  isLoading?: boolean
  emptyLabel?: React.ReactNode

  className?: string

  rowClassName?: (row: T, rowIndex: number) => string | undefined

  onRowClick?: (row: T) => void
  onRowContextMenu?: (row: T, e: React.MouseEvent<HTMLTableRowElement>) => void

  /**
   * Controlled Sorting:
   * - sort: aktueller Sort-Status
   * - onSortChange: wird bei Klick auf Header aufgerufen
   */
  sort?: SortState
  onSortChange?: (next: SortState) => void

  /**
   * Uncontrolled Sorting:
   * Initiale Sortierung (wenn sort nicht gesetzt ist)
   */
  defaultSort?: SortState
}

function cn(...parts: Array<string | false | null | undefined>) {
  return parts.filter(Boolean).join(' ')
}

function alignTd(a?: Align) {
  if (a === 'center') return 'text-center'
  if (a === 'right') return 'text-right'
  return 'text-left'
}

function justifyForAlign(a?: Align) {
  if (a === 'center') return 'justify-center'
  if (a === 'right') return 'justify-end'
  return 'justify-start'
}

function xPadForColumn(colIndex: number, totalCols: number) {
  const isFirst = colIndex === 0
  const isLast = colIndex === totalCols - 1
  // außen weniger Padding, innen normal
  if (isFirst) return 'pl-2 pr-2'
  if (isLast) return 'pl-2 pr-2'
  return 'px-2'
}

function normalizeSortValue(v: any): { isNull: boolean; kind: 'number' | 'string'; value: number | string } {
  if (v === null || v === undefined) return { isNull: true, kind: 'string', value: '' }
  if (v instanceof Date) return { isNull: false, kind: 'number', value: v.getTime() }
  if (typeof v === 'number') return { isNull: false, kind: 'number', value: v }
  if (typeof v === 'boolean') return { isNull: false, kind: 'number', value: v ? 1 : 0 }
  if (typeof v === 'bigint') return { isNull: false, kind: 'number', value: Number(v) }
  // strings & sonstiges
  return { isNull: false, kind: 'string', value: String(v).toLocaleLowerCase() }
}

export default function Table<T>({
  columns,
  rows,
  getRowKey,
  title,
  description,
  actions,
  fullWidth = false,
  card = true,
  striped = false,
  stickyHeader = false,
  compact = false,
  isLoading = false,
  emptyLabel = 'Keine Daten vorhanden.',
  className,
  rowClassName,
  onRowClick,
  onRowContextMenu,
  sort,
  onSortChange,
  defaultSort = null,
}: TableProps<T>) {
  const cellY = compact ? 'py-2' : 'py-4'
  const headY = compact ? 'py-3' : 'py-3.5'

  const isControlled = sort !== undefined
  const [internalSort, setInternalSort] = React.useState<SortState>(defaultSort)
  const sortState = isControlled ? sort! : internalSort

  const setSortState = React.useCallback(
    (next: SortState) => {
      if (!isControlled) setInternalSort(next)
      onSortChange?.(next)
    },
    [isControlled, onSortChange]
  )

  const sortedRows = React.useMemo(() => {
    if (!sortState) return rows
    const col = columns.find((c) => c.key === sortState.key)
    if (!col) return rows

    const dirMul = sortState.direction === 'asc' ? 1 : -1

    const decorated = rows.map((r, i) => ({ r, i }))
    decorated.sort((x, y) => {
      let res = 0

      if (col.sortFn) {
        res = col.sortFn(x.r, y.r)
      } else {
        const ax = col.sortValue ? col.sortValue(x.r) : (x.r as any)?.[col.key]
        const by = col.sortValue ? col.sortValue(y.r) : (y.r as any)?.[col.key]

        const na = normalizeSortValue(ax)
        const nb = normalizeSortValue(by)

        // nulls immer nach hinten
        if (na.isNull && !nb.isNull) res = 1
        else if (!na.isNull && nb.isNull) res = -1
        else if (na.kind === 'number' && nb.kind === 'number') res = na.value < nb.value ? -1 : na.value > nb.value ? 1 : 0
        else res = String(na.value).localeCompare(String(nb.value), undefined, { numeric: true })
      }

      if (res === 0) return x.i - y.i // stable
      return res * dirMul
    })

    return decorated.map((d) => d.r)
  }, [rows, columns, sortState])

  return (
    <div className={cn(fullWidth ? '' : 'px-4 sm:px-6 lg:px-8', className)}>
      {(title || description || actions) && (
        <div className="sm:flex sm:items-center">
          <div className="sm:flex-auto">
            {title && (
              <h1 className="text-base font-semibold text-gray-900 dark:text-white">
                {title}
              </h1>
            )}
            {description && (
              <p className="mt-2 text-sm text-gray-700 dark:text-gray-300">
                {description}
              </p>
            )}
          </div>
          {actions && (
            <div className="mt-4 sm:mt-0 sm:ml-16 sm:flex-none">{actions}</div>
          )}
        </div>
      )}

      <div className={cn(title || description || actions ? 'mt-8' : '')}>
        <div className="flow-root">
          <div className="overflow-x-auto">
            <div className={cn('inline-block min-w-full align-middle', fullWidth ? '' : 'sm:px-6 lg:px-8')}>
              <div
                className={cn(
                  card &&
                    'overflow-hidden shadow-sm outline-1 outline-black/5 rounded-lg dark:shadow-none dark:-outline-offset-1 dark:outline-white/10'
                )}
              >
                <table className="relative min-w-full divide-y divide-gray-200 dark:divide-white/10">
                  <thead
                    className={cn(
                      card && 'bg-gray-50/90 dark:bg-gray-800/70',
                      stickyHeader &&
                        'sticky top-0 z-10 backdrop-blur-sm shadow-sm'
                    )}
                  >
                    <tr>
                      {columns.map((col, colIndex) => {
                        const xPad = xPadForColumn(colIndex, columns.length)
                        const isSorted = !!sortState && sortState.key === col.key
                        const dir = isSorted ? sortState!.direction : undefined
                        const ariaSort =
                          col.sortable && !col.srOnlyHeader
                            ? isSorted
                              ? dir === 'asc'
                                ? 'ascending'
                                : 'descending'
                              : 'none'
                            : undefined

                        const nextSort = () => {
                          if (!col.sortable || col.srOnlyHeader) return
                          if (!isSorted) return setSortState({ key: col.key, direction: 'asc' })
                          if (dir === 'asc') return setSortState({ key: col.key, direction: 'desc' })
                          return setSortState(null)
                        }

                        return (
                          <th
                            key={col.key}
                            scope="col"
                            aria-sort={ariaSort as any}
                            className={cn(
                              headY,
                              xPad,
                              'text-xs font-semibold tracking-wide text-gray-700 dark:text-gray-200 whitespace-nowrap',
                              alignTd(col.align),
                              col.widthClassName,
                              col.headerClassName
                            )}
                          >
                            {col.srOnlyHeader ? (
                              <span className="sr-only">{col.header}</span>
                            ) : col.sortable ? (
                              <button
                                type="button"
                                onClick={nextSort}
                                className={cn(
                                  'group inline-flex w-full items-center gap-2 select-none rounded-md px-1.5 py-1 -my-1 hover:bg-gray-100/70 dark:hover:bg-white/5',
                                  justifyForAlign(col.align)
                                )}
                              >
                                <span>{col.header}</span>

                                <span
                                  className={cn(
                                    'flex-none rounded-sm text-gray-400 dark:text-gray-500',
                                    isSorted
                                      ? 'bg-gray-100 text-gray-900 dark:bg-gray-800 dark:text-white'
                                      : 'invisible group-hover:visible group-focus-visible:visible'
                                  )}
                                >
                                  <ChevronDownIcon
                                    aria-hidden="true"
                                    className={cn(
                                      'size-5 transition-transform',
                                      isSorted && dir === 'asc' && 'rotate-180'
                                    )}
                                  />
                                </span>
                              </button>
                            ) : (
                              col.header
                            )}
                          </th>
                        )
                      })}
                    </tr>
                  </thead>

                  <tbody
                    className={cn(
                      'divide-y divide-gray-200 dark:divide-white/10',
                      card ? 'bg-white dark:bg-gray-800/50' : 'bg-white dark:bg-gray-900'
                    )}
                  >
                    {isLoading ? (
                      <tr>
                        <td colSpan={columns.length} className={cn(cellY, 'px-2 text-sm text-gray-500 dark:text-gray-400')}>
                          Lädt…
                        </td>
                      </tr>
                    ) : sortedRows.length === 0 ? (
                      <tr>
                        <td colSpan={columns.length} className={cn(cellY, 'px-2 text-sm text-gray-500 dark:text-gray-400')}>
                          {emptyLabel}
                        </td>
                      </tr>
                    ) : (
                      sortedRows.map((row, rowIndex) => {
                        const key = getRowKey ? getRowKey(row, rowIndex) : String(rowIndex)

                        return (
                          <tr
                            key={key}
                            className={cn(
                              striped && 'even:bg-gray-50 dark:even:bg-gray-800/50',
                              onRowClick && 'cursor-pointer',
                              onRowClick && 'hover:bg-gray-50 dark:hover:bg-white/5 transition-colors',
                              rowClassName?.(row, rowIndex)
                            )}
                            onClick={() => onRowClick?.(row)}
                            onContextMenu={
                              onRowContextMenu
                                ? (e) => {
                                    e.preventDefault()
                                    onRowContextMenu(row, e)
                                  }
                                : undefined
                            }
                          >
                            {columns.map((col, colIndex) => {
                              const xPad = xPadForColumn(colIndex, columns.length)
                              const content =
                                col.cell?.(row, rowIndex) ??
                                col.accessor?.(row) ??
                                (row as any)?.[col.key]

                              return (
                                <td
                                  key={col.key}
                                  className={cn(
                                    cellY,
                                    xPad,
                                    'text-sm whitespace-nowrap',
                                    alignTd(col.align),
                                    col.className,
                                    col.key === columns[0]?.key
                                      ? 'font-medium text-gray-900 dark:text-white'
                                      : 'text-gray-500 dark:text-gray-400'
                                  )}
                                >
                                  {content}
                                </td>
                              )
                            })}
                          </tr>
                        )
                      })
                    )}
                  </tbody>
                </table>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  )
}
