import { useCallback, useMemo } from 'react'
import {
  getWidgetStore,
  useWidget,
  useWidgetId,
  useWidgetShallow,
} from '../stores'
import { TableUI, type TableUIProps } from './table-ui'
import {
  DEFAULT_TABLE_PAGE_SIZE,
  type TableColumn,
  type TableRow,
  type TableSortState,
  type TableWidgetState,
} from './types'
import { deriveVisibleRows, resolveColumns } from './helpers'

export interface TableProps<T extends TableRow = TableRow> extends Pick<
  TableUIProps<T>,
  | 'pageSizeOptions'
  | 'selectable'
  | 'labels'
  | 'emptyContent'
  | 'size'
  | 'keyColumn'
> {
  /**
   * Column definitions. Order can be overridden by ChangeColumn via the
   * `columnOrder` field on the extended widget state.
   */
  columns: readonly TableColumn[]
  /**
   * When set, render only the first `visibleColumns` entries **after**
   * applying the user's `columnOrder` from the store. Useful for showing
   * a compact projection while letting consumers (e.g. `Widget.ChangeColumn`)
   * see the full column list. When omitted, every column renders.
   */
  visibleColumns?: number
  /** Initial page size (only on first mount; afterwards lives on the store). */
  initialPageSize?: number
  /** Selected row ids (destination-owned). */
  selection?: readonly (string | number)[]
  onSelectionChange?: (next: readonly (string | number)[]) => void
  onRowClick?: (row: T) => void
  onRowHover?: (row: T | null) => void
  /**
   * When `true`, the bridge stops sorting and paginating locally and
   * renders `data` as-is — the consumer is then responsible for
   * refetching the slice that matches the active `page` / `pageSize` /
   * `sort` from the outbound callbacks below. `total` is required in
   * this mode (the server already knows the full row count; the
   * widget can't infer it from a partial page).
   */
  remote?: boolean
  /**
   * Server-reported total row count. Required when `remote === true`;
   * ignored in local mode (where the bridge derives total from the
   * sorted result of `deriveVisibleRows`).
   */
  total?: number
  /** Fires after the store-level sort write so the consumer can refetch. */
  onSortChange?: (next: TableSortState) => void
  /** Fires after the store-level page write so the consumer can refetch. */
  onPageChange?: (page: number) => void
  /** Fires after the store-level pageSize write so the consumer can refetch. */
  onPageSizeChange?: (pageSize: number) => void
}

/**
 * Bridge component — reads the widget store's `data`, applies sort + pagination
 * locally, and feeds `<TableUI />`. Sort, page, and pageSize live on the
 * extended widget state ({@link TableWidgetState}).
 */
export function Table<T extends TableRow = TableRow>({
  columns,
  visibleColumns,
  initialPageSize = DEFAULT_TABLE_PAGE_SIZE,
  selection,
  onSelectionChange,
  onRowClick,
  onRowHover,
  remote = false,
  total: remoteTotal,
  onSortChange,
  onPageChange,
  onPageSizeChange,
  ...uiProps
}: TableProps<T>) {
  const id = useWidgetId()
  const data = useWidget(id, (s) => s.data as readonly T[] | undefined)
  const ui = useWidgetShallow<
    {
      sort: TableSortState | undefined
      page: number
      pageSize: number
      columnOrder: readonly string[] | undefined
    },
    TableWidgetState
  >(id, (s) => ({
    sort: s.sort,
    page: s.page ?? 0,
    pageSize: s.pageSize ?? initialPageSize,
    columnOrder: s.columnOrder,
  }))

  const orderedColumns = useMemo(() => {
    const reordered = resolveColumns(columns, ui.columnOrder)
    return visibleColumns == null
      ? reordered
      : reordered.slice(0, visibleColumns)
  }, [columns, ui.columnOrder, visibleColumns])

  // In remote mode the consumer already paginated / sorted server-side,
  // so pass `data` and `total` straight through. Local mode keeps the
  // bridge-driven sort + slice via `deriveVisibleRows`.
  const { rows, totalRows } = useMemo(() => {
    if (remote) {
      return {
        rows: data ?? [],
        totalRows: remoteTotal ?? 0,
      }
    }
    const { sorted, visible } = deriveVisibleRows(data ?? [], {
      sort: ui.sort,
      page: ui.page,
      pageSize: ui.pageSize,
    })
    return { rows: visible, totalRows: sorted.length }
  }, [remote, data, remoteTotal, ui.sort, ui.page, ui.pageSize])

  const handleSortChange = useCallback(
    (next: TableSortState) => {
      getWidgetStore(id).setState({
        sort: next,
        page: 0,
      } as Partial<TableWidgetState>)
      onSortChange?.(next)
    },
    [id, onSortChange],
  )

  const handlePageChange = useCallback(
    (page: number) => {
      getWidgetStore(id).setState({ page } as Partial<TableWidgetState>)
      onPageChange?.(page)
    },
    [id, onPageChange],
  )

  const handlePageSizeChange = useCallback(
    (pageSize: number) => {
      getWidgetStore(id).setState({
        pageSize,
        page: 0,
      } as Partial<TableWidgetState>)
      onPageSizeChange?.(pageSize)
    },
    [id, onPageSizeChange],
  )

  return (
    <TableUI
      {...uiProps}
      columns={orderedColumns}
      rows={rows}
      total={totalRows}
      page={ui.page}
      pageSize={ui.pageSize}
      sort={ui.sort}
      selection={selection}
      onSortChange={handleSortChange}
      onPageChange={handlePageChange}
      onPageSizeChange={handlePageSizeChange}
      onSelectionChange={onSelectionChange}
      onRowClick={onRowClick}
      onRowHover={onRowHover}
    />
  )
}
