import type { ReactNode } from "react";
import type { SxProps, Theme } from "@mui/material/styles";
import { useTheme } from "@mui/material/styles";
import useMediaQuery from "@mui/material/useMediaQuery";
import Box from "@mui/material/Box";
import Typography from "@mui/material/Typography";
import Table from "@mui/material/Table";
import TableBody from "@mui/material/TableBody";
import TableCell from "@mui/material/TableCell";
import TableContainer from "@mui/material/TableContainer";
import TableHead from "@mui/material/TableHead";
import TableRow from "@mui/material/TableRow";
import TableSortLabel from "@mui/material/TableSortLabel";
import MenuItem from "@mui/material/MenuItem";
import MuiTextField from "@mui/material/TextField";
import IconButton from "@mui/material/IconButton";

export interface DataColumn<T> {
  key: string;
  header: string;
  align?: "left" | "right";
  /** Render the cell with the DM Mono numeric face. */
  mono?: boolean;
  /** Emphasize the cell (font-weight 500). */
  bold?: boolean;
  /** If set, the header becomes a clickable sort control (requires onSort). */
  sortKey?: string;
  /**
   * Fixed column width (number = px, string = any CSS width incl. "%"). When
   * ANY column sets a width the table switches to `table-layout: fixed`, so
   * columns keep a stable size regardless of cell content (e.g. while
   * filtering). Overflowing text is clipped with an ellipsis.
   */
  width?: number | string;
  render: (row: T) => ReactNode;
}

export interface DataTableProps<T> {
  columns: DataColumn<T>[];
  rows: T[];
  rowKey: (row: T) => string | number;
  rowInactive?: (row: T) => boolean;
  /** When set, each row is clickable and shows a pointer cursor. */
  onRowClick?: (row: T) => void;
  /** When it returns true, the row is tinted with the error palette (danger highlight). */
  rowDanger?: (row: T) => boolean;
  /** Per-row style escape hatch (merged last) for arbitrary status tints. */
  rowSx?: (row: T) => SxProps<Theme>;
  empty?: ReactNode;
  /** Server-sort wiring: active key (matches a column's sortKey), direction, handler. */
  sortBy?: string;
  sortDir?: "asc" | "desc";
  onSort?: (key: string) => void;
}

/** Dense, Soft-SaaS-styled data table. Styling only — never re-formats values. */
export default function DataTable<T>({
  columns,
  rows,
  rowKey,
  rowInactive,
  onRowClick,
  rowDanger,
  rowSx,
  empty,
  sortBy,
  sortDir,
  onSort,
}: DataTableProps<T>) {
  const theme = useTheme();
  const isMobile = useMediaQuery(theme.breakpoints.down("sm"));
  if (import.meta.env.DEV && columns.some((c) => c.sortKey) && !onSort) {
    console.warn("DataTable: columns with sortKey require an onSort prop.");
  }
  if (rows.length === 0 && empty) return <>{empty}</>;

  // On phones a wide multi-column table would force horizontal scrolling and
  // truncate every cell. Instead render each row as a stacked "label: value"
  // card so all content is readable; the actions column (key "actions") drops
  // to a button row at the bottom.
  if (isMobile) {
    const actionCol = columns.find((c) => c.key === "actions");
    const dataCols = columns.filter((c) => c.key !== "actions");
    // The header row (with its sort labels) is gone in the card layout, so
    // surface the same columns[].sortKey/onSort wiring through a compact
    // dropdown + direction toggle, otherwise mobile users can't sort at all.
    const sortCols = columns.filter((c) => c.sortKey);
    return (
      <Box sx={{ display: "flex", flexDirection: "column", gap: 1.5 }}>
        {onSort && sortCols.length > 0 && (
          <Box sx={{ display: "flex", alignItems: "center", gap: 1 }}>
            <MuiTextField
              select
              size="small"
              label="Řadit podle"
              value={sortBy ?? ""}
              onChange={(e) => onSort(e.target.value)}
              sx={{ flex: 1 }}
            >
              {sortCols.map((c) => (
                <MenuItem key={c.key} value={c.sortKey!}>
                  {c.header}
                </MenuItem>
              ))}
            </MuiTextField>
            <IconButton
              aria-label={
                sortDir === "desc" ? "Řadit vzestupně" : "Řadit sestupně"
              }
              disabled={!sortBy}
              onClick={() => sortBy && onSort(sortBy)}
              size="small"
            >
              <svg
                width="20"
                height="20"
                viewBox="0 0 24 24"
                fill="none"
                stroke="currentColor"
                strokeWidth="2"
                aria-hidden="true"
                style={{
                  transform:
                    sortDir === "desc" ? "rotate(180deg)" : "rotate(0deg)",
                  transition: "transform .15s",
                }}
              >
                <line x1="12" y1="19" x2="12" y2="5" />
                <polyline points="5 12 12 5 19 12" />
              </svg>
            </IconButton>
          </Box>
        )}
        {rows.map((row) => {
          const extra = rowSx?.(row);
          return (
            <Box
              key={rowKey(row)}
              onClick={onRowClick ? () => onRowClick(row) : undefined}
              sx={[
                {
                  border: 1,
                  borderColor: "divider",
                  borderRadius: 2,
                  p: 1.5,
                  display: "flex",
                  flexDirection: "column",
                  gap: 0.75,
                  bgcolor: "background.paper",
                },
                onRowClick ? { cursor: "pointer" } : {},
                rowDanger?.(row)
                  ? {
                      borderColor: "error.main",
                      backgroundColor:
                        "rgba(var(--mui-palette-error-mainChannel) / 0.12)",
                    }
                  : {},
                rowInactive?.(row) ? { opacity: 0.55 } : {},
                ...(Array.isArray(extra) ? extra : extra ? [extra] : []),
              ]}
            >
              {dataCols.map((c) => (
                <Box
                  key={c.key}
                  sx={{
                    display: "flex",
                    justifyContent: "space-between",
                    alignItems: "baseline",
                    gap: 1.5,
                  }}
                >
                  <Typography
                    component="span"
                    sx={{
                      flexShrink: 0,
                      color: "text.secondary",
                      textTransform: "uppercase",
                      letterSpacing: ".05em",
                      fontWeight: 700,
                      fontSize: ".64rem",
                      pt: 0.25,
                    }}
                  >
                    {c.header}
                  </Typography>
                  <Box
                    sx={{
                      minWidth: 0,
                      textAlign: "right",
                      fontSize: ".85rem",
                      ...(c.bold ? { fontWeight: 500 } : {}),
                      ...(c.mono
                        ? { fontFamily: "'DM Mono', Menlo, monospace" }
                        : {}),
                    }}
                  >
                    {c.render(row)}
                  </Box>
                </Box>
              ))}
              {actionCol && (
                <Box
                  onClick={(e) => e.stopPropagation()}
                  sx={{
                    display: "flex",
                    flexWrap: "wrap",
                    justifyContent: "flex-end",
                    gap: 0.5,
                    pt: 0.75,
                    mt: 0.25,
                    borderTop: 1,
                    borderColor: "divider",
                  }}
                >
                  {actionCol.render(row)}
                </Box>
              )}
            </Box>
          );
        })}
      </Box>
    );
  }
  // When any column declares a width, lock the table to fixed layout so column
  // widths come from the colgroup (stable) instead of from cell content (which
  // jumps as rows are filtered). Tables that set no widths keep auto layout.
  const hasWidths = columns.some((c) => c.width != null);
  const colWidth = (w: number | string | undefined) =>
    w == null ? undefined : typeof w === "number" ? `${w}px` : w;
  return (
    <TableContainer sx={{ borderRadius: 2, overflowX: "auto" }}>
      <Table
        size="small"
        sx={{
          ...(hasWidths ? { tableLayout: "fixed", minWidth: 720 } : {}),
          "& td, & th": { borderColor: "divider" },
        }}
      >
        {hasWidths && (
          <colgroup>
            {columns.map((c) => (
              <col key={c.key} style={{ width: colWidth(c.width) }} />
            ))}
          </colgroup>
        )}
        <TableHead>
          <TableRow>
            {columns.map((c) => (
              <TableCell
                key={c.key}
                align={c.align}
                sortDirection={
                  c.sortKey && sortBy === c.sortKey ? (sortDir ?? "asc") : false
                }
                sx={{
                  textTransform: "uppercase",
                  letterSpacing: ".05em",
                  fontSize: ".64rem",
                  fontWeight: 700,
                  color: "text.secondary",
                }}
              >
                {c.sortKey && onSort ? (
                  <TableSortLabel
                    active={sortBy === c.sortKey}
                    direction={
                      sortBy === c.sortKey ? (sortDir ?? "asc") : "asc"
                    }
                    onClick={() => onSort(c.sortKey!)}
                  >
                    {c.header}
                  </TableSortLabel>
                ) : (
                  c.header
                )}
              </TableCell>
            ))}
          </TableRow>
        </TableHead>
        <TableBody>
          {rows.map((row) => {
            const extra = rowSx?.(row);
            return (
              <TableRow
                key={rowKey(row)}
                hover
                onClick={onRowClick ? () => onRowClick(row) : undefined}
                sx={[
                  onRowClick ? { cursor: "pointer" } : {},
                  rowDanger?.(row)
                    ? {
                        backgroundColor:
                          "rgba(var(--mui-palette-error-mainChannel) / 0.12)",
                        "&:hover": {
                          backgroundColor:
                            "rgba(var(--mui-palette-error-mainChannel) / 0.18)",
                        },
                      }
                    : {},
                  rowInactive?.(row) ? { opacity: 0.55 } : {},
                  ...(Array.isArray(extra) ? extra : extra ? [extra] : []),
                ]}
              >
                {columns.map((c) => (
                  <TableCell
                    key={c.key}
                    align={c.align}
                    // The actions cell must not bubble its click to the row: an
                    // action button inside an onRowClick row would otherwise
                    // fire both the action AND the row navigation.
                    onClick={
                      onRowClick && c.key === "actions"
                        ? (e) => e.stopPropagation()
                        : undefined
                    }
                    sx={{
                      fontSize: ".8rem",
                      ...(c.bold ? { fontWeight: 500 } : {}),
                      ...(c.mono
                        ? { fontFamily: "'DM Mono', Menlo, monospace" }
                        : {}),
                      ...(hasWidths
                        ? {
                            whiteSpace: "nowrap",
                            overflow: "hidden",
                            textOverflow: "ellipsis",
                          }
                        : {}),
                    }}
                  >
                    {c.render(row)}
                  </TableCell>
                ))}
              </TableRow>
            );
          })}
        </TableBody>
      </Table>
    </TableContainer>
  );
}
