import type { ReactNode } from 'react'

import {
  BODY_CELL,
  HEAD_CELL,
  NUMERIC_CELL,
  TABLE_FRAME,
} from '@/components/table'
import { TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'
import { cn } from '@/lib/utils'

export interface MetricsColumn<T> {
  /** El encabezado, ya traducido. **Obligatorio**: es la razón de ser de la pieza. */
  header: string
  /** Alinea a la derecha y fija cifras tabulares, en la celda y en el encabezado. */
  numeric?: boolean
  cell: (row: T) => ReactNode
}

interface Props<T> {
  /** Qué lista es. Es el `<caption>`, fuera de la vista. */
  caption: string
  columns: MetricsColumn<T>[]
  rows: T[]
  rowKey: (row: T) => string
  /**
   * A partir de cuántas filas la lista se acota y se desplaza por dentro.
   *
   * **Es un umbral y no una altura fija**, y la diferencia es todo el asunto: un
   * `max-h` escrito a secas le pone marco y barra a una lista de tres filas, que
   * no lo necesita y que además queda flotando adentro de una caja mucho más
   * alta. Con el umbral, la lista corta se dibuja entera y sin recuadro interno,
   * y sólo la larga —una consulta con dieciocho páginas compitiendo— se
   * convierte en una región desplazable con el encabezado fijo.
   */
  scrollAfter?: number
}

/** Doce filas entran de sobra en una tarjeta sin empujar el resto de la página. */
const DEFAULT_SCROLL_AFTER = 12

/**
 * Una región desplazable **de esta pieza**, y no `TABLE_SCROLLER`.
 *
 * Aquélla acota al 70 % de la altura de la ventana, que es lo correcto para una
 * tabla que es la pantalla entera. Acá la lista vive adentro de una tarjeta que
 * tiene cosas arriba y abajo, y setecientos píxeles de lista adentro de una
 * tarjeta son la tarjeta empujando todo lo demás fuera de la vista.
 */
const EMBEDDED_SCROLLER = 'max-h-96 overflow-y-auto overscroll-contain'

/**
 * Una lista de cifras **con sus encabezados**.
 *
 * Existe porque las tres listas incrustadas del módulo —las páginas que rankean
 * por una consulta, las consultas por las que aparece una página y las URLs que
 * se pisan— eran tres columnas de números pegadas a la derecha **sin decir qué
 * era cada una**. Quien miraba veía `43 %`, `12,4` y `318` y tenía que deducir
 * cuál era el reparto, cuál la posición y cuál las impresiones por el tamaño de
 * los números.
 *
 * No es `ClientDataTable` ni la reemplaza: aquélla busca, ordena, esconde
 * columnas y pagina, y todo eso adentro de una tarjeta que es la mitad de una
 * pantalla es una tabla peleándole el protagonismo al análisis que la rodea.
 * Ésta hace lo único que faltaba: nombrar las columnas.
 */
export function MetricsTable<T>({
  caption,
  columns,
  rows,
  rowKey,
  scrollAfter = DEFAULT_SCROLL_AFTER,
}: Props<T>) {
  const scrolls = rows.length > scrollAfter

  return (
    <div className={cn(TABLE_FRAME, scrolls && EMBEDDED_SCROLLER)}>
      <table className="w-full caption-bottom text-sm">
        <caption className="sr-only">{caption}</caption>

        <TableHeader>
          <TableRow>
            {columns.map((column) => (
              <TableHead
                key={column.header}
                scope="col"
                className={cn(
                  column.numeric && NUMERIC_CELL,
                  // El encabezado se queda arriba **sólo cuando hay por dónde
                  // desplazarse**: `sticky` sin un contenedor acotado no se fija
                  // contra nada y lo único que hace es crear un contexto de
                  // apilado de más.
                  scrolls && HEAD_CELL
                )}
              >
                {column.header}
              </TableHead>
            ))}
          </TableRow>
        </TableHeader>

        <TableBody>
          {rows.map((row) => (
            <TableRow key={rowKey(row)}>
              {columns.map((column) => (
                <TableCell
                  key={column.header}
                  className={cn(BODY_CELL, column.numeric && NUMERIC_CELL)}
                >
                  {column.cell(row)}
                </TableCell>
              ))}
            </TableRow>
          ))}
        </TableBody>
      </table>
    </div>
  )
}
