import { useEffect, useRef } from 'react';
import pluralize from 'pluralize';
import {
  Accordion,
  AccordionDetails,
  AccordionSummary,
  Box,
  CircularProgress,
  Divider,
  Link,
  Skeleton,
  Table,
  TableBody,
  TableCell,
  TableContainer,
  TableHead,
  TableRow,
  Tooltip,
  Typography
} from '@mui/material';
import { tablePaginationClasses } from '@mui/material/TablePagination';
import AutorenewIcon from '@mui/icons-material/Autorenew';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import InfoIcon from '@mui/icons-material/Info';

import { Pagination } from '@northern.tech/common-ui';

import { useAppDispatch, useAppSelector } from '../store';
import {
  selectContainersList,
  selectContainersSummary,
  selectContainersSummaryLoading,
  selectImagesList
} from '../store/containersSlice/selectors';
import {
  fetchContainerImages,
  fetchContainers,
  fetchContainersSummary
} from '../store/containersSlice/thunks';
import { setListPage, setListPerPage } from '../store/containersSlice';
import type { Container, ContainerImage } from '../store/containersSlice/types';
import {
  formatBytes,
  formatContainerStatus,
  formatPorts,
  formatRelativeTime
} from '../utils/format';
import { capitalize } from '../utils/strings';

interface ContainersTabProps {
  hostkey: string;
}

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

// 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 ListState<T> {
  rows: readonly T[];
  total: number;
  page: number;
  perPage: number;
  loading: boolean;
  error: string | null;
}

interface SectionProps<T> {
  title: string;
  // Stable hook for tests, e.g. `containers`/`images` (used as the
  // `${testId}-rows` data-testid on the table body).
  testId: string;
  columns: Column<T>[];
  list: ListState<T>;
  emptyText: string;
  onChangePage: (page: number) => void;
  onChangePerPage: (perPage: number) => void;
  sectionRef?: React.Ref<HTMLDivElement>;
}

const PaginatedSection = <T,>({
  title,
  testId,
  columns,
  list,
  emptyText,
  onChangePage,
  onChangePerPage,
  sectionRef
}: SectionProps<T>) => (
  <Accordion
    ref={sectionRef}
    defaultExpanded
    disableGutters
    square
    elevation={0}
    sx={{
      mt: 3,
      scrollMarginTop: 16,
      backgroundColor: 'transparent',
      '&::before': { display: 'none' }
    }}
  >
    <AccordionSummary
      expandIcon={<ExpandMoreIcon />}
      aria-controls={`${testId}-content`}
      sx={{
        flexDirection: 'row-reverse',
        gap: 1,
        px: 0,
        minHeight: 'auto',
        '& .MuiAccordionSummary-content': { my: 1.5 }
      }}
    >
      <Typography component="h4" variant="h6" sx={{ fontSize: 16, fontWeight: 700 }}>
        {title}
      </Typography>
    </AccordionSummary>
    <AccordionDetails sx={{ p: 0 }}>
      <TableContainer sx={{ overflowX: 'auto' }}>
          <Table size="small" sx={{ minWidth: 800, tableLayout: 'fixed' }}>
            <TableHead>
              <TableRow>
                {columns.map(col => (
                  <TableCell key={col.label}>{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>
        <Box
          sx={{
            [`& .${tablePaginationClasses.root}`]: { justifyContent: 'flex-start !important' },
            [`& .${tablePaginationClasses.spacer}`]: { display: 'none !important' },
            [`& .${tablePaginationClasses.toolbar}`]: { justifyContent: 'flex-start', pl: 0 }
          }}
        >
          <Pagination
            count={list.total}
            rowsPerPage={list.perPage}
            page={list.page}
            onChangePage={onChangePage}
            onChangeRowsPerPage={onChangePerPage}
            // Empty options hides the "Rows per page" label + selector.
            rowsPerPageOptions={[]}
          />
        </Box>
    </AccordionDetails>
  </Accordion>
);

const containerColumns: Column<Container>[] = [
  { label: 'Names', render: r => (r.name ? r.name.replace(/^\//, '') : '—') },
  { label: 'Image', render: r => r.image ?? '—' },
  { label: 'Command', render: r => r.command ?? '—' },
  { label: 'Created', render: r => formatRelativeTime(r.created_at) },
  { label: 'Status', render: formatContainerStatus },
  { label: 'Ports', render: r => formatPorts(r.ports) || '—' },
  { label: 'Engine', render: r => r.engine ?? '—' },
  { label: 'Container ID', render: r => (r.id ? r.id.slice(0, 12) : '—') }
];

const imageColumns: Column<ContainerImage>[] = [
  { label: 'Repository', render: r => r.repository ?? '—' },
  { label: 'Tag', render: r => r.tag ?? '—' },
  { label: 'Image ID', render: r => (r.id ? r.id.replace(/^sha256:/, '').slice(0, 12) : '—') },
  { label: 'Created', render: r => formatRelativeTime(r.created_at) },
  { label: 'Size', render: r => formatBytes(r.size_bytes) },
  { label: 'Engine', render: r => r.engine ?? '—' }
];

const SummaryLine = ({ count, children }: { count: number; children: React.ReactNode }) => (
  <Box>
    <Typography component="span" sx={{ color: 'text.primary', fontWeight: 500 }}>
      {count}
    </Typography>{' '}
    {children}
  </Box>
);

interface SummaryHeaderProps extends ContainersTabProps {
  onViewImages: () => void;
}

const SummaryHeader = ({ hostkey, onViewImages }: SummaryHeaderProps) => {
  const summary = useAppSelector(state => selectContainersSummary(state, hostkey));
  const loading = useAppSelector(state => selectContainersSummaryLoading(state, hostkey));

  if (loading && !summary) {
    return <CircularProgress size={20} />;
  }
  if (!summary) return null;

  return (
    <Box
      sx={{
        display: 'flex',
        alignItems: 'flex-start',
        flexWrap: 'wrap',
        gap: 26
      }}
    >
      <Box>
        <Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}>
          <Typography component="h3" sx={{ fontSize: 23, fontWeight: 700 }}>
            Running containers
          </Typography>
          <Tooltip
            title="Container info is collected on hosts during CFEngine agent runs, using commands like docker ps, docker images, etc."
            arrow
          >
            <InfoIcon fontSize="small" sx={{ color: 'text.secondary', cursor: 'help' }} />
          </Tooltip>
        </Box>
        <Typography component="div" sx={{ fontSize: 44, fontWeight: 700, mt: 2 }}>
          {summary.containers.running}
        </Typography>
      </Box>
      <Box sx={{ color: 'text.secondary', fontSize: 14, fontWeight: 500 }}>
        {summary.last_collected_at && (
          <Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5, mb: 1.5, fontStyle: 'italic' }}>
            <AutorenewIcon fontSize="small" />
            Last updated: {formatRelativeTime(summary.last_collected_at)}
          </Box>
        )}
        <SummaryLine count={summary.images.named}>
          {pluralize('named image', summary.images.named)}{' '}
          <Link component="button" underline="hover" onClick={onViewImages}>
            View
          </Link>
        </SummaryLine>
        <SummaryLine count={summary.images.total}>
          {pluralize('image', summary.images.total)} in total (
          {formatBytes(summary.images.total_size_bytes)})
        </SummaryLine>
        <SummaryLine count={summary.volumes.count}>
          {pluralize('local volume', summary.volumes.count)} (
          {formatBytes(summary.volumes.total_size_bytes)})
        </SummaryLine>
        <Box>
          Container engines:{' '}
          <Typography component="span" sx={{ color: 'text.primary' }}>
            {summary.engines.length > 0 ? summary.engines.map(capitalize).join(', ') : '—'}
          </Typography>
        </Box>
      </Box>
    </Box>
  );
};

export const ContainersTab = ({ hostkey }: ContainersTabProps) => {
  const dispatch = useAppDispatch();
  const containers = useAppSelector(state => selectContainersList(state, hostkey));
  const images = useAppSelector(state => selectImagesList(state, hostkey));
  const imagesRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (hostkey) {
      dispatch(fetchContainersSummary(hostkey));
    }
  }, [dispatch, hostkey]);

  useEffect(() => {
    if (hostkey) {
      dispatch(fetchContainers({ hostkey, page: containers.page, perPage: containers.perPage }));
    }
  }, [dispatch, hostkey, containers.page, containers.perPage]);

  useEffect(() => {
    if (hostkey) {
      dispatch(fetchContainerImages({ hostkey, page: images.page, perPage: images.perPage }));
    }
  }, [dispatch, hostkey, images.page, images.perPage]);

  return (
    <Box>
      <SummaryHeader
        hostkey={hostkey}
        onViewImages={() => imagesRef.current?.scrollIntoView({ behavior: 'smooth', block: 'start' })}
      />
      <Divider sx={{ my: '20px' }} />
      <PaginatedSection
        title="Containers"
        testId="containers"
        columns={containerColumns}
        list={containers}
        emptyText="No containers reported for this host."
        onChangePage={page => dispatch(setListPage({ hostkey, list: 'containers', page }))}
        onChangePerPage={perPage =>
          dispatch(setListPerPage({ hostkey, list: 'containers', perPage }))
        }
      />
      <Divider sx={{ my: '20px' }} />
      <PaginatedSection
        title="Images"
        testId="images"
        columns={imageColumns}
        list={images}
        emptyText="No images reported for this host."
        sectionRef={imagesRef}
        onChangePage={page => dispatch(setListPage({ hostkey, list: 'images', page }))}
        onChangePerPage={perPage => dispatch(setListPerPage({ hostkey, list: 'images', perPage }))}
      />
    </Box>
  );
};
