/**
 * MapPage — geographical overview of fail2ban bans.
 *
 * Shows a clickable SVG world map coloured by ban density, a time-range
 * selector, and a companion table filtered by the selected country (or all
 * bans when no country is selected).
 */

import { useState, useMemo, useEffect } from "react";
import {
  Badge,
  Button,
  MessageBar,
  MessageBarBody,
  Spinner,
  Table,
  TableBody,
  TableCell,
  TableCellLayout,
  TableHeader,
  TableHeaderCell,
  TableRow,
  Text,
  Tooltip,
  makeStyles,
  tokens,
} from "@fluentui/react-components";
import {
  ArrowCounterclockwiseRegular,
  ChevronLeftRegular,
  ChevronRightRegular,
  DismissRegular,
} from "@fluentui/react-icons";
import { DashboardFilterBar } from "../components/DashboardFilterBar";
import { WorldMap } from "../components/WorldMap";
import { useMapData } from "../hooks/useMapData";
import { useMapColorThresholds } from "../hooks/useMapColorThresholds";
import type { TimeRange } from "../types/map";
import type { BanOriginFilter } from "../types/ban";

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

const useStyles = makeStyles({
  root: {
    display: "flex",
    flexDirection: "column",
    gap: tokens.spacingVerticalL,
    padding: tokens.spacingVerticalXXL,
    paddingLeft: tokens.spacingHorizontalXXL,
    paddingRight: tokens.spacingHorizontalXXL,
  },
  header: {
    display: "flex",
    alignItems: "center",
    justifyContent: "space-between",
    flexWrap: "wrap",
    gap: tokens.spacingHorizontalM,
  },
  tableWrapper: {
    overflow: "auto",
    maxHeight: "420px",
    borderRadius: tokens.borderRadiusMedium,
    border: `1px solid ${tokens.colorNeutralStroke1}`,
  },
  filterBar: {
    display: "flex",
    alignItems: "center",
    justifyContent: "space-between",
    gap: tokens.spacingHorizontalM,
    padding: tokens.spacingVerticalS,
    borderRadius: tokens.borderRadiusMedium,
    backgroundColor: tokens.colorNeutralBackground2,
  },
  pagination: {
    display: "flex",
    alignItems: "center",
    justifyContent: "space-between",
    gap: tokens.spacingHorizontalS,
    padding: `${tokens.spacingVerticalS} ${tokens.spacingHorizontalM}`,
    borderTop: `1px solid ${tokens.colorNeutralStroke2}`,
    backgroundColor: tokens.colorNeutralBackground2,
  },
});

// ---------------------------------------------------------------------------
// MapPage
// ---------------------------------------------------------------------------

export function MapPage(): React.JSX.Element {
  const styles = useStyles();
  const [range, setRange] = useState<TimeRange>("24h");
  const [originFilter, setOriginFilter] = useState<BanOriginFilter>("all");
  const [selectedCountry, setSelectedCountry] = useState<string | null>(null);
  const [page, setPage] = useState<number>(1);
  const [pageSize, setPageSize] = useState<number>(100);

  const PAGE_SIZE_OPTIONS = [25, 50, 100] as const;

  const source = range === "24h" ? "fail2ban" : "archive";

  const { countries, countryNames, bans, total, loading, error, refresh } =
    useMapData(range, originFilter, source);

  const {
    thresholds: mapThresholds,
    error: mapThresholdError,
  } = useMapColorThresholds();

  const thresholdLow = mapThresholds?.threshold_low ?? 20;
  const thresholdMedium = mapThresholds?.threshold_medium ?? 50;
  const thresholdHigh = mapThresholds?.threshold_high ?? 100;

  useEffect(() => {
    if (mapThresholdError) {
      // Silently fall back to defaults if fetch fails
      console.warn("Failed to load map color thresholds:", mapThresholdError);
    }
  }, [mapThresholdError]);

  useEffect(() => {
    setPage(1);
  }, [range, originFilter, selectedCountry, bans, pageSize]);

  /** Bans visible in the companion table (filtered by selected country). */
  const visibleBans = useMemo(() => {
    if (!selectedCountry) return bans;
    return bans.filter((b) => b.country_code === selectedCountry);
  }, [bans, selectedCountry]);

  const selectedCountryName = selectedCountry
    ? (countryNames[selectedCountry] ?? selectedCountry)
    : null;

  const totalPages = Math.max(1, Math.ceil(visibleBans.length / pageSize));
  const hasPrev = page > 1;
  const hasNext = page < totalPages;

  const pageBans = useMemo(() => {
    const start = (page - 1) * pageSize;
    return visibleBans.slice(start, start + pageSize);
  }, [visibleBans, page, pageSize]);

  return (
    <div className={styles.root}>
      {/* ---------------------------------------------------------------- */}
      {/* Header row                                                        */}
      {/* ---------------------------------------------------------------- */}
      <div className={styles.header}>
        <Text as="h1" size={700} weight="semibold">
          World Map
        </Text>

        <div style={{ display: "flex", alignItems: "center", gap: tokens.spacingHorizontalM, flexWrap: "wrap" }}>
          <DashboardFilterBar
            timeRange={range}
            onTimeRangeChange={(value) => {
              setRange(value);
              setSelectedCountry(null);
            }}
            originFilter={originFilter}
            onOriginFilterChange={(value) => {
              setOriginFilter(value);
              setSelectedCountry(null);
            }}
          />
          <Badge appearance="filled" color={source === "archive" ? "brand" : "success"}>
            {source === "archive" ? "Archive (BanGUI DB)" : "Live (fail2ban DB)"}
          </Badge>
          <Button
            icon={<ArrowCounterclockwiseRegular />}
            onClick={(): void => {
              refresh();
            }}
            disabled={loading}
            title="Refresh"
          />
        </div>
      </div>

      {/* ---------------------------------------------------------------- */}
      {/* Error / loading states                                            */}
      {/* ---------------------------------------------------------------- */}
      {error && (
        <MessageBar intent="error">
          <MessageBarBody>{error}</MessageBarBody>
        </MessageBar>
      )}

      {loading && !error && (
        <div style={{ display: "flex", justifyContent: "center", padding: tokens.spacingVerticalXL }}>
          <Spinner label="Loading map data…" />
        </div>
      )}

      {/* ---------------------------------------------------------------- */}
      {/* World map                                                         */}
      {/* ---------------------------------------------------------------- */}
      {!loading && !error && (
        <WorldMap
          countries={countries}
          countryNames={countryNames}
          selectedCountry={selectedCountry}
          onSelectCountry={setSelectedCountry}
          thresholdLow={thresholdLow}
          thresholdMedium={thresholdMedium}
          thresholdHigh={thresholdHigh}
        />
      )}

      {/* ---------------------------------------------------------------- */}
      {/* Active country filter info bar                                    */}
      {/* ---------------------------------------------------------------- */}
      {selectedCountry && (
        <div className={styles.filterBar}>
          <Text size={300}>
            Showing <strong>{String(visibleBans.length)}</strong> bans from{" "}
            <strong>{selectedCountryName ?? selectedCountry}</strong>
            {" "}({String(countries[selectedCountry] ?? 0)} total in window)
          </Text>
          <Button
            appearance="subtle"
            size="small"
            icon={<DismissRegular />}
            onClick={(): void => {
              setSelectedCountry(null);
            }}
          >
            Clear filter
          </Button>
        </div>
      )}

      {/* ---------------------------------------------------------------- */}
      {/* Summary line                                                      */}
      {/* ---------------------------------------------------------------- */}
      {!loading && !error && (
        <Text size={300} style={{ color: tokens.colorNeutralForeground3 }}>
          {String(total)} total ban{total !== 1 ? "s" : ""} in the selected period
          {" · "}
          {String(Object.keys(countries).length)} countr{Object.keys(countries).length !== 1 ? "ies" : "y"} affected
        </Text>
      )}

      {/* ---------------------------------------------------------------- */}
      {/* Companion bans table                                              */}
      {/* ---------------------------------------------------------------- */}
      {!loading && !error && (
        <div className={styles.tableWrapper}>
          <Table size="small" aria-label="Bans list">
            <TableHeader>
              <TableRow>
                <TableHeaderCell>IP Address</TableHeaderCell>
                <TableHeaderCell>Jail</TableHeaderCell>
                <TableHeaderCell>Banned At</TableHeaderCell>
                <TableHeaderCell>Country</TableHeaderCell>
                <TableHeaderCell>Origin</TableHeaderCell>
                <TableHeaderCell>Times Banned</TableHeaderCell>
              </TableRow>
            </TableHeader>
            <TableBody>
              {visibleBans.length === 0 ? (
                <TableRow>
                  <TableCell colSpan={6}>
                    <TableCellLayout>
                      <Text size={200} style={{ color: tokens.colorNeutralForeground3 }}>
                        No bans found.
                      </Text>
                    </TableCellLayout>
                  </TableCell>
                </TableRow>
              ) : (
                pageBans.map((ban) => (
                  <TableRow key={`${ban.ip}-${ban.banned_at}`}>
                    <TableCell>
                      <TableCellLayout>{ban.ip}</TableCellLayout>
                    </TableCell>
                    <TableCell>
                      <TableCellLayout>{ban.jail}</TableCellLayout>
                    </TableCell>
                    <TableCell>
                      <TableCellLayout>
                        {new Date(ban.banned_at).toLocaleString()}
                      </TableCellLayout>
                    </TableCell>
                    <TableCell>
                      <TableCellLayout>
                        {ban.country_name ?? ban.country_code ? (
                          ban.country_name ?? ban.country_code
                        ) : (
                          <Tooltip
                            content="Country could not be resolved — will retry automatically."
                            relationship="description"
                          >
                            <Text style={{ color: tokens.colorNeutralForeground3 }}>
                              —
                            </Text>
                          </Tooltip>
                        )}
                      </TableCellLayout>
                    </TableCell>
                    <TableCell>
                      <TableCellLayout>
                        <Badge
                          appearance="tint"
                          color={ban.origin === "blocklist" ? "brand" : "informative"}
                        >
                          {ban.origin === "blocklist" ? "Blocklist" : "Selfblock"}
                        </Badge>
                      </TableCellLayout>
                    </TableCell>
                    <TableCell>
                      <TableCellLayout>{String(ban.ban_count)}</TableCellLayout>
                    </TableCell>
                  </TableRow>
                ))
              )}
            </TableBody>
          </Table>
          <div className={styles.pagination}>
            <div style={{ display: "flex", alignItems: "center", gap: tokens.spacingHorizontalS }}>
              <Text size={200} style={{ color: tokens.colorNeutralForeground3 }}>
                Showing {pageBans.length} of {visibleBans.length} filtered ban{visibleBans.length !== 1 ? "s" : ""}
                {" · "}Page {page} of {totalPages}
              </Text>

              <div style={{ display: "flex", alignItems: "center", gap: tokens.spacingHorizontalS }}>
                <Text size={200} style={{ color: tokens.colorNeutralForeground3 }}>
                  Page size
                </Text>
                <select
                  aria-label="Page size"
                  value={pageSize}
                  onChange={(event): void => {
                    setPageSize(Number(event.target.value));
                  }}
                >
                  {PAGE_SIZE_OPTIONS.map((option) => (
                    <option key={option} value={option}>
                      {option}
                    </option>
                  ))}
                </select>
              </div>
            </div>
            <div style={{ display: "flex", gap: tokens.spacingHorizontalXS }}>
              <Button
                icon={<ChevronLeftRegular />}
                appearance="subtle"
                disabled={!hasPrev}
                onClick={(): void => {
                  setPage(page - 1);
                }}
                aria-label="Previous page"
              />
              <Button
                icon={<ChevronRightRegular />}
                appearance="subtle"
                disabled={!hasNext}
                onClick={(): void => {
                  setPage(page + 1);
                }}
                aria-label="Next page"
              />
            </div>
          </div>
        </div>
      )}
    </div>
  );
}
