import { useEffect, useMemo, useState, type ReactNode } from 'react'
import {
  Table,
  pixel,
  proportional,
  useTableColumnResize,
  useTablePagination,
  type TableColumn,
  type TablePlugin,
  type TableSortEntry,
  type TableSortState,
} from '@astryxdesign/core'

export interface DataTableColumn<T> {
  id: string
  header?: ReactNode
  sortable?: boolean
  resizable?: boolean
  sortValue?: (row: T) => string | number | null
  searchValue?: (row: T) => string
  cell?: (row: T) => ReactNode
  minWidth?: number
  width?: number | string
  align?: 'start' | 'center' | 'end'
}

export interface DataTableSort {
  columnId: string
  direction: 'asc' | 'desc'
}

interface SortCapability {
  kind: 'sort'
  sort?: DataTableSort | null
  onSortChange?: (sort: DataTableSort | null) => void
  defaultSort?: DataTableSort | null
  manual?: boolean
}

interface SearchCapability {
  kind: 'search'
  label: string
}

interface ColumnResizeCapability {
  kind: 'columnResize'
}

interface PaginationCapability {
  kind: 'pagination'
  pageSize: number
}

export type DataTableCapability = SortCapability | SearchCapability | ColumnResizeCapability | PaginationCapability

export function withSorting(config?: { defaultSort?: DataTableSort | null; sort?: DataTableSort | null; onSortChange?: (sort: DataTableSort | null) => void; manual?: boolean }): DataTableCapability {
  return { kind: 'sort', ...config }
}

export function withSearch(config: { label: string }): DataTableCapability {
  return { kind: 'search', label: config.label }
}

export function withColumnResizing(): DataTableCapability {
  return { kind: 'columnResize' }
}

export function withPagination(config: { pageSize: number }): DataTableCapability {
  return { kind: 'pagination', pageSize: config.pageSize }
}

export interface DataTableProps<T> {
  caption: string
  columns: DataTableColumn<T>[]
  rows: readonly T[]
  getRowId: (row: T) => string | number
  capabilities?: DataTableCapability[]
  emptyState?: ReactNode
}

const SEARCH_CONTROL =
  'w-full rounded-md border border-border bg-surface-raised px-3 py-1.5 text-sm text-fg placeholder:text-fg-subtle focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent'

const SORT_BUTTON =
  'inline-flex w-full items-center gap-1 bg-transparent text-inherit font-semibold focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-accent'

function compareValues(a: string | number | null | undefined, b: string | number | null | undefined): number {
  if (typeof a === 'number' && typeof b === 'number') return a - b
  return String(a ?? '').localeCompare(String(b ?? ''), undefined, { numeric: true, sensitivity: 'base' })
}

function toAstryxSort(sort: DataTableSort | null): TableSortState {
  if (!sort) return []
  return [{ sortKey: sort.columnId, direction: sort.direction === 'asc' ? 'ascending' : 'descending' }]
}

/** ui-primitives-compatible DataTable composed over the astryx Table. */
export function DataTable<T extends object>({ caption, columns, rows, getRowId, capabilities = [], emptyState }: DataTableProps<T>) {
  const rowsAny = rows as Record<string, unknown>[]
  const sortCap = capabilities.find((c): c is SortCapability => c.kind === 'sort')
  const searchCap = capabilities.find((c): c is SearchCapability => c.kind === 'search')
  const resizeCap = capabilities.find((c): c is ColumnResizeCapability => c.kind === 'columnResize')
  const paginationCap = capabilities.find((c): c is PaginationCapability => c.kind === 'pagination')

  const [query, setQuery] = useState('')
  const [page, setPage] = useState(1)
  const [columnWidths, setColumnWidths] = useState<Record<string, number>>({})
  const [internalSort, setInternalSort] = useState<TableSortState>(() => toAstryxSort(sortCap?.defaultSort ?? null))

  const filtered = useMemo(() => {
    if (!searchCap || query.trim() === '') return rowsAny
    const needle = query.trim().toLowerCase()
    return rowsAny.filter((row) =>
      columns.some((col) => {
        const haystack = col.searchValue ? col.searchValue(row as T) : String(row[col.id] ?? '')
        return haystack.toLowerCase().includes(needle)
      }),
    )
  }, [rowsAny, columns, searchCap, query])

  const sortState = sortCap?.sort != null ? toAstryxSort(sortCap.sort) : internalSort

  const emitSort = (next: TableSortState) => {
    if (sortCap?.onSortChange) {
      sortCap.onSortChange(next.length === 0 ? null : { columnId: next[0]!.sortKey, direction: next[0]!.direction === 'ascending' ? 'asc' : 'desc' })
    } else {
      setInternalSort(next)
    }
    if (paginationCap) setPage(1)
  }

  const cycleColumn = (columnId: string) => {
    if (sortState.length === 0 || sortState[0]!.sortKey !== columnId) {
      emitSort([{ sortKey: columnId, direction: 'ascending' }])
    } else if (sortState[0]!.direction === 'ascending') {
      emitSort([{ sortKey: columnId, direction: 'descending' }])
    } else {
      emitSort([])
    }
  }

  const sorted = useMemo(() => {
    if (!sortCap || sortCap.manual || sortState.length === 0) return filtered
    const entry = sortState[0]!
    const column = columns.find((c) => c.id === entry.sortKey)
    if (!column) return filtered
    const direction = entry.direction === 'ascending' ? 1 : -1
    return filtered
      .map((row, index) => ({ row, index }))
      .sort((a, b) => {
        const keyA = column.sortValue ? column.sortValue(a.row as T) : (a.row[entry.sortKey] as string | number | null | undefined)
        const keyB = column.sortValue ? column.sortValue(b.row as T) : (b.row[entry.sortKey] as string | number | null | undefined)
        if (keyA == null || keyB == null) {
          if (keyA == null && keyB == null) return a.index - b.index
          return keyA == null ? 1 : -1
        }
        const order = compareValues(keyA, keyB)
        return order !== 0 ? order * direction : a.index - b.index
      })
      .map((entryRow) => entryRow.row)
  }, [filtered, columns, sortState, sortCap])

  const aColumns = useMemo<TableColumn<Record<string, unknown>>[]>(
    () =>
      columns.map((col) => {
        const entry: TableSortEntry | undefined = sortCap && col.sortable ? sortState.find((e) => e.sortKey === col.id) : undefined
        return {
          key: col.id,
          header:
            sortCap && col.sortable ? (
              <button type="button" onClick={() => cycleColumn(col.id)} className={SORT_BUTTON}>
                <span>{col.header}</span>
                {entry ? (
                  <span aria-hidden="true" className="text-fg-subtle">
                    {entry.direction === 'ascending' ? ' \u2191' : ' \u2193'}
                  </span>
                ) : null}
              </button>
            ) : (
              col.header
            ),
          align: col.align,
          sortable: col.sortable ? { sortKey: col.id } : undefined,
          resizable: col.resizable ?? false,
          width: col.width === undefined ? (col.minWidth ? proportional(1, { minWidth: col.minWidth }) : proportional(1)) : typeof col.width === 'number' ? pixel(col.width) : proportional(1),
          renderCell: col.cell,
        }
      }) as TableColumn<Record<string, unknown>>[],
    [columns, sortCap, sortState],
  )

  const resizePlugin = useTableColumnResize<Record<string, unknown>>({ columnWidths, onColumnResizeEnd: setColumnWidths, columns: aColumns })

  const ariaSortPlugin: TablePlugin<Record<string, unknown>> = {
    transformHeaderCell: (props, column) => {
      if (!column.sortable) return props
      const entry = sortState.find((e) => e.sortKey === column.key)
      return { ...props, htmlProps: { ...props.htmlProps, 'aria-sort': entry ? entry.direction : 'none' } }
    },
  }

  const pageSize = paginationCap?.pageSize ?? 0
  const pageCount = pageSize > 0 ? Math.max(1, Math.ceil(sorted.length / pageSize)) : 1

  useEffect(() => {
    setPage((p) => Math.min(p, pageCount))
  }, [pageCount])

  const pageRows = pageSize > 0 ? sorted.slice((page - 1) * pageSize, page * pageSize) : sorted

  const paginationPlugin = useTablePagination<Record<string, unknown>>({
    page,
    onPageChange: setPage,
    totalItems: sorted.length,
    pageSize: pageSize > 0 ? pageSize : 10,
    position: 'below',
  })

  const plugins = useMemo(() => {
    const all: Record<string, TablePlugin<Record<string, unknown>>> = {}
    if (paginationCap) all.pagination = paginationPlugin
    if (resizeCap) all.columnResize = resizePlugin
    if (sortCap) all.sort = ariaSortPlugin
    return all
  }, [paginationCap, resizeCap, sortCap, paginationPlugin, resizePlugin, ariaSortPlugin])

  return (
    <div className="flex min-w-0 flex-col">
      {searchCap ? (
        <div className="px-3 pt-3">
          <input type="search" aria-label={searchCap.label} placeholder={searchCap.label} value={query} onChange={(event) => setQuery(event.target.value)} className={SEARCH_CONTROL} />
        </div>
      ) : null}
      <Table aria-label={caption} data={pageRows} columns={aColumns} idKey={(item) => getRowId(item as T)} plugins={plugins} emptyState={emptyState} density="compact" />
    </div>
  )
}