import {
  Accordion,
  AccordionDetails,
  AccordionSummary,
  accordionSummaryClasses,
  Box,
  Skeleton,
  Table,
  TableBody,
  TableCell,
  TableContainer,
  TableHead,
  TableRow,
  TableSortLabel,
  tableSortLabelClasses,
  Typography
} from '@mui/material';
import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';

import { Pagination } from '@/components/common/Pagination';

import type { ListState } from '../store/listState';

export interface Column<T> {
  label: string;
  render: (row: T) => React.ReactNode;
  sortKey?: string;
}

// Clamp cell content to 3 lines; hidden overflow by default, scrollable on
// hover so long values (commands, image refs, port lists) stay readable without
// stretching the row. `overflowWrap: anywhere` lets long unbroken tokens wrap
// instead of overflowing the fixed-width column.
const LINE_HEIGHT_EM = 1.4;
const clampSx = {
  lineHeight: `${LINE_HEIGHT_EM}em`,
  maxHeight: `${LINE_HEIGHT_EM * 3}em`,
  overflow: 'hidden',
  overflowWrap: 'anywhere' as const,
  '&:hover': { overflowY: 'auto' as const }
};

interface SectionProps<T> {
  // Controls shown next to the title, e.g. a "view all" link or a filter switch.
  actions?: React.ReactNode;
  columns: Column<T>[];
  emptyText: string;
  list: ListState<T>;
  onChangePage: (page: number) => void;
  onChangePerPage: (perPage: number) => void;
  onSort: (sortKey: string) => void;
  sectionRef?: React.Ref<HTMLDivElement>;
  testId: string;
  title: string;
}

const PaginatedSection = <T,>({
  actions,
  title,
  testId,
  columns,
  list,
  emptyText,
  onChangePage,
  onChangePerPage,
  onSort,
  sectionRef
}: SectionProps<T>) => (
  <Accordion
    ref={sectionRef}
    defaultExpanded
    disableGutters
    square
    elevation={0}
    slots={{ heading: 'div' }}
    slotProps={{ region: { id: `${testId}-content`, 'aria-labelledby': `${testId}-title` } }}
    sx={{
      mt: 3,
      scrollMarginTop: 16,
      backgroundColor: 'transparent',
      '&::before': { display: 'none' }
    }}
  >
    <Box sx={{ display: 'flex', alignItems: 'center' }}>
      <AccordionSummary
        expandIcon={<ExpandMoreIcon />}
        aria-controls={`${testId}-content`}
        sx={{
          flexDirection: 'row-reverse',
          gap: 1,
          px: 0,
          width: 'auto',
          minHeight: 'auto',
          [`& .${accordionSummaryClasses.content}`]: { my: 1.5, alignItems: 'center' }
        }}
      >
        <Typography id={`${testId}-title`} component="h4" variant="h6">
          {title}
        </Typography>
      </AccordionSummary>
      {actions && <Box sx={{ display: 'flex', alignItems: 'center', flexGrow: 1, ml: 2 }}>{actions}</Box>}
    </Box>
    <AccordionDetails sx={{ p: 0 }}>
      <TableContainer sx={{ overflowX: 'auto' }}>
        <Table size="small" sx={{ minWidth: 800, tableLayout: 'fixed' }}>
          <TableHead>
            <TableRow>
              {columns.map(col => {
                const active = !!col.sortKey && list.sort === col.sortKey;
                return (
                  <TableCell key={col.label} sortDirection={active ? list.order : false}>
                    {col.sortKey ? (
                      <TableSortLabel
                        active={active}
                        direction={active ? list.order : 'asc'}
                        IconComponent={ArrowDropDownIcon}
                        onClick={() => onSort(col.sortKey as string)}
                        sx={{ [`& .${tableSortLabelClasses.icon}`]: { fontSize: 24 } }}
                      >
                        {col.label}
                      </TableSortLabel>
                    ) : (
                      col.label
                    )}
                  </TableCell>
                );
              })}
            </TableRow>
          </TableHead>
          <TableBody data-testid={`${testId}-rows`}>
            {list.loading ? (
              // Render as many skeleton rows as are currently shown (or a full
              // page on first load) so the table keeps its height — swapping to
              // a single spinner row is what made it collapse and jump.
              Array.from({ length: list.rows.length || list.perPage }).map((_, ri) => (
                <TableRow key={ri} sx={{ height: 58 }}>
                  {columns.map(col => (
                    <TableCell key={col.label}>
                      <Skeleton variant="text" />
                    </TableCell>
                  ))}
                </TableRow>
              ))
            ) : list.rows.length > 0 ? (
              list.rows.map((row, ri) => (
                <TableRow key={ri} hover>
                  {columns.map(col => (
                    <TableCell key={col.label} sx={{ verticalAlign: 'top' }}>
                      <Box sx={clampSx}>{col.render(row)}</Box>
                    </TableCell>
                  ))}
                </TableRow>
              ))
            ) : (
              <TableRow>
                <TableCell colSpan={columns.length} className="muted">
                  {list.error ?? emptyText}
                </TableCell>
              </TableRow>
            )}
          </TableBody>
        </Table>
      </TableContainer>
      <Pagination
        align="left"
        count={list.total}
        rowsPerPage={list.perPage}
        page={list.page}
        onChangePage={onChangePage}
        onChangeRowsPerPage={onChangePerPage}
        // Empty options hides the "Rows per page" label + selector.
        rowsPerPageOptions={[]}
      />
    </AccordionDetails>
  </Accordion>
);

export default PaginatedSection;
