/**
 * 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).
 *
 * Critical sections wrapped with SectionErrorBoundary for resilience.
 */

import { useState, useMemo, useEffect } from "react";
import {
  Button,
  MessageBar,
  MessageBarActions,
  MessageBarBody,
  Spinner,
  Text,
  makeStyles,
  mergeClasses,
  tokens,
} from "@fluentui/react-components";
import {
  ArrowCounterclockwiseRegular,
  DismissRegular,
} from "@fluentui/react-icons";
import { DashboardFilterBar } from "../components/DashboardFilterBar";
import { SectionErrorBoundary } from "../components/SectionErrorBoundary";
import { WorldMap } from "../components/WorldMap";
import { useMapData } from "../hooks/useMapData";
import { useMapColorThresholds } from "../hooks/useMapColorThresholds";
import { MapBansTable } from "./map/MapBansTable";
import { getDataSource } from "../utils/queryUtils";
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}`,
  },
  stickyHeaderCell: {
    position: "sticky",
    top: 0,
    zIndex: 1,
    backgroundColor: tokens.colorNeutralBackground1,
    boxShadow: `0 1px 0 ${tokens.colorNeutralStroke2}`,
  },
  filterBar: {
    display: "flex",
    alignItems: "center",
    justifyContent: "space-between",
    gap: tokens.spacingHorizontalM,
    padding: tokens.spacingVerticalS,
    borderRadius: tokens.borderRadiusMedium,
    backgroundColor: tokens.colorNeutralBackground2,
  },
  headerActions: {
    display: "flex",
    alignItems: "center",
    gap: tokens.spacingHorizontalM,
    flexWrap: "wrap",
  },
  initialLoad: {
    display: "flex",
    justifyContent: "center",
    padding: tokens.spacingVerticalXL,
  },
  summaryRow: {
    display: "flex",
    alignItems: "center",
    gap: tokens.spacingHorizontalS,
  },
  summaryText: {
    color: tokens.colorNeutralForeground3,
  },
  tableWrapperLoading: {
    opacity: 0.5,
    transition: "opacity 150ms",
  },
  pagination: {
    display: "flex",
    alignItems: "center",
    justifyContent: "space-between",
    gap: tokens.spacingHorizontalS,
    padding: `${tokens.spacingVerticalS} ${tokens.spacingHorizontalM}`,
    borderTop: `1px solid ${tokens.colorNeutralStroke2}`,
    backgroundColor: tokens.colorNeutralBackground2,
    position: "sticky",
    bottom: 0,
    zIndex: 1,
  },
});

// ---------------------------------------------------------------------------
// 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 source = getDataSource(range);

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

  // True after the first successful data load — keeps the map mounted
  // during subsequent re-fetches so country selection gives instant feedback.
  const [hasLoadedOnce, setHasLoadedOnce] = useState(false);
  useEffect(() => {
    if (!loading && !error) setHasLoadedOnce(true);
  }, [loading, error]);

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

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

  const [dismissedThresholdWarning, setDismissedThresholdWarning] = useState(false);

  useEffect(() => {
    setPage(1);
  }, [range, originFilter, selectedCountry, 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));

  // Clamp page to totalPages when data shrinks below current page offset
  useEffect(() => {
    if (page > totalPages) {
      setPage(totalPages);
    }
  }, [totalPages, page]);

  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} data-testid="map-page">
      {/* ---------------------------------------------------------------- */}
      {/* Header row                                                        */}
      {/* ---------------------------------------------------------------- */}
      <div className={styles.header}>
        <Text as="h1" size={700} weight="semibold">
          World Map
        </Text>

        <div className={styles.headerActions}>
          <DashboardFilterBar
            timeRange={range}
            onTimeRangeChange={(value) => {
              setRange(value);
              setSelectedCountry(null);
            }}
            originFilter={originFilter}
            onOriginFilterChange={(value) => {
              setOriginFilter(value);
              setSelectedCountry(null);
            }}
          />
          <Button
            icon={<ArrowCounterclockwiseRegular />}
            onClick={(): void => {
              refresh();
            }}
            disabled={loading}
            title="Refresh"
          />
        </div>
      </div>

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

      {mapThresholdError && !dismissedThresholdWarning && (
        <MessageBar intent="warning">
          <MessageBarBody>
            Map color thresholds could not be loaded. Using default thresholds.
          </MessageBarBody>
          <MessageBarActions>
            <Button
              appearance="transparent"
              size="small"
              icon={<DismissRegular />}
              onClick={(): void => {
                setDismissedThresholdWarning(true);
              }}
            />
          </MessageBarActions>
        </MessageBar>
      )}

      {/* Initial load spinner — only shown before the first data arrives. */}
      {loading && !error && !hasLoadedOnce && (
        <div className={styles.initialLoad}>
          <Spinner label="Loading map data…" />
        </div>
      )}

      {/* ---------------------------------------------------------------- */}
      {/* World map                                                         */}
      {/* Keep the map mounted after first load so clicking a country gives */}
      {/* immediate visual feedback before the filtered data arrives.       */}
      {/* ---------------------------------------------------------------- */}
      {!error && hasLoadedOnce && (
        <SectionErrorBoundary sectionName="World Map">
          <WorldMap
            countries={countries}
            countryNames={countryNames}
            selectedCountry={selectedCountry}
            onSelectCountry={setSelectedCountry}
            thresholdLow={thresholdLow}
            thresholdMedium={thresholdMedium}
            thresholdHigh={thresholdHigh}
          />
        </SectionErrorBoundary>
      )}

      {/* ---------------------------------------------------------------- */}
      {/* 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                                                      */}
      {/* ---------------------------------------------------------------- */}
      {!error && hasLoadedOnce && (
        <div className={styles.summaryRow}>
          <Text size={300} className={styles.summaryText}>
            {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>
          {loading && <Spinner size="tiny" />}
        </div>
      )}

      {/* ---------------------------------------------------------------- */}
      {/* Companion bans table                                              */}
      {/* ---------------------------------------------------------------- */}
      {!error && hasLoadedOnce && (
        <SectionErrorBoundary sectionName="Map Ban Table">
          <div className={mergeClasses(styles.tableWrapper, loading && styles.tableWrapperLoading)}>
            <MapBansTable
              pageBans={pageBans}
              visibleCount={visibleBans.length}
              page={page}
              pageSize={pageSize}
              totalPages={totalPages}
              hasPrev={hasPrev}
              hasNext={hasNext}
              onPageChange={setPage}
              onPageSizeChange={setPageSize}
            />
          </div>
        </SectionErrorBoundary>
      )}
    </div>
  );
}
