import type { CSSProperties, ReactNode } from 'react';
import { cx } from '../../utils/cx';
import './Table.css';

export type TableAlign = 'start' | 'center' | 'end';

export interface TableColumn<T> {
  /** Identifies the column and, by default, reads `row[key]` for the cell. */
  key: string;
  header: ReactNode;
  /** Custom cell renderer. Use for badges, buttons, formatted values. */
  render?: (row: T, rowIndex: number) => ReactNode;
  align?: TableAlign;
  /** Any CSS width, e.g. `"120px"` or `"25%"`. */
  width?: string;
}

export interface TableProps<T extends Record<string, unknown>> {
  columns: TableColumn<T>[];
  rows: T[];
  /** Stable row key. Defaults to the row index. */
  getRowKey?: (row: T, index: number) => string | number;
  /** Describes the table for screen readers. Visually hidden unless `showCaption`. */
  caption?: ReactNode;
  showCaption?: boolean;
  density?: 'compact' | 'normal';
  /** Tints alternating rows. */
  striped?: boolean;
  /** Highlights the row under the pointer. */
  hoverable?: boolean;
  /** Shown in place of the body when `rows` is empty. */
  emptyMessage?: ReactNode;
  className?: string;
  style?: CSSProperties;
}

/**
 * Data table. Pass `columns` and `rows`; use a column's `render` for anything
 * that is not plain text.
 */
export function Table<T extends Record<string, unknown>>({
  columns,
  rows,
  getRowKey,
  caption,
  showCaption = false,
  density = 'normal',
  striped = false,
  hoverable = true,
  emptyMessage = 'No data',
  className,
  style,
}: TableProps<T>) {
  return (
    <div className={cx('ds-table-wrap', className)} style={style}>
      <table
        className={cx(
          'ds-table',
          `ds-table--${density}`,
          striped && 'ds-table--striped',
          hoverable && 'ds-table--hoverable',
        )}
      >
        {caption != null && (
          <caption className={cx('ds-table__caption', !showCaption && 'ds-visually-hidden')}>
            {caption}
          </caption>
        )}
        <thead className="ds-table__head">
          <tr>
            {columns.map((column) => (
              <th
                key={column.key}
                scope="col"
                style={column.width ? { width: column.width } : undefined}
                className={cx('ds-table__th', column.align && `ds-table__cell--${column.align}`)}
              >
                {column.header}
              </th>
            ))}
          </tr>
        </thead>
        <tbody className="ds-table__body">
          {rows.length === 0 ? (
            <tr>
              <td className="ds-table__empty" colSpan={columns.length}>
                {emptyMessage}
              </td>
            </tr>
          ) : (
            rows.map((row, rowIndex) => (
              <tr key={getRowKey ? getRowKey(row, rowIndex) : rowIndex} className="ds-table__row">
                {columns.map((column) => (
                  <td
                    key={column.key}
                    className={cx(
                      'ds-table__td',
                      column.align && `ds-table__cell--${column.align}`,
                    )}
                  >
                    {column.render
                      ? column.render(row, rowIndex)
                      : ((row[column.key] ?? '') as ReactNode)}
                  </td>
                ))}
              </tr>
            ))
          )}
        </tbody>
      </table>
    </div>
  );
}
