/**
 * `BannedIpsSection` component.
 *
 * Displays a paginated table of IPs currently banned in a specific fail2ban
 * jail.  Supports server-side search filtering (debounced), page navigation,
 * page-size selection, and per-row unban actions.
 *
 * Only the current page is geo-enriched by the backend, so the component
 * remains fast even when a jail contains thousands of banned IPs.
 */

import {
  Badge,
  Button,
  DataGrid,
  DataGridBody,
  DataGridCell,
  DataGridHeader,
  DataGridHeaderCell,
  DataGridRow,
  Dropdown,
  Field,
  Input,
  MessageBar,
  MessageBarBody,
  Option,
  Spinner,
  Text,
  Tooltip,
  makeStyles,
  tokens,
  type TableColumnDefinition,
  createTableColumn,
} from "@fluentui/react-components";
import { formatTimestamp } from "../../utils/formatDate";
import {
  ArrowClockwiseRegular,
  ChevronLeftRegular,
  ChevronRightRegular,
  DismissRegular,
  SearchRegular,
} from "@fluentui/react-icons";
import type { ActiveBan } from "../../types/jail";

// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------

/** Available page-size options. */
const PAGE_SIZE_OPTIONS = [10, 25, 50, 100] as const;

// ---------------------------------------------------------------------------
// Styles
// ---------------------------------------------------------------------------

const useStyles = makeStyles({
  root: {
    display: "flex",
    flexDirection: "column",
    gap: tokens.spacingVerticalS,
    backgroundColor: tokens.colorNeutralBackground1,
    borderRadius: tokens.borderRadiusMedium,
    borderTopWidth: "1px",
    borderTopStyle: "solid",
    borderTopColor: tokens.colorNeutralStroke2,
    borderRightWidth: "1px",
    borderRightStyle: "solid",
    borderRightColor: tokens.colorNeutralStroke2,
    borderBottomWidth: "1px",
    borderBottomStyle: "solid",
    borderBottomColor: tokens.colorNeutralStroke2,
    borderLeftWidth: "1px",
    borderLeftStyle: "solid",
    borderLeftColor: tokens.colorNeutralStroke2,
    padding: tokens.spacingVerticalM,
  },
  header: {
    display: "flex",
    alignItems: "center",
    justifyContent: "space-between",
    gap: tokens.spacingHorizontalM,
    paddingBottom: tokens.spacingVerticalS,
    borderBottomWidth: "1px",
    borderBottomStyle: "solid",
    borderBottomColor: tokens.colorNeutralStroke2,
  },
  headerLeft: {
    display: "flex",
    alignItems: "center",
    gap: tokens.spacingHorizontalM,
  },
  toolbar: {
    display: "flex",
    alignItems: "flex-end",
    gap: tokens.spacingHorizontalS,
    flexWrap: "wrap",
  },
  searchField: {
    minWidth: "200px",
    flexGrow: 1,
  },
  tableWrapper: {
    overflowX: "auto",
  },
  centred: {
    display: "flex",
    justifyContent: "center",
    alignItems: "center",
    padding: tokens.spacingVerticalXXL,
  },
  pagination: {
    display: "flex",
    alignItems: "center",
    justifyContent: "flex-end",
    gap: tokens.spacingHorizontalS,
    paddingTop: tokens.spacingVerticalS,
    flexWrap: "wrap",
  },
  pageSizeWrapper: {
    display: "flex",
    alignItems: "center",
    gap: tokens.spacingHorizontalXS,
  },
  mono: {
    fontFamily: "Consolas, 'Courier New', monospace",
    fontSize: tokens.fontSizeBase200,
  },
});

// ---------------------------------------------------------------------------
// Column definitions
// ---------------------------------------------------------------------------

/** A row item augmented with an `onUnban` callback for the row action. */
interface BanRow {
  ban: ActiveBan;
  onUnban: (ip: string) => void;
}

const columns: TableColumnDefinition<BanRow>[] = [
  createTableColumn<BanRow>({
    columnId: "ip",
    renderHeaderCell: () => "IP Address",
    renderCell: ({ ban }) => (
      <Text
        style={{
          fontFamily: "Consolas, 'Courier New', monospace",
          fontSize: tokens.fontSizeBase200,
        }}
      >
        {ban.ip}
      </Text>
    ),
  }),
  createTableColumn<BanRow>({
    columnId: "country",
    renderHeaderCell: () => "Country",
    renderCell: ({ ban }) =>
      ban.country ? (
        <Text size={200}>{ban.country}</Text>
      ) : (
        <Text size={200} style={{ color: tokens.colorNeutralForeground4 }}>
          —
        </Text>
      ),
  }),
  createTableColumn<BanRow>({
    columnId: "banned_at",
    renderHeaderCell: () => "Banned At",
    renderCell: ({ ban }) => (
      <Text size={200}>{ban.banned_at ? formatTimestamp(ban.banned_at) : "—"}</Text>
    ),
  }),
  createTableColumn<BanRow>({
    columnId: "expires_at",
    renderHeaderCell: () => "Expires At",
    renderCell: ({ ban }) => (
      <Text size={200}>{ban.expires_at ? formatTimestamp(ban.expires_at) : "—"}</Text>
    ),
  }),
  createTableColumn<BanRow>({
    columnId: "actions",
    renderHeaderCell: () => "",
    renderCell: ({ ban, onUnban }) => (
      <Tooltip content={`Unban ${ban.ip}`} relationship="label">
        <Button
          size="small"
          appearance="subtle"
          icon={<DismissRegular />}
          onClick={() => {
            onUnban(ban.ip);
          }}
          aria-label={`Unban ${ban.ip}`}
        />
      </Tooltip>
    ),
  }),
];

// ---------------------------------------------------------------------------
// Props
// ---------------------------------------------------------------------------

/** Props for {@link BannedIpsSection}. */
export interface BannedIpsSectionProps {
  items: ActiveBan[];
  total: number;
  page: number;
  pageSize: number;
  search: string;
  loading: boolean;
  error: string | null;
  opError: string | null;
  onSearch: (term: string) => void;
  onPageChange: (page: number) => void;
  onPageSizeChange: (size: number) => void;
  onRefresh: () => void;
  onUnban: (ip: string) => void;
}

// ---------------------------------------------------------------------------
// Component
// ---------------------------------------------------------------------------

/**
 * Paginated section showing currently banned IPs for a single jail.
 *
 * @param props - {@link BannedIpsSectionProps}
 */
export function BannedIpsSection({
  items,
  total,
  page,
  pageSize,
  search,
  loading,
  error,
  opError,
  onSearch,
  onPageChange,
  onPageSizeChange,
  onRefresh,
  onUnban,
}: BannedIpsSectionProps): React.JSX.Element {
  const styles = useStyles();

  const rows: BanRow[] = items.map((ban) => ({
    ban,
    onUnban,
  }));

  const totalPages = pageSize > 0 ? Math.ceil(total / pageSize) : 1;

  return (
    <div className={styles.root}>
      {/* Section header */}
      <div className={styles.header}>
        <div className={styles.headerLeft}>
          <Text as="h2" size={500} weight="semibold">
            Currently Banned IPs
          </Text>
          <Badge appearance="tint">{String(total)}</Badge>
        </div>
        <Button
          size="small"
          appearance="subtle"
          icon={<ArrowClockwiseRegular />}
          onClick={onRefresh}
          aria-label="Refresh banned IPs"
        />
      </div>

      {/* Toolbar */}
      <div className={styles.toolbar}>
        <div className={styles.searchField}>
          <Field label="Search by IP">
            <Input
              aria-label="Search by IP"
              contentBefore={<SearchRegular />}
              placeholder="e.g. 192.168"
              value={search}
              onChange={(_, d) => {
                onSearch(d.value);
              }}
            />
          </Field>
        </div>
      </div>

      {/* Error bars */}
      {error && (
        <MessageBar intent="error">
          <MessageBarBody>{error}</MessageBarBody>
        </MessageBar>
      )}
      {opError && (
        <MessageBar intent="error">
          <MessageBarBody>{opError}</MessageBarBody>
        </MessageBar>
      )}

      {/* Table */}
      {loading ? (
        <div className={styles.centred}>
          <Spinner label="Loading banned IPs…" />
        </div>
      ) : items.length === 0 ? (
        <div className={styles.centred}>
          <Text size={200} style={{ color: tokens.colorNeutralForeground3 }}>
            No IPs currently banned in this jail.
          </Text>
        </div>
      ) : (
        <div className={styles.tableWrapper}>
          <DataGrid
            items={rows}
            columns={columns}
            getRowId={(row: BanRow) => row.ban.ip}
            focusMode="composite"
          >
            <DataGridHeader>
              <DataGridRow>
                {({ renderHeaderCell }) => (
                  <DataGridHeaderCell>{renderHeaderCell()}</DataGridHeaderCell>
                )}
              </DataGridRow>
            </DataGridHeader>
            <DataGridBody<BanRow>>
              {({ item, rowId }) => (
                <DataGridRow<BanRow> key={rowId}>
                  {({ renderCell }) => (
                    <DataGridCell>{renderCell(item)}</DataGridCell>
                  )}
                </DataGridRow>
              )}
            </DataGridBody>
          </DataGrid>
        </div>
      )}

      {/* Pagination */}
      {total > 0 && (
        <div className={styles.pagination}>
          <div className={styles.pageSizeWrapper}>
            <Text size={200}>Rows per page:</Text>
            <Dropdown
              aria-label="Rows per page"
              value={String(pageSize)}
              selectedOptions={[String(pageSize)]}
              onOptionSelect={(_, d) => {
                const newSize = Number(d.optionValue);
                if (!Number.isNaN(newSize)) {
                  onPageSizeChange(newSize);
                  onPageChange(1);
                }
              }}
              style={{ minWidth: "80px" }}
            >
              {PAGE_SIZE_OPTIONS.map((n) => (
                <Option key={n} value={String(n)}>
                  {String(n)}
                </Option>
              ))}
            </Dropdown>
          </div>

          <Text size={200}>
            {String((page - 1) * pageSize + 1)}–
            {String(Math.min(page * pageSize, total))} of {String(total)}
          </Text>

          <Button
            size="small"
            appearance="subtle"
            icon={<ChevronLeftRegular />}
            disabled={page <= 1}
            onClick={() => {
              onPageChange(Math.max(1, page - 1));
            }}
            aria-label="Previous page"
          />
          <Button
            size="small"
            appearance="subtle"
            icon={<ChevronRightRegular />}
            disabled={page >= totalPages}
            onClick={() => {
              onPageChange(page + 1);
            }}
            aria-label="Next page"
          />
        </div>
      )}
    </div>
  );
}
