"use client";

import { type Key, useCallback, useEffect, useMemo, useRef, useState } from "react";
import {
  Alert,
  Button,
  Card,
  Descriptions,
  Divider,
  Drawer,
  Form,
  Input,
  Modal,
  Progress,
  Radio,
  Select,
  Space,
  Statistic,
  Table,
  Tabs,
  Tag,
  Tooltip,
  Typography,
  message,
} from "antd";
import type { ColumnsType, TablePaginationConfig } from "antd/es/table";
import {
  InfoCircleOutlined,
  PlusOutlined,
  ReloadOutlined,
  StopOutlined,
  SyncOutlined,
} from "@ant-design/icons";
import dayjs from "dayjs";
import { useRouter } from "next/navigation";

import { PageStateNotice } from "@/components/common/page-state-notice";
import {
  cancelScanBatch,
  cancelScanCampaign,
  createScanBatch,
  createScanCampaign,
  createScanClientRequestId,
  fetchScanBatches,
  fetchScanCampaignBatches,
  fetchScanCampaigns,
  fetchScanPortExposures,
  fetchScanStats,
  syncScanBatch,
  syncScanCampaign,
} from "@/lib/api/scans";
import { useAuth } from "@/lib/hooks/use-auth";
import type {
  CreateScanBatchRequest,
  ScanBatch,
  ScanBatchStatus,
  ScanCampaign,
  ScanObservationResult,
  ScanPortExposure,
  ScanRiskLevel,
  ScanStats,
} from "@/types/scans";

const { Title, Paragraph, Text } = Typography;

type ExposureFilterKey =
  | "all_exposed"
  | "high_risk"
  | "admin_ports"
  | "databases"
  | "newly_exposed"
  | "production"
  | "no_owner"
  | "needs_rescan";

type CreateScanFormValues = {
  source: CreateScanBatchRequest["source"];
  target_scope: NonNullable<CreateScanBatchRequest["target_scope"]>;
  target_ids?: string;
  note?: string;
};

type LoadOptions = {
  silent?: boolean;
};

type ActiveScanWorkRow = {
  key: string;
  kind: "campaign" | "batch";
  record: ScanCampaign | ScanBatch;
};

const CREATE_PERMISSION = "scan:create_batches";
const MANAGE_PERMISSION = "scan:manage_batches";
const INCONCLUSIVE_RESULT_COPY =
  "Inconclusive scan result. CAASM inventory is unchanged; rescan before treating it as closed or safe.";
const ID_LINK_STYLE = {
  display: "block",
  height: "auto",
  maxWidth: "100%",
  overflow: "hidden",
  padding: 0,
  textAlign: "left",
  textOverflow: "ellipsis",
  whiteSpace: "nowrap",
} as const;
const ACTIVE_WORK_ID_BUTTON_STYLE = {
  ...ID_LINK_STYLE,
  minWidth: 0,
  width: "100%",
} as const;
const ACTIVE_WORK_ID_TEXT_STYLE = {
  display: "block",
  maxWidth: "100%",
  overflow: "hidden",
  textOverflow: "ellipsis",
  whiteSpace: "nowrap",
} as const;
const TABLE_META_TEXT_STYLE = {
  display: "block",
  fontSize: 12,
  maxWidth: "100%",
} as const;

const riskMeta: Record<string, { label: string; color: string; rank: number }> = {
  critical: { label: "Critical", color: "red", rank: 5 },
  high: { label: "High", color: "volcano", rank: 4 },
  medium: { label: "Medium", color: "gold", rank: 3 },
  low: { label: "Low", color: "blue", rank: 2 },
  unknown: { label: "Unknown", color: "default", rank: 1 },
};

const resultMeta: Record<string, { label: string; color: string; description: string }> = {
  confirmed_open: {
    label: "Confirmed open",
    color: "red",
    description: "Scanner reached this port from its public egress point.",
  },
  confirmed_closed: {
    label: "Confirmed closed",
    color: "green",
    description: "Scanner reached the host and observed the port as closed.",
  },
  unknown_or_filtered: {
    label: "Needs rescan",
    color: "gold",
    description: "Inconclusive scanner result. This does not overwrite CAASM port inventory facts.",
  },
  skipped: {
    label: "Skipped",
    color: "default",
    description: "The scanner or platform skipped this target.",
  },
};

const statusColors: Record<string, string> = {
  draft: "default",
  submitting: "processing",
  queued: "blue",
  running: "geekblue",
  completed: "green",
  failed: "red",
  canceled: "default",
  cancel_requested: "orange",
  sync_failed: "volcano",
};

const ACTIVE_SCAN_STATUSES = new Set(["submitting", "queued", "running", "sync_failed", "cancel_requested"]);
const CANCELABLE_SCAN_STATUSES = new Set(["submitting", "queued", "running", "sync_failed"]);
const POLLING_INTERVAL_MS = 5000;
const BACKGROUND_SCAN_WORK_REFRESH_MS = 15000;
const STALE_ACTIVE_MINUTES = 5;
const STALE_SYNC_MINUTES = 10;

const statusFilterOptions = [
  "submitting",
  "queued",
  "running",
  "completed",
  "failed",
  "canceled",
  "cancel_requested",
  "sync_failed",
].map((value) => ({ label: value.replaceAll("_", " ").toUpperCase(), value }));

const quickFilters: { key: ExposureFilterKey; label: string; description: string }[] = [
  { key: "all_exposed", label: "All exposed", description: "Confirmed open scanner observations" },
  { key: "high_risk", label: "High risk", description: "Critical and high operational risk" },
  { key: "admin_ports", label: "Admin ports", description: "SSH, RDP, database admin, and management ports" },
  { key: "databases", label: "Databases", description: "Database-style ports exposed to the scanner" },
  { key: "newly_exposed", label: "Newly exposed", description: "Recently first observed public reachability" },
  { key: "production", label: "Production", description: "Production-tagged assets" },
  { key: "no_owner", label: "No owner", description: "Assets missing ownership context" },
  { key: "needs_rescan", label: "Needs rescan", description: INCONCLUSIVE_RESULT_COPY },
];

function formatDate(value?: string | null) {
  return value ? dayjs(value).format("YYYY-MM-DD HH:mm") : "-";
}

function formatNumber(value?: number | null) {
  return typeof value === "number" ? value.toLocaleString() : "0";
}

function getRiskRank(value?: ScanRiskLevel | null) {
  const key = (value || "unknown").toString().toLowerCase();
  return riskMeta[key]?.rank ?? 1;
}

function riskTag(value?: ScanRiskLevel | null) {
  const key = (value || "unknown").toString().toLowerCase();
  const meta = riskMeta[key] ?? { label: value || "Unknown", color: "default" };
  return <Tag color={meta.color}>{meta.label}</Tag>;
}

function resultTag(value?: ScanObservationResult | null) {
  const key = (value || "unknown").toString();
  const meta = resultMeta[key] ?? { label: key, color: "default", description: "Scanner observation result." };
  return (
    <Tooltip title={meta.description}>
      <Tag color={meta.color}>{meta.label}</Tag>
    </Tooltip>
  );
}

function sortExposures(records: ScanPortExposure[]) {
  return [...records].sort((a, b) => {
    const riskDelta = getRiskRank(b.derived_risk ?? b.risk) - getRiskRank(a.derived_risk ?? a.risk);
    if (riskDelta !== 0) return riskDelta;
    const bDate = dayjs(b.last_confirmed || b.last_observed || b.updated_at || b.created_at || 0).valueOf();
    const aDate = dayjs(a.last_confirmed || a.last_observed || a.updated_at || a.created_at || 0).valueOf();
    return bDate - aDate;
  });
}

function parseTargetIds(value: string) {
  return value
    .split(/[,\s]+/)
    .map((part) => Number(part.trim()))
    .filter((id) => Number.isInteger(id) && id > 0);
}

function getBatchStatusTag(status: ScanBatchStatus) {
  const label = status ? status.replaceAll("_", " ").toUpperCase() : "UNKNOWN";
  return <Tag color={statusColors[status] || "default"}>{label}</Tag>;
}

function isScanActive(record: ScanBatch | ScanCampaign) {
  return ACTIVE_SCAN_STATUSES.has(record.status) || (record.status === "canceled" && (record.running_jobs ?? 0) > 0);
}

function isScanCancelable(record: ScanBatch | ScanCampaign) {
  return CANCELABLE_SCAN_STATUSES.has(record.status);
}

function isScanStopping(record: ScanBatch | ScanCampaign) {
  return record.status === "cancel_requested" || (record.status === "canceled" && (record.running_jobs ?? 0) > 0);
}

function getScannerJobs(record: ScanBatch | ScanCampaign) {
  return {
    queued: record.queued_jobs ?? 0,
    running: record.running_jobs ?? 0,
    succeeded: record.succeeded_jobs ?? 0,
    failed: record.failed_jobs ?? 0,
    skipped: record.skipped_jobs ?? 0,
  };
}

function scannerJobsSummary(record: ScanBatch | ScanCampaign) {
  const jobs = getScannerJobs(record);
  return (
    <Space wrap size={4}>
      <Tag color={jobs.running > 0 ? "geekblue" : "default"}>Running {formatNumber(jobs.running)}</Tag>
      <Tag color={jobs.queued > 0 ? "blue" : "default"}>Queued {formatNumber(jobs.queued)}</Tag>
      <Tag color="green">Done {formatNumber(jobs.succeeded)}</Tag>
      <Tag color={jobs.failed > 0 ? "red" : "default"}>Failed {formatNumber(jobs.failed)}</Tag>
      <Tag>Skipped {formatNumber(jobs.skipped)}</Tag>
    </Space>
  );
}

function getScannerJobTotals(records: Array<ScanBatch | ScanCampaign>) {
  return records.reduce(
    (totals, record) => {
      const jobs = getScannerJobs(record);
      totals.queued += jobs.queued;
      totals.running += jobs.running;
      totals.succeeded += jobs.succeeded;
      totals.failed += jobs.failed;
      totals.skipped += jobs.skipped;
      return totals;
    },
    { queued: 0, running: 0, succeeded: 0, failed: 0, skipped: 0 },
  );
}

function isOlderThan(value: string | null | undefined, minutes: number) {
  return value ? dayjs().diff(dayjs(value), "minute") >= minutes : false;
}

function getLatestTimestamp(values: Array<string | null | undefined>) {
  return values
    .filter((value): value is string => Boolean(value))
    .sort((a, b) => dayjs(b).valueOf() - dayjs(a).valueOf())[0];
}

function getStaleActiveRecords(records: Array<ScanBatch | ScanCampaign>) {
  return records.filter(
    (record) =>
      isScanActive(record) &&
      (isOlderThan(record.updated_at, STALE_ACTIVE_MINUTES) ||
        isOlderThan(record.last_synced_at, STALE_SYNC_MINUTES)),
  );
}

function getMetadataText(record: { metadata?: Record<string, unknown> | null }, keys: string[]) {
  for (const key of keys) {
    const value = record.metadata?.[key];
    if (typeof value === "string" && value.trim()) {
      return value;
    }
    if (typeof value === "number") {
      return String(value);
    }
  }
  return undefined;
}

function getCampaignProgress(record: ScanCampaign) {
  if (typeof record.progress === "number") {
    return Math.max(0, Math.min(100, Math.round(record.progress)));
  }

  if (record.batch_count && record.batch_count > 0) {
    return Math.round(((record.completed_batches ?? 0) / record.batch_count) * 100);
  }

  return record.status === "completed" ? 100 : 0;
}

function endpointLabel(record: ScanPortExposure) {
  return `${record.public_ip}:${record.port}/${record.transport || "tcp"}`;
}

function getDetailValue(value?: string | number | null) {
  if (value === null || value === undefined || value === "") return "-";
  return value;
}

export default function PortExposurePage() {
  const router = useRouter();
  const { hasPermission } = useAuth();
  const canCreate = hasPermission(CREATE_PERMISSION);
  const canManage = hasPermission(MANAGE_PERMISSION);

  const [stats, setStats] = useState<ScanStats | null>(null);
  const [exposures, setExposures] = useState<ScanPortExposure[]>([]);
  const [exposureTotal, setExposureTotal] = useState(0);
  const [exposurePagination, setExposurePagination] = useState<TablePaginationConfig>({ current: 1, pageSize: 20 });
  const [exposureLoading, setExposureLoading] = useState(false);
  const [exposureError, setExposureError] = useState<string | null>(null);
  const [quickFilter, setQuickFilter] = useState<ExposureFilterKey>("all_exposed");
  const [search, setSearch] = useState("");

  const [batches, setBatches] = useState<ScanBatch[]>([]);
  const [activeBatches, setActiveBatches] = useState<ScanBatch[]>([]);
  const [batchTotal, setBatchTotal] = useState(0);
  const [batchPagination, setBatchPagination] = useState<TablePaginationConfig>({ current: 1, pageSize: 20 });
  const [batchLoading, setBatchLoading] = useState(false);
  const [batchError, setBatchError] = useState<string | null>(null);
  const [batchStatus, setBatchStatus] = useState<string | undefined>();
  const [batchSearch, setBatchSearch] = useState("");
  const [syncingBatchId, setSyncingBatchId] = useState<number | null>(null);
  const [cancelingBatchIds, setCancelingBatchIds] = useState<Set<number>>(() => new Set());
  const [selectedBatchRowKeys, setSelectedBatchRowKeys] = useState<Key[]>([]);

  const [campaigns, setCampaigns] = useState<ScanCampaign[]>([]);
  const [activeCampaigns, setActiveCampaigns] = useState<ScanCampaign[]>([]);
  const [campaignTotal, setCampaignTotal] = useState(0);
  const [campaignPagination, setCampaignPagination] = useState<TablePaginationConfig>({ current: 1, pageSize: 10 });
  const [campaignLoading, setCampaignLoading] = useState(false);
  const [campaignError, setCampaignError] = useState<string | null>(null);
  const [campaignStatus, setCampaignStatus] = useState<string | undefined>();
  const [campaignSearch, setCampaignSearch] = useState("");
  const [campaignBatchFilter, setCampaignBatchFilter] = useState<ScanCampaign | null>(null);
  const [syncingCampaignId, setSyncingCampaignId] = useState<number | null>(null);
  const [cancelingCampaignIds, setCancelingCampaignIds] = useState<Set<number>>(() => new Set());

  const [createOpen, setCreateOpen] = useState(false);
  const [creating, setCreating] = useState(false);
  const [selectedExposure, setSelectedExposure] = useState<ScanPortExposure | null>(null);
  const [selectedBatch, setSelectedBatch] = useState<ScanBatch | null>(null);
  const [selectedCampaign, setSelectedCampaign] = useState<ScanCampaign | null>(null);
  const [primaryTab, setPrimaryTab] = useState("exposed");
  const [scanWorkTab, setScanWorkTab] = useState("active");
  const [isPageVisible, setIsPageVisible] = useState(true);
  const [form] = Form.useForm<CreateScanFormValues>();
  const targetScope = Form.useWatch("target_scope", form) ?? "all_public_ports";
  const pollingInFlightRef = useRef(false);
  const lastScanWorkListRefreshRef = useRef(0);

  const loadStats = useCallback(async () => {
    try {
      setStats(await fetchScanStats());
    } catch (error) {
      console.error("Failed to load scan stats", error);
    }
  }, []);

  const loadExposures = useCallback(
    async (page: number, pageSize: number) => {
      setExposureLoading(true);
      try {
        const payload = await fetchScanPortExposures({
          page,
          page_size: pageSize,
          q: search || undefined,
          quick_filter: quickFilter,
          result: quickFilter === "needs_rescan" ? "unknown_or_filtered" : "confirmed_open",
          sort_by: "risk",
          sort_order: "desc",
        });
        setExposures(sortExposures(payload.items ?? []));
        setExposureTotal(payload.total ?? 0);
        setExposureError(null);
      } catch (error) {
        console.error("Failed to load port exposures", error);
        setExposures([]);
        setExposureTotal(0);
        setExposureError("The port exposure API is temporarily unavailable.");
      } finally {
        setExposureLoading(false);
      }
    },
    [quickFilter, search],
  );

  const loadCampaigns = useCallback(
    async (page: number, pageSize: number, options: LoadOptions = {}) => {
      if (!options.silent) {
        setCampaignLoading(true);
      }
      try {
        const payload = await fetchScanCampaigns({
          page,
          page_size: pageSize,
          status: campaignStatus,
          q: campaignSearch || undefined,
        });
        setCampaigns(payload.items ?? []);
        setCampaignTotal(payload.total ?? 0);
        setCampaignError(null);
      } catch (error) {
        console.error("Failed to load scan campaigns", error);
        if (!options.silent) {
          setCampaigns([]);
          setCampaignTotal(0);
          setCampaignError("Scan campaigns could not be loaded.");
        }
      } finally {
        if (!options.silent) {
          setCampaignLoading(false);
        }
      }
    },
    [campaignSearch, campaignStatus],
  );

  const loadBatches = useCallback(
    async (page: number, pageSize: number, options: LoadOptions = {}) => {
      if (!options.silent) {
        setBatchLoading(true);
      }
      try {
        if (campaignBatchFilter) {
          const payload = await fetchScanCampaignBatches(campaignBatchFilter.id);
          const normalizedSearch = batchSearch.trim().toLowerCase();
          const filtered = payload.filter((batch) => {
            const matchesStatus = batchStatus ? batch.status === batchStatus : true;
            const matchesSearch = normalizedSearch
              ? [
                  batch.request_id,
                  batch.scanner_batch_id,
                  batch.source,
                  batch.created_by_name,
                ].some((value) => value?.toLowerCase().includes(normalizedSearch))
              : true;
            return matchesStatus && matchesSearch;
          });
          const start = (page - 1) * pageSize;
          setBatches(filtered.slice(start, start + pageSize));
          setBatchTotal(filtered.length);
        } else {
          const payload = await fetchScanBatches({
            page,
            page_size: pageSize,
            status: batchStatus,
            q: batchSearch || undefined,
          });
          setBatches(payload.items ?? []);
          setBatchTotal(payload.total ?? 0);
        }
        setBatchError(null);
      } catch (error) {
        console.error("Failed to load scan batches", error);
        if (!options.silent) {
          setBatches([]);
          setBatchTotal(0);
          setBatchError("Scan batches could not be loaded.");
        }
      } finally {
        if (!options.silent) {
          setBatchLoading(false);
        }
      }
    },
    [batchSearch, batchStatus, campaignBatchFilter],
  );

  const loadActiveScanWork = useCallback(async () => {
    try {
      const [campaignPayload, batchPayload] = await Promise.all([
        fetchScanCampaigns({ page: 1, page_size: 100, status: "active" }),
        fetchScanBatches({ page: 1, page_size: 100, status: "active" }),
      ]);
      setActiveCampaigns(campaignPayload.items ?? []);
      setActiveBatches(batchPayload.items ?? []);
    } catch (error) {
      console.error("Failed to load active scan work", error);
    }
  }, []);

  const refreshScanWork = useCallback(
    async (options: LoadOptions = {}) => {
      await Promise.all([
        loadStats(),
        loadActiveScanWork(),
        loadCampaigns(campaignPagination.current ?? 1, campaignPagination.pageSize ?? 10, options),
        loadBatches(batchPagination.current ?? 1, batchPagination.pageSize ?? 20, options),
      ]);
    },
    [batchPagination, campaignPagination, loadActiveScanWork, loadBatches, loadCampaigns, loadStats],
  );

  const visibleScanWorkRecords = useMemo(
    () => [
      ...campaigns,
      ...batches.filter((batch) => !batch.campaign_id),
    ],
    [batches, campaigns],
  );

  const activeScanWorkRecords = useMemo(
    () => [...activeCampaigns, ...activeBatches].filter(isScanActive),
    [activeBatches, activeCampaigns],
  );

  const activeScanWorkRows = useMemo<ActiveScanWorkRow[]>(
    () => [
      ...activeCampaigns
        .filter(isScanActive)
        .map((record) => ({ key: `campaign-${record.id}`, kind: "campaign" as const, record })),
      ...activeBatches
        .filter(isScanActive)
        .map((record) => ({ key: `batch-${record.id}`, kind: "batch" as const, record })),
    ],
    [activeBatches, activeCampaigns],
  );

  const scannerJobTotals = useMemo(
    () => getScannerJobTotals(activeScanWorkRecords.length > 0 ? activeScanWorkRecords : visibleScanWorkRecords),
    [activeScanWorkRecords, visibleScanWorkRecords],
  );

  const staleScanWorkRecords = useMemo(
    () => getStaleActiveRecords(activeScanWorkRecords.length > 0 ? activeScanWorkRecords : visibleScanWorkRecords),
    [activeScanWorkRecords, visibleScanWorkRecords],
  );

  const latestVisibleSyncAt = useMemo(
    () => getLatestTimestamp(visibleScanWorkRecords.map((record) => record.last_synced_at)),
    [visibleScanWorkRecords],
  );

  const visibleCallbackFailures = useMemo(
    () =>
      visibleScanWorkRecords.reduce(
        (total, record) => total + (record.callback_failure_count ?? 0),
        0,
      ),
    [visibleScanWorkRecords],
  );

  const latestScannerSyncAt = stats?.latest_scanner_sync_at ?? latestVisibleSyncAt;
  const callbackFailureCount = stats?.callback_failures ?? visibleCallbackFailures;
  const latestSyncStale = isOlderThan(latestScannerSyncAt, STALE_SYNC_MINUTES);
  const shouldPollScanWork = activeScanWorkRecords.length > 0 || (stats?.active_batches ?? 0) > 0;
  const isActiveScanWorkVisible = primaryTab === "batches" && scanWorkTab === "active";

  useEffect(() => {
    const handleVisibilityChange = () => {
      setIsPageVisible(document.visibilityState === "visible");
    };

    handleVisibilityChange();
    document.addEventListener("visibilitychange", handleVisibilityChange);
    return () => document.removeEventListener("visibilitychange", handleVisibilityChange);
  }, []);

  useEffect(() => {
    void loadStats();
    void loadActiveScanWork();
  }, [loadActiveScanWork, loadStats]);

  useEffect(() => {
    void loadExposures(exposurePagination.current ?? 1, exposurePagination.pageSize ?? 20);
  }, [exposurePagination, loadExposures]);

  useEffect(() => {
    void loadCampaigns(campaignPagination.current ?? 1, campaignPagination.pageSize ?? 10);
  }, [campaignPagination, loadCampaigns]);

  useEffect(() => {
    void loadBatches(batchPagination.current ?? 1, batchPagination.pageSize ?? 20);
  }, [batchPagination, loadBatches]);

  useEffect(() => {
    setSelectedBatchRowKeys((current) =>
      current.filter((key) => batches.some((batch) => batch.id === key)),
    );
  }, [batches]);

  const pollScanWork = useCallback(async () => {
    if (pollingInFlightRef.current || document.visibilityState !== "visible") {
      return;
    }

    pollingInFlightRef.current = true;
    try {
      await loadStats();
      await loadActiveScanWork();

      const now = Date.now();
      const shouldRefreshLists =
        isActiveScanWorkVisible ||
        now - lastScanWorkListRefreshRef.current >= BACKGROUND_SCAN_WORK_REFRESH_MS;

      if (shouldRefreshLists) {
        await Promise.all([
          loadCampaigns(campaignPagination.current ?? 1, campaignPagination.pageSize ?? 10, { silent: true }),
          loadBatches(batchPagination.current ?? 1, batchPagination.pageSize ?? 20, { silent: true }),
        ]);
        lastScanWorkListRefreshRef.current = now;
      }
    } finally {
      pollingInFlightRef.current = false;
    }
  }, [
    batchPagination,
    campaignPagination,
    isActiveScanWorkVisible,
    loadActiveScanWork,
    loadBatches,
    loadCampaigns,
    loadStats,
  ]);

  useEffect(() => {
    if (!shouldPollScanWork || !isPageVisible) {
      return undefined;
    }

    const intervalId = window.setInterval(() => {
      void pollScanWork();
    }, POLLING_INTERVAL_MS);

    return () => window.clearInterval(intervalId);
  }, [isPageVisible, pollScanWork, shouldPollScanWork]);

  useEffect(() => {
    if (shouldPollScanWork && isPageVisible) {
      void pollScanWork();
    }
  }, [isPageVisible, pollScanWork, shouldPollScanWork]);

  const createScan = useCallback(
    async (values: CreateScanFormValues) => {
      const targetScope = values.target_scope || "selected_ports";
      const targetIds = targetScope === "selected_ports" ? parseTargetIds(values.target_ids || "") : [];
      if (targetScope === "selected_ports" && !targetIds.length) {
        message.warning("Enter at least one CAASM port ID.");
        return;
      }

      setCreating(true);
      try {
        const payload = {
          client_request_id: createScanClientRequestId(),
          source: values.source,
          target_scope: targetScope,
          target_ids: targetScope === "selected_ports" ? targetIds : undefined,
          metadata: {
            source_page: "scans_port_exposure",
            target_scope: targetScope,
            operator_note: values.note || undefined,
          },
        };

        if (targetScope === "all_public_ports") {
          const campaign = await createScanCampaign(payload);
          message.success("All-public scan campaign created");
          setCreateOpen(false);
          form.resetFields();
          setSelectedCampaign(campaign);
          setCampaignBatchFilter(null);
          await loadCampaigns(1, campaignPagination.pageSize ?? 10);
          await loadStats();
          return;
        }

        const batch = await createScanBatch(payload);
        message.success("Port exposure scan batch created");
        setCreateOpen(false);
        form.resetFields();
        router.push(`/dashboard/scans/port-exposure/${batch.id}`);
      } catch (error) {
        console.error("Failed to create scan", error);
        message.error("Failed to create scan");
      } finally {
        setCreating(false);
      }
    },
    [campaignPagination.pageSize, form, loadCampaigns, loadStats, router],
  );

  const handleSync = useCallback(
    async (batchId: number) => {
      if (!canManage) return;
      setSyncingBatchId(batchId);
      try {
        await syncScanBatch(batchId);
        message.success("Scan batch sync requested");
        await loadBatches(batchPagination.current ?? 1, batchPagination.pageSize ?? 20);
        await loadStats();
      } catch (error) {
        console.error("Failed to sync scan batch", error);
        message.error("Failed to sync scan batch");
      } finally {
        setSyncingBatchId(null);
      }
    },
    [batchPagination, canManage, loadBatches, loadStats],
  );

  const handleCampaignSync = useCallback(
    async (campaignId: number) => {
      if (!canManage) return;
      setSyncingCampaignId(campaignId);
      try {
        const campaign = await syncScanCampaign(campaignId);
        message.success("Scan campaign sync requested");
        setSelectedCampaign((current) => (current?.id === campaign.id ? campaign : current));
        await loadCampaigns(campaignPagination.current ?? 1, campaignPagination.pageSize ?? 10);
        await loadBatches(batchPagination.current ?? 1, batchPagination.pageSize ?? 20);
        await loadStats();
      } catch (error) {
        console.error("Failed to sync scan campaign", error);
        message.error("Failed to sync scan campaign");
      } finally {
        setSyncingCampaignId(null);
      }
    },
    [batchPagination, campaignPagination, canManage, loadBatches, loadCampaigns, loadStats],
  );

  const setBatchCanceling = useCallback((batchId: number, canceling: boolean) => {
    setCancelingBatchIds((current) => {
      const next = new Set(current);
      if (canceling) {
        next.add(batchId);
      } else {
        next.delete(batchId);
      }
      return next;
    });
  }, []);

  const setCampaignCanceling = useCallback((campaignId: number, canceling: boolean) => {
    setCancelingCampaignIds((current) => {
      const next = new Set(current);
      if (canceling) {
        next.add(campaignId);
      } else {
        next.delete(campaignId);
      }
      return next;
    });
  }, []);

  const requestBatchCancel = useCallback(
    async (batch: ScanBatch, options: { showMessage?: boolean } = {}) => {
      if (!canManage || !isScanCancelable(batch)) return false;
      setBatchCanceling(batch.id, true);
      try {
        const canceled = await cancelScanBatch(batch.id);
        setSelectedBatch((current) => (current?.id === canceled.id ? canceled : current));
        if (options.showMessage !== false) {
          message.success("Scan batch cancel requested");
        }
        await refreshScanWork({ silent: true });
        return true;
      } catch (error) {
        console.error("Failed to cancel scan batch", error);
        if (options.showMessage !== false) {
          message.error("Failed to cancel scan batch");
        }
        return false;
      } finally {
        setBatchCanceling(batch.id, false);
      }
    },
    [canManage, refreshScanWork, setBatchCanceling],
  );

  const handleCancelBatch = useCallback(
    (batch: ScanBatch) => {
      Modal.confirm({
        title: "Cancel scan batch?",
        content: "The scanner will stop queued work and let any in-flight jobs drain.",
        okText: "Cancel batch",
        okButtonProps: { danger: true },
        onOk: async () => {
          await requestBatchCancel(batch);
        },
      });
    },
    [requestBatchCancel],
  );

  const requestCampaignCancel = useCallback(
    async (campaign: ScanCampaign) => {
      if (!canManage || !isScanCancelable(campaign)) return;
      setCampaignCanceling(campaign.id, true);
      try {
        const canceled = await cancelScanCampaign(campaign.id);
        message.success("Scan campaign cancel requested");
        setSelectedCampaign((current) => (current?.id === canceled.id ? canceled : current));
        await refreshScanWork({ silent: true });
      } catch (error) {
        console.error("Failed to cancel scan campaign", error);
        message.error("Failed to cancel scan campaign");
      } finally {
        setCampaignCanceling(campaign.id, false);
      }
    },
    [canManage, refreshScanWork, setCampaignCanceling],
  );

  const handleCancelCampaign = useCallback(
    (campaign: ScanCampaign) => {
      Modal.confirm({
        title: "Cancel scan campaign?",
        content: "Cancel will apply to the campaign and its scanner batches. In-flight jobs may briefly continue while they drain.",
        okText: "Cancel campaign",
        okButtonProps: { danger: true },
        onOk: async () => {
          await requestCampaignCancel(campaign);
        },
      });
    },
    [requestCampaignCancel],
  );

  const selectedCancelableBatches = useMemo(
    () => batches.filter((batch) => selectedBatchRowKeys.includes(batch.id) && isScanCancelable(batch)),
    [batches, selectedBatchRowKeys],
  );

  const handleBulkCancelBatches = useCallback(() => {
    if (!selectedCancelableBatches.length) return;
    Modal.confirm({
      title: `Cancel ${selectedCancelableBatches.length} scan batch${selectedCancelableBatches.length === 1 ? "" : "es"}?`,
      content: "Queued work will be stopped and running scanner jobs may continue briefly while cancellation drains.",
      okText: "Cancel selected",
      okButtonProps: { danger: true },
      onOk: async () => {
        const results = await Promise.all(
          selectedCancelableBatches.map((batch) => requestBatchCancel(batch, { showMessage: false })),
        );
        const canceledCount = results.filter(Boolean).length;
        if (canceledCount) {
          message.success(`Cancel requested for ${canceledCount} batch${canceledCount === 1 ? "" : "es"}`);
        }
        if (canceledCount !== selectedCancelableBatches.length) {
          message.error("Some scan batches could not be canceled");
        }
        setSelectedBatchRowKeys([]);
      },
    });
  }, [requestBatchCancel, selectedCancelableBatches]);

  const showCampaignBatches = useCallback((campaign: ScanCampaign) => {
    setCampaignBatchFilter(campaign);
    setBatchPagination((prev) => ({ ...prev, current: 1 }));
  }, []);

  const exposureColumns: ColumnsType<ScanPortExposure> = useMemo(
    () => [
      {
        title: "Risk",
        dataIndex: "risk",
        width: 110,
        render: (_, record) => riskTag(record.derived_risk ?? record.risk),
      },
      {
        title: "Endpoint",
        key: "endpoint",
        width: 230,
        render: (_, record) => (
          <Space direction="vertical" size={2}>
            <Text strong>{record.public_ip}</Text>
            <Text type="secondary" style={{ fontSize: 12 }}>
              {record.asset_name || record.resource_id || `Batch ${record.scanner_batch_id || record.batch_id}`}
            </Text>
          </Space>
        ),
      },
      {
        title: "Scan result",
        key: "result",
        width: 190,
        render: (_, record) => (
          <Space direction="vertical" size={6}>
            <Space size={6} wrap>
              {resultTag(record.result)}
              {record.is_new ? <Tag color="blue">New</Tag> : null}
            </Space>
            <Text type="secondary" style={{ fontSize: 12 }}>
              Last {formatDate(record.last_confirmed || record.last_observed)}
            </Text>
          </Space>
        ),
      },
      {
        title: "Port",
        key: "port",
        width: 130,
        render: (_, record) => (
          <Space direction="vertical" size={2}>
            <Text strong>{record.port}</Text>
            <Text type="secondary" style={{ fontSize: 12 }}>
              {record.transport?.toUpperCase() || "TCP"}
            </Text>
          </Space>
        ),
      },
      {
        title: "Action",
        key: "actions",
        width: 210,
        fixed: "right",
        render: (_, record) => (
          <Space wrap>
            <Button
              size="small"
              onClick={(event) => {
                event.stopPropagation();
                setSelectedExposure(record);
              }}
            >
              Details
            </Button>
            {record.caasm_asset_id ? (
              <Button
                size="small"
                href={`/dashboard/caasm/assets?asset_id=${record.caasm_asset_id}`}
                onClick={(event) => event.stopPropagation()}
              >
                Asset
              </Button>
            ) : null}
            <Tooltip title={record.caasm_port_id ? "Create a new scan batch for this CAASM port" : "This exposure has no CAASM port ID to rescan from"}>
              <Button
                size="small"
                icon={<ReloadOutlined />}
                disabled={!canCreate || !record.caasm_port_id}
                onClick={(event) => {
                  event.stopPropagation();
                  if (!record.caasm_port_id) return;
                  void createScan({
                    source: "caasm_ports",
                    target_scope: "selected_ports",
                    target_ids: String(record.caasm_port_id),
                    note: `Rescan ${record.public_ip}:${record.port}`,
                  });
                }}
              >
                Rescan
              </Button>
            </Tooltip>
          </Space>
        ),
      },
    ],
    [canCreate, createScan],
  );

  const campaignColumns: ColumnsType<ScanCampaign> = useMemo(
    () => [
      {
        title: "Campaign",
        key: "campaign",
        width: 340,
        render: (_, record) => (
          <Space direction="vertical" size={2} style={{ width: "100%" }}>
            <Tooltip title={record.request_id || `Campaign #${record.id}`}>
              <Button
                type="link"
                style={ID_LINK_STYLE}
                onClick={() => setSelectedCampaign(record)}
              >
                {record.request_id || `Campaign #${record.id}`}
              </Button>
            </Tooltip>
            <Space size={6} wrap>
              <Tag>{record.target_scope?.replaceAll("_", " ") || "all public ports"}</Tag>
              <Text type="secondary" style={TABLE_META_TEXT_STYLE} ellipsis={{ tooltip: record.source || "caasm_ports" }}>
                {record.source || "caasm_ports"}
              </Text>
            </Space>
          </Space>
        ),
      },
      {
        title: "Status",
        dataIndex: "status",
        width: 150,
        render: (_, record) => (
          <Space direction="vertical" size={4}>
            {getBatchStatusTag(record.status)}
            {isScanStopping(record) ? (
              <Text type="secondary" style={{ fontSize: 12 }}>
                {record.status === "cancel_requested" ? "Cancel requested" : "Stopping"}
              </Text>
            ) : null}
          </Space>
        ),
      },
      {
        title: "Progress",
        key: "progress",
        width: 280,
        render: (_, record) => (
          <Space direction="vertical" size={4} style={{ width: "100%" }}>
            <Progress percent={getCampaignProgress(record)} size="small" />
            <Text type="secondary" style={{ fontSize: 12 }}>
              {formatNumber(record.completed_batches)} done / {formatNumber(record.failed_batches)} failed / {formatNumber(record.batch_count)} batches
            </Text>
            {scannerJobsSummary(record)}
            <Text type="secondary" style={{ fontSize: 12 }}>
              Last sync {formatDate(record.last_synced_at)}
            </Text>
          </Space>
        ),
      },
      {
        title: "Targets",
        key: "targets",
        width: 190,
        render: (_, record) => (
          <Space direction="vertical" size={2}>
            <Text>{formatNumber(record.accepted_targets)} accepted</Text>
            <Text type="secondary" style={{ fontSize: 12 }}>
              {formatNumber(record.rejected_targets)} rejected / {formatNumber(record.target_count)} total
            </Text>
          </Space>
        ),
      },
      {
        title: "Created",
        key: "created",
        width: 220,
        render: (_, record) => (
          <Space direction="vertical" size={2}>
            <Text>{formatDate(record.created_at || record.submitted_at)}</Text>
            <Text type="secondary" style={{ fontSize: 12 }}>{record.created_by_name || "Unknown operator"}</Text>
          </Space>
        ),
      },
      {
        title: "Action",
        key: "action",
        width: 320,
        render: (_, record) => (
          <Space wrap>
            <Button size="small" onClick={() => setSelectedCampaign(record)}>
              Detail
            </Button>
            <Button size="small" onClick={() => showCampaignBatches(record)}>
              Batches
            </Button>
            <Button
              size="small"
              icon={<SyncOutlined />}
              disabled={!canManage}
              loading={syncingCampaignId === record.id}
              onClick={() => void handleCampaignSync(record.id)}
            >
              Sync
            </Button>
            {isScanStopping(record) ? (
              <Button size="small" icon={<StopOutlined />} disabled>
                {record.status === "cancel_requested" ? "Cancel requested" : "Stopping"}
              </Button>
            ) : (
              <Button
                size="small"
                danger
                icon={<StopOutlined />}
                disabled={!canManage || !isScanCancelable(record)}
                loading={cancelingCampaignIds.has(record.id)}
                onClick={() => handleCancelCampaign(record)}
              >
                Cancel
              </Button>
            )}
          </Space>
        ),
      },
    ],
    [
      cancelingCampaignIds,
      canManage,
      handleCancelCampaign,
      handleCampaignSync,
      showCampaignBatches,
      syncingCampaignId,
    ],
  );

  const batchColumns: ColumnsType<ScanBatch> = useMemo(
    () => [
      {
        title: "Batch",
        key: "batch",
        width: 360,
        render: (_, record) => (
          <Space direction="vertical" size={2} style={{ width: "100%" }}>
            <Tooltip title={record.request_id || `Batch #${record.id}`}>
              <Button
                type="link"
                style={ID_LINK_STYLE}
                onClick={() => setSelectedBatch(record)}
              >
                {record.request_id || `Batch #${record.id}`}
              </Button>
            </Tooltip>
            <Text
              type="secondary"
              style={TABLE_META_TEXT_STYLE}
              ellipsis={{ tooltip: `Scanner ${record.scanner_batch_id || "-"}` }}
            >
              Scanner {record.scanner_batch_id || "-"}
            </Text>
          </Space>
        ),
      },
      {
        title: "Status",
        dataIndex: "status",
        width: 150,
        render: (_, record) => (
          <Space direction="vertical" size={4}>
            {getBatchStatusTag(record.status)}
            {isScanStopping(record) ? (
              <Text type="secondary" style={{ fontSize: 12 }}>
                {record.status === "cancel_requested" ? "Cancel requested" : "Stopping"}
              </Text>
            ) : null}
          </Space>
        ),
      },
      {
        title: "Targets",
        key: "targets",
        width: 170,
        render: (_, record) => (
          <Space direction="vertical" size={2}>
            <Text>{formatNumber(record.accepted_targets)} accepted</Text>
            <Text type="secondary" style={{ fontSize: 12 }}>
              {formatNumber(record.rejected_targets)} rejected / {formatNumber(record.target_count)} total
            </Text>
          </Space>
        ),
      },
      {
        title: "Scanner jobs",
        key: "scanner_jobs",
        width: 270,
        render: (_, record) => (
          <Space direction="vertical" size={4}>
            {scannerJobsSummary(record)}
            <Text type="secondary" style={{ fontSize: 12 }}>
              Last sync {formatDate(record.last_synced_at)}
            </Text>
          </Space>
        ),
      },
      {
        title: "Results",
        key: "results",
        width: 210,
        render: (_, record) => {
          const counts = record.result_counts ?? {};
          return (
            <Space wrap size={4}>
              <Tag color="red">Open {counts.confirmed_open ?? 0}</Tag>
              <Tag color="gold">Needs rescan {counts.unknown_or_filtered ?? 0}</Tag>
              <Tag>Skipped {counts.skipped ?? 0}</Tag>
            </Space>
          );
        },
      },
      {
        title: "Created",
        key: "created",
        width: 220,
        render: (_, record) => (
          <Space direction="vertical" size={2}>
            <Text>{formatDate(record.created_at || record.submitted_at)}</Text>
            <Text type="secondary" style={{ fontSize: 12 }}>{record.created_by_name || "Unknown operator"}</Text>
          </Space>
        ),
      },
      {
        title: "Callback",
        key: "callback",
        width: 200,
        render: (_, record) => (
          <Space direction="vertical" size={2}>
            <Text>{record.latest_callback_status || "-"}</Text>
            <Text type="secondary" style={{ fontSize: 12 }}>{formatDate(record.latest_callback_at)}</Text>
          </Space>
        ),
      },
      {
        title: "Action",
        key: "action",
        width: 280,
        render: (_, record) => (
          <Space wrap>
            <Button size="small" onClick={() => setSelectedBatch(record)}>
              Detail
            </Button>
            <Button
              size="small"
              icon={<SyncOutlined />}
              disabled={!canManage}
              loading={syncingBatchId === record.id}
              onClick={() => void handleSync(record.id)}
            >
              Sync
            </Button>
            {isScanStopping(record) ? (
              <Button size="small" icon={<StopOutlined />} disabled>
                {record.status === "cancel_requested" ? "Cancel requested" : "Stopping"}
              </Button>
            ) : (
              <Button
                size="small"
                danger
                icon={<StopOutlined />}
                disabled={!canManage || !isScanCancelable(record)}
                loading={cancelingBatchIds.has(record.id)}
                onClick={() => handleCancelBatch(record)}
              >
                Cancel
              </Button>
            )}
          </Space>
        ),
      },
    ],
    [cancelingBatchIds, canManage, handleCancelBatch, handleSync, syncingBatchId],
  );

  const activeWorkColumns: ColumnsType<ActiveScanWorkRow> = useMemo(
    () => [
      {
        title: "Scan work",
        key: "scan_work",
        width: 330,
        onCell: () => ({ style: { overflow: "hidden" } }),
        render: (_, row) => {
          const record = row.record;
          const scanWorkTitle = record.request_id || `${row.kind} #${record.id}`;
          return (
            <Space direction="vertical" size={6} style={{ minWidth: 0, width: "100%" }}>
              <Tag color={row.kind === "campaign" ? "purple" : "blue"} style={{ width: "fit-content" }}>
                {row.kind === "campaign" ? "Campaign" : "Batch"}
              </Tag>
              <Tooltip title={scanWorkTitle}>
                <div style={{ minWidth: 0, width: "100%" }}>
                  <Button
                    type="link"
                    style={ACTIVE_WORK_ID_BUTTON_STYLE}
                    onClick={() => {
                      if (row.kind === "campaign") {
                        setSelectedCampaign(record as ScanCampaign);
                      } else {
                        setSelectedBatch(record as ScanBatch);
                      }
                    }}
                  >
                    <span style={ACTIVE_WORK_ID_TEXT_STYLE}>{scanWorkTitle}</span>
                  </Button>
                </div>
              </Tooltip>
              <Text
                type="secondary"
                style={TABLE_META_TEXT_STYLE}
                ellipsis={{
                  tooltip:
                    row.kind === "batch"
                      ? `Scanner ${(record as ScanBatch).scanner_batch_id || "-"}`
                      : record.source || "caasm_ports",
                }}
              >
                {row.kind === "batch"
                  ? `Scanner ${(record as ScanBatch).scanner_batch_id || "-"}`
                  : record.source || "caasm_ports"}
              </Text>
            </Space>
          );
        },
      },
      {
        title: "Status",
        key: "status",
        width: 160,
        render: (_, row) => (
          <Space direction="vertical" size={4}>
            {getBatchStatusTag(row.record.status)}
            {isScanStopping(row.record) ? (
              <Text type="secondary" style={{ fontSize: 12 }}>
                {row.record.status === "cancel_requested" ? "Cancel requested" : "Stopping"}
              </Text>
            ) : null}
          </Space>
        ),
      },
      {
        title: "Scanner jobs",
        key: "scanner_jobs",
        width: 360,
        render: (_, row) => (
          <Space direction="vertical" size={6} style={{ width: "100%" }}>
            {scannerJobsSummary(row.record)}
            <Progress
              percent={
                row.kind === "campaign"
                  ? getCampaignProgress(row.record as ScanCampaign)
                  : row.record.target_count
                    ? Math.round((((row.record.succeeded_jobs ?? 0) + (row.record.failed_jobs ?? 0) + (row.record.skipped_jobs ?? 0)) / row.record.target_count) * 100)
                    : 0
              }
              size="small"
              showInfo={false}
            />
          </Space>
        ),
      },
      {
        title: "Freshness",
        key: "freshness",
        width: 240,
        render: (_, row) => (
          <Space direction="vertical" size={2}>
            <Text type={isOlderThan(row.record.updated_at, STALE_ACTIVE_MINUTES) ? "warning" : "secondary"}>
              Updated {formatDate(row.record.updated_at)}
            </Text>
            <Text type={isOlderThan(row.record.last_synced_at, STALE_SYNC_MINUTES) ? "warning" : "secondary"}>
              Synced {formatDate(row.record.last_synced_at)}
            </Text>
          </Space>
        ),
      },
      {
        title: "Action",
        key: "action",
        width: 280,
        fixed: "right",
        render: (_, row) => {
          const record = row.record;
          const isCampaign = row.kind === "campaign";
          const syncing = isCampaign
            ? syncingCampaignId === record.id
            : syncingBatchId === record.id;
          const canceling = isCampaign
            ? cancelingCampaignIds.has(record.id)
            : cancelingBatchIds.has(record.id);
          return (
            <Space wrap>
              <Button
                size="small"
                onClick={() => {
                  if (isCampaign) {
                    setSelectedCampaign(record as ScanCampaign);
                  } else {
                    setSelectedBatch(record as ScanBatch);
                  }
                }}
              >
                Detail
              </Button>
              <Button
                size="small"
                icon={<SyncOutlined />}
                disabled={!canManage}
                loading={syncing}
                onClick={() => {
                  if (isCampaign) {
                    void handleCampaignSync(record.id);
                  } else {
                    void handleSync(record.id);
                  }
                }}
              >
                Sync
              </Button>
              {isScanStopping(record) ? (
                <Button size="small" icon={<StopOutlined />} disabled>
                  {record.status === "cancel_requested" ? "Cancel requested" : "Stopping"}
                </Button>
              ) : (
                <Button
                  size="small"
                  danger
                  icon={<StopOutlined />}
                  disabled={!canManage || !isScanCancelable(record)}
                  loading={canceling}
                  onClick={() => {
                    if (isCampaign) {
                      handleCancelCampaign(record as ScanCampaign);
                    } else {
                      handleCancelBatch(record as ScanBatch);
                    }
                  }}
                >
                  Cancel
                </Button>
              )}
            </Space>
          );
        },
      },
    ],
    [
      cancelingBatchIds,
      cancelingCampaignIds,
      canManage,
      handleCancelBatch,
      handleCancelCampaign,
      handleCampaignSync,
      handleSync,
      syncingBatchId,
      syncingCampaignId,
    ],
  );

  const metricCards = (
    <div style={{ display: "grid", gap: 16, gridTemplateColumns: "repeat(auto-fit, minmax(220px, 1fr))" }}>
      <Card>
        <Statistic title="Confirmed exposed" value={stats?.result_counts?.confirmed_open ?? 0} />
        <Paragraph type="secondary" style={{ marginBottom: 0, marginTop: 8 }}>
          Scanner-confirmed reachable public ports.
        </Paragraph>
      </Card>
      <Card>
        <Statistic title="High-risk ports" value={stats?.high_risk_open_count ?? 0} />
        <Paragraph type="secondary" style={{ marginBottom: 0, marginTop: 8 }}>
          Risk is derived from scanner result and CAASM context.
        </Paragraph>
      </Card>
      <Card>
        <Statistic title="New since last scan" value={stats?.newly_exposed_count ?? 0} />
        <Paragraph type="secondary" style={{ marginBottom: 0, marginTop: 8 }}>
          Newly observed public reachability.
        </Paragraph>
      </Card>
      <Card>
        <Statistic
          title={(
            <Space size={6}>
              <span>Unknown needs rescan</span>
              <Tooltip title={INCONCLUSIVE_RESULT_COPY}>
                <InfoCircleOutlined style={{ color: "#8c8c8c" }} />
              </Tooltip>
            </Space>
          )}
          value={stats?.unknown_or_filtered_count ?? stats?.result_counts?.unknown_or_filtered ?? 0}
        />
        <Paragraph type="secondary" style={{ marginBottom: 0, marginTop: 8 }}>
          Inconclusive; CAASM facts remain unchanged.
        </Paragraph>
      </Card>
    </div>
  );

  const statusStrip = (
    <Card size="small">
      <Space direction="vertical" size={10} style={{ width: "100%" }}>
        <Space style={{ justifyContent: "space-between", width: "100%" }} align="start" wrap>
          <Space wrap size={[8, 8]}>
            <Tag color={shouldPollScanWork ? "processing" : "default"}>
              Active batches {formatNumber(stats?.active_batches ?? activeScanWorkRecords.length)}
            </Tag>
            <Tag color={scannerJobTotals.running > 0 ? "geekblue" : "default"}>
              Running {formatNumber(scannerJobTotals.running)}
            </Tag>
            <Tag color={scannerJobTotals.queued > 0 ? "blue" : "default"}>
              Queued {formatNumber(scannerJobTotals.queued)}
            </Tag>
            <Tag color="green">Done {formatNumber(scannerJobTotals.succeeded)}</Tag>
            <Tag color={scannerJobTotals.failed > 0 ? "red" : "default"}>
              Failed {formatNumber(scannerJobTotals.failed)}
            </Tag>
            <Tag>Skipped {formatNumber(scannerJobTotals.skipped)}</Tag>
            {typeof callbackFailureCount === "number" ? (
              <Tag color={callbackFailureCount > 0 ? "volcano" : "default"}>
                Callback failures {formatNumber(callbackFailureCount)}
              </Tag>
            ) : null}
          </Space>
          <Text type={latestSyncStale ? "warning" : "secondary"}>
            Latest sync {formatDate(latestScannerSyncAt)}
          </Text>
        </Space>
        {staleScanWorkRecords.length || latestSyncStale ? (
          <Alert
            type="warning"
            showIcon
            message="Scan status may be stale"
            description={
              staleScanWorkRecords.length
                ? `${staleScanWorkRecords.length} active scan item${staleScanWorkRecords.length === 1 ? "" : "s"} have not updated recently.`
                : `Scanner sync has not updated in at least ${STALE_SYNC_MINUTES} minutes.`
            }
          />
        ) : null}
      </Space>
    </Card>
  );

  const activeScanPanel = (
    <Space direction="vertical" size={16} style={{ width: "100%" }}>
      {statusStrip}
      <Card>
        <Space direction="vertical" size={16} style={{ width: "100%" }}>
          <Space style={{ justifyContent: "space-between", width: "100%" }} align="start" wrap>
            <div>
              <Title level={5} style={{ marginBottom: 4 }}>
                Active scan status
              </Title>
              <Text type="secondary">Live campaign and batch work from the scanner control plane.</Text>
            </div>
            <Space wrap>
              <Button icon={<ReloadOutlined />} onClick={() => void refreshScanWork()}>
                Refresh
              </Button>
              {selectedCancelableBatches.length ? (
                <Button
                  danger
                  icon={<StopOutlined />}
                  disabled={!canManage}
                  onClick={handleBulkCancelBatches}
                >
                  Cancel selected ({selectedCancelableBatches.length})
                </Button>
              ) : null}
            </Space>
          </Space>
          <Table
            rowKey="key"
            dataSource={activeScanWorkRows}
            columns={activeWorkColumns}
            loading={batchLoading || campaignLoading}
            tableLayout="fixed"
            locale={{
              emptyText: (
                <PageStateNotice
                  tone="empty"
                  compact
                  title="No active scanner work"
                  description="Running, queued, submitting, canceling, and stale scan work appears here while the page polls."
                  actions={<Button icon={<ReloadOutlined />} onClick={() => void refreshScanWork()}>Refresh</Button>}
                />
              ),
            }}
            pagination={false}
            scroll={{ x: 1370 }}
          />
        </Space>
      </Card>
    </Space>
  );

  const batchInventoryPanel = (
    <Card>
      <Space direction="vertical" size={16} style={{ width: "100%" }}>
        <Space style={{ justifyContent: "space-between", width: "100%" }} align="start" wrap>
          <div>
            <Title level={5} style={{ marginBottom: 4 }}>
              Scan batches
            </Title>
            <Text type="secondary">Full scanner batch inventory with cancellation and manual sync controls.</Text>
          </div>
          <Space wrap>
            {campaignBatchFilter ? (
              <Tag
                closable
                onClose={() => {
                  setCampaignBatchFilter(null);
                  setBatchPagination((prev) => ({ ...prev, current: 1 }));
                }}
              >
                Campaign {campaignBatchFilter.request_id || `#${campaignBatchFilter.id}`}
              </Tag>
            ) : null}
            <Select
              allowClear
              placeholder="All statuses"
              style={{ width: 180 }}
              value={batchStatus}
              onChange={(value) => {
                setBatchStatus(value);
                setBatchPagination((prev) => ({ ...prev, current: 1 }));
              }}
              options={statusFilterOptions}
            />
            <Input.Search
              allowClear
              placeholder="Request ID, scanner batch, IP, or asset"
              style={{ width: 340 }}
              onSearch={(value) => {
                setBatchSearch(value);
                setBatchPagination((prev) => ({ ...prev, current: 1 }));
              }}
            />
            {selectedBatchRowKeys.length ? (
              <Button
                danger
                icon={<StopOutlined />}
                disabled={!canManage || !selectedCancelableBatches.length}
                onClick={handleBulkCancelBatches}
              >
                Cancel selected ({selectedCancelableBatches.length})
              </Button>
            ) : null}
            <Button icon={<SyncOutlined />} onClick={() => void loadBatches(batchPagination.current ?? 1, batchPagination.pageSize ?? 20)}>
              Sync now
            </Button>
          </Space>
        </Space>
        <Table
          rowKey="id"
          dataSource={batches}
          columns={batchColumns}
          loading={batchLoading}
          tableLayout="fixed"
          rowSelection={{
            selectedRowKeys: selectedBatchRowKeys,
            onChange: setSelectedBatchRowKeys,
            getCheckboxProps: (record) => ({
              disabled: !isScanCancelable(record),
            }),
          }}
          locale={{
            emptyText: batchError ? (
              <PageStateNotice
                tone="error"
                compact
                title="Scan batches unavailable"
                description={batchError}
                actions={<Button icon={<ReloadOutlined />} onClick={() => void loadBatches(batchPagination.current ?? 1, batchPagination.pageSize ?? 20)}>Retry</Button>}
              />
            ) : (
              <PageStateNotice
                tone="empty"
                compact
                title="No scan batches yet"
                description="Create a scan from selected CAASM ports to start collecting external reachability observations."
                actions={canCreate ? <Button type="primary" icon={<PlusOutlined />} onClick={() => setCreateOpen(true)}>Create scan</Button> : undefined}
              />
            ),
          }}
          pagination={{ ...batchPagination, total: batchTotal }}
          onChange={(pager) => setBatchPagination(pager)}
          scroll={{ x: 1780 }}
        />
      </Space>
    </Card>
  );

  const campaignInventoryPanel = (
    <Card>
      <Space direction="vertical" size={16} style={{ width: "100%" }}>
        <Space style={{ justifyContent: "space-between", width: "100%" }} align="start" wrap>
          <div>
            <Title level={5} style={{ marginBottom: 4 }}>
              Campaigns
            </Title>
            <Text type="secondary">All-public scans split into scanner-sized child batches.</Text>
          </div>
          <Space wrap>
            <Select
              allowClear
              placeholder="All statuses"
              style={{ width: 180 }}
              value={campaignStatus}
              onChange={(value) => {
                setCampaignStatus(value);
                setCampaignPagination((prev) => ({ ...prev, current: 1 }));
              }}
              options={statusFilterOptions}
            />
            <Input.Search
              allowClear
              placeholder="Campaign request ID"
              style={{ width: 300 }}
              onSearch={(value) => {
                setCampaignSearch(value);
                setCampaignPagination((prev) => ({ ...prev, current: 1 }));
              }}
            />
            <Button icon={<SyncOutlined />} onClick={() => void loadCampaigns(campaignPagination.current ?? 1, campaignPagination.pageSize ?? 10)}>
              Sync now
            </Button>
          </Space>
        </Space>
        <Table
          rowKey="id"
          dataSource={campaigns}
          columns={campaignColumns}
          loading={campaignLoading}
          tableLayout="fixed"
          locale={{
            emptyText: campaignError ? (
              <PageStateNotice
                tone="error"
                compact
                title="Scan campaigns unavailable"
                description={campaignError}
                actions={<Button icon={<ReloadOutlined />} onClick={() => void loadCampaigns(campaignPagination.current ?? 1, campaignPagination.pageSize ?? 10)}>Retry</Button>}
              />
            ) : (
              <PageStateNotice
                tone="empty"
                compact
                title="No scan campaigns yet"
                description="Create an all-public scan to split CAASM public ports into campaign batches."
                actions={canCreate ? <Button type="primary" icon={<PlusOutlined />} onClick={() => setCreateOpen(true)}>Create campaign</Button> : undefined}
              />
            ),
          }}
          pagination={{ ...campaignPagination, total: campaignTotal }}
          onChange={(pager) => setCampaignPagination(pager)}
          scroll={{ x: 1510 }}
        />
      </Space>
    </Card>
  );

  const operationRecord = selectedBatch ?? selectedCampaign;
  const operationKind = selectedBatch ? "Batch" : selectedCampaign ? "Campaign" : "Scan work";

  const operationsPanel = (
    <Card>
      <Tabs
        defaultActiveKey="detail"
        items={[
          {
            key: "detail",
            label: "Selected detail",
            children: operationRecord ? (
              <Descriptions bordered size="small" column={{ xs: 1, md: 2 }}>
                <Descriptions.Item label="Type">{operationKind}</Descriptions.Item>
                <Descriptions.Item label="Status">{getBatchStatusTag(operationRecord.status)}</Descriptions.Item>
                <Descriptions.Item label="Request ID">{operationRecord.request_id || "-"}</Descriptions.Item>
                <Descriptions.Item label="Scanner batch">
                  {selectedBatch?.scanner_batch_id || getMetadataText(operationRecord, ["scanner_batch_id"]) || "-"}
                </Descriptions.Item>
                <Descriptions.Item label="Scanner status">
                  {getDetailValue(operationRecord.scanner_status || getMetadataText(operationRecord, ["scanner_status"]))}
                </Descriptions.Item>
                <Descriptions.Item label="Last synced">{formatDate(operationRecord.last_synced_at)}</Descriptions.Item>
                <Descriptions.Item label="Updated">{formatDate(operationRecord.updated_at)}</Descriptions.Item>
                <Descriptions.Item label="Operator">
                  {getDetailValue(operationRecord.created_by_name)}
                </Descriptions.Item>
              </Descriptions>
            ) : (
              <PageStateNotice
                tone="empty"
                compact
                title="Select a campaign or batch"
                description="Use Detail from Active, Batches, or Campaigns to inspect scanner metadata and recovery state here."
              />
            ),
          },
          {
            key: "events",
            label: "Events",
            children: operationRecord ? (
              <Descriptions bordered size="small" column={1}>
                <Descriptions.Item label="Submitted">{formatDate(operationRecord.submitted_at)}</Descriptions.Item>
                <Descriptions.Item label="Completed">{formatDate(operationRecord.completed_at)}</Descriptions.Item>
                <Descriptions.Item label="Latest callback">{getDetailValue(operationRecord.latest_callback_status)}</Descriptions.Item>
                <Descriptions.Item label="Latest callback at">{formatDate(operationRecord.latest_callback_at)}</Descriptions.Item>
                <Descriptions.Item label="Callback failures">
                  {formatNumber(operationRecord.callback_failure_count)}
                </Descriptions.Item>
              </Descriptions>
            ) : (
              <PageStateNotice tone="empty" compact title="No selected scan work" description="Select a row to review callback and lifecycle events." />
            ),
          },
          {
            key: "recovery",
            label: "Recovery",
            children: (
              <Space direction="vertical" size={12} style={{ width: "100%" }}>
                {staleScanWorkRecords.length || latestSyncStale ? (
                  <Alert
                    type="warning"
                    showIcon
                    message="Status freshness needs attention"
                    description={
                      staleScanWorkRecords.length
                        ? `${staleScanWorkRecords.length} active scan item${staleScanWorkRecords.length === 1 ? "" : "s"} have stale scanner updates.`
                        : `Latest scanner sync is older than ${STALE_SYNC_MINUTES} minutes.`
                    }
                  />
                ) : (
                  <Alert type="success" showIcon message="No stale active scan work detected" />
                )}
                {operationRecord?.scanner_error ? (
                  <Alert type="error" showIcon message="Scanner error" description={operationRecord.scanner_error} />
                ) : null}
                {operationRecord?.cancel_error ? (
                  <Alert type="error" showIcon message="Cancel error" description={operationRecord.cancel_error} />
                ) : null}
                <Space wrap>
                  <Button icon={<ReloadOutlined />} onClick={() => void refreshScanWork()}>
                    Refresh state
                  </Button>
                  {operationRecord && isScanStopping(operationRecord) ? (
                    <Button icon={<StopOutlined />} disabled>
                      {operationRecord.status === "cancel_requested" ? "Cancel requested" : "Stopping"}
                    </Button>
                  ) : null}
                </Space>
              </Space>
            ),
          },
          {
            key: "audit",
            label: "Audit",
            children: operationRecord ? (
              <Descriptions bordered size="small" column={1}>
                <Descriptions.Item label="Cancel requested">{formatDate(operationRecord.cancel_requested_at)}</Descriptions.Item>
                <Descriptions.Item label="Cancel request ID">
                  {getDetailValue(operationRecord.cancel_request_id || getMetadataText(operationRecord, ["cancel_request_id"]))}
                </Descriptions.Item>
                <Descriptions.Item label="Cancel requested by">
                  {getDetailValue(operationRecord.cancel_requested_by_name)}
                </Descriptions.Item>
                <Descriptions.Item label="Canceled">{formatDate(operationRecord.canceled_at)}</Descriptions.Item>
                <Descriptions.Item label="Cancel status">
                  {getDetailValue(operationRecord.latest_cancel_status || getMetadataText(operationRecord, ["latest_cancel_status", "cancel_status"]))}
                </Descriptions.Item>
              </Descriptions>
            ) : (
              <PageStateNotice tone="empty" compact title="No audit context selected" description="Select a campaign or batch to inspect cancel metadata." />
            ),
          },
        ]}
      />
    </Card>
  );

  const scanWorkTabs = (
    <Tabs
      activeKey={scanWorkTab}
      onChange={setScanWorkTab}
      items={[
        { key: "active", label: "Active", children: activeScanPanel },
        { key: "batches", label: "Batches", children: batchInventoryPanel },
        { key: "campaigns", label: "Campaigns", children: campaignInventoryPanel },
        { key: "operations", label: "Operations", children: operationsPanel },
      ]}
    />
  );

  return (
    <Space direction="vertical" size={20} style={{ width: "100%" }}>
      <Space align="start" style={{ justifyContent: "space-between", width: "100%" }} wrap>
        <div>
          <Title level={3} style={{ marginBottom: 4 }}>
            Port Exposure
          </Title>
          <Paragraph type="secondary" style={{ marginBottom: 0 }}>
            Scanner observations are separate from CAASM inventory facts. Inconclusive results do not mark ports closed.
          </Paragraph>
        </div>
        <Space wrap>
          <Button icon={<ReloadOutlined />} onClick={() => {
            void loadStats();
            void loadExposures(exposurePagination.current ?? 1, exposurePagination.pageSize ?? 20);
            void loadCampaigns(campaignPagination.current ?? 1, campaignPagination.pageSize ?? 10);
            void loadBatches(batchPagination.current ?? 1, batchPagination.pageSize ?? 20);
          }}>
            Refresh
          </Button>
          <Button type="primary" icon={<PlusOutlined />} disabled={!canCreate} onClick={() => setCreateOpen(true)}>
            Create scan
          </Button>
        </Space>
      </Space>

      {metricCards}

      <Tabs
        activeKey={primaryTab}
        onChange={setPrimaryTab}
        items={[
          {
            key: "exposed",
            label: "Exposed ports",
            children: (
              <Card>
                <Space direction="vertical" size={16} style={{ width: "100%" }}>
                  <Space style={{ justifyContent: "space-between", width: "100%" }} align="start" wrap>
                    <Space wrap>
                      {quickFilters.map((filter) => (
                        <Tooltip key={filter.key} title={filter.description}>
                          <Button
                            type={quickFilter === filter.key ? "primary" : "default"}
                            onClick={() => {
                              setQuickFilter(filter.key);
                              setExposurePagination((prev) => ({ ...prev, current: 1 }));
                            }}
                          >
                            {filter.label}
                          </Button>
                        </Tooltip>
                      ))}
                    </Space>
                    <Input.Search
                      allowClear
                      placeholder="Search IP, asset, owner, or service"
                      style={{ width: 320 }}
                      onSearch={(value) => {
                        setSearch(value);
                        setExposurePagination((prev) => ({ ...prev, current: 1 }));
                      }}
                    />
                  </Space>
                  <Table
                    rowKey="id"
                    dataSource={exposures}
                    columns={exposureColumns}
                    loading={exposureLoading}
                    locale={{
                      emptyText: exposureError ? (
                        <PageStateNotice
                          tone="error"
                          compact
                          title="Port exposures unavailable"
                          description={exposureError}
                          actions={<Button icon={<ReloadOutlined />} onClick={() => void loadExposures(exposurePagination.current ?? 1, exposurePagination.pageSize ?? 20)}>Retry</Button>}
                        />
                      ) : (
                        <PageStateNotice
                          tone="empty"
                          compact
                          title="No scanner-confirmed exposure yet"
                          description="Create a port exposure scan from CAASM Ports or this page. Inconclusive results will appear as needs rescan, not as exposed ports."
                          actions={canCreate ? <Button type="primary" icon={<PlusOutlined />} onClick={() => setCreateOpen(true)}>Create scan</Button> : undefined}
                        />
                      ),
                    }}
                    pagination={{ ...exposurePagination, total: exposureTotal }}
                    onChange={(pager) => setExposurePagination(pager)}
                    onRow={(record) => ({
                      onClick: () => setSelectedExposure(record),
                      style: { cursor: "pointer" },
                    })}
                    scroll={{ x: 900 }}
                  />
                </Space>
              </Card>
            ),
          },
          {
            key: "batches",
            label: "Scan batches",
            children: scanWorkTabs,
          },
        ]}
      />

      <Drawer
        title={selectedExposure ? endpointLabel(selectedExposure) : "Port exposure detail"}
        width={620}
        placement="right"
        open={Boolean(selectedExposure)}
        onClose={() => setSelectedExposure(null)}
      >
        {selectedExposure ? (
          <Space direction="vertical" size={18} style={{ width: "100%" }}>
            <Space wrap>
              {riskTag(selectedExposure.derived_risk ?? selectedExposure.risk)}
              {resultTag(selectedExposure.result)}
              {selectedExposure.is_new ? <Tag color="blue">New</Tag> : null}
            </Space>

            <Descriptions bordered size="small" column={1} title="Endpoint">
              <Descriptions.Item label="Endpoint">{selectedExposure.public_ip}</Descriptions.Item>
              <Descriptions.Item label="Service">
                {getDetailValue(selectedExposure.service_name || selectedExposure.asset_service)}
              </Descriptions.Item>
              <Descriptions.Item label="Port">{selectedExposure.port}</Descriptions.Item>
              <Descriptions.Item label="Transport">{selectedExposure.transport?.toUpperCase() || "TCP"}</Descriptions.Item>
            </Descriptions>

            <Descriptions bordered size="small" column={1} title="Ownership">
              <Descriptions.Item label="Owner">{getDetailValue(selectedExposure.owner)}</Descriptions.Item>
              <Descriptions.Item label="Env">{getDetailValue(selectedExposure.environment)}</Descriptions.Item>
            </Descriptions>

            <Descriptions bordered size="small" column={1} title="Exposure evidence">
              <Descriptions.Item label="Evidence">
                {selectedExposure.result === "unknown_or_filtered"
                  ? "No scanner conclusion. CAASM inventory is unchanged."
                  : selectedExposure.evidence_summary || "Scanner evidence recorded for this port."}
              </Descriptions.Item>
              <Descriptions.Item label="Reason code">{getDetailValue(selectedExposure.reason_code)}</Descriptions.Item>
              <Descriptions.Item label="Scanner source">{getDetailValue(selectedExposure.scanner_source_ip)}</Descriptions.Item>
              <Descriptions.Item label="Connect duration">
                {selectedExposure.connect_ms != null ? `${selectedExposure.connect_ms} ms` : "-"}
              </Descriptions.Item>
              <Descriptions.Item label="First observed">{formatDate(selectedExposure.first_observed)}</Descriptions.Item>
              <Descriptions.Item label="Last observed">
                {formatDate(selectedExposure.last_confirmed || selectedExposure.last_observed)}
              </Descriptions.Item>
            </Descriptions>

            <Descriptions bordered size="small" column={1} title="Asset context">
              <Descriptions.Item label="Asset">{getDetailValue(selectedExposure.asset_name)}</Descriptions.Item>
              <Descriptions.Item label="Cloud provider">{getDetailValue(selectedExposure.cloud_provider)}</Descriptions.Item>
              <Descriptions.Item label="Cloud account">{getDetailValue(selectedExposure.cloud_account_id)}</Descriptions.Item>
              <Descriptions.Item label="Region">{getDetailValue(selectedExposure.region)}</Descriptions.Item>
              <Descriptions.Item label="Resource type">{getDetailValue(selectedExposure.resource_type)}</Descriptions.Item>
              <Descriptions.Item label="Resource ID">{getDetailValue(selectedExposure.resource_id)}</Descriptions.Item>
            </Descriptions>

            <Divider style={{ margin: 0 }} />

            <Descriptions bordered size="small" column={1} title="Record IDs">
              <Descriptions.Item label="Exposure ID">{selectedExposure.id}</Descriptions.Item>
              <Descriptions.Item label="Batch">{selectedExposure.scanner_batch_id || selectedExposure.batch_id}</Descriptions.Item>
              <Descriptions.Item label="CAASM asset">{getDetailValue(selectedExposure.caasm_asset_id)}</Descriptions.Item>
              <Descriptions.Item label="CAASM port">{getDetailValue(selectedExposure.caasm_port_id)}</Descriptions.Item>
              <Descriptions.Item label="Observation">{getDetailValue(selectedExposure.latest_observation_id)}</Descriptions.Item>
            </Descriptions>
          </Space>
        ) : null}
      </Drawer>

      <Drawer
        title={selectedCampaign ? selectedCampaign.request_id || `Campaign #${selectedCampaign.id}` : "Scan campaign detail"}
        width={640}
        placement="right"
        open={Boolean(selectedCampaign)}
        onClose={() => setSelectedCampaign(null)}
        extra={selectedCampaign ? (
          <Space>
            <Button onClick={() => showCampaignBatches(selectedCampaign)}>
              Batches
            </Button>
            <Button
              icon={<SyncOutlined />}
              disabled={!canManage}
              loading={syncingCampaignId === selectedCampaign.id}
              onClick={() => void handleCampaignSync(selectedCampaign.id)}
            >
              Sync
            </Button>
            {isScanStopping(selectedCampaign) ? (
              <Button icon={<StopOutlined />} disabled>
                {selectedCampaign.status === "cancel_requested" ? "Cancel requested" : "Stopping"}
              </Button>
            ) : (
              <Button
                danger
                icon={<StopOutlined />}
                disabled={!canManage || !isScanCancelable(selectedCampaign)}
                loading={cancelingCampaignIds.has(selectedCampaign.id)}
                onClick={() => handleCancelCampaign(selectedCampaign)}
              >
                Cancel
              </Button>
            )}
          </Space>
        ) : null}
      >
        {selectedCampaign ? (
          <Space direction="vertical" size={18} style={{ width: "100%" }}>
            <Space wrap>
              {getBatchStatusTag(selectedCampaign.status)}
              <Tag>{selectedCampaign.target_scope?.replaceAll("_", " ") || "all public ports"}</Tag>
              <Tag>{selectedCampaign.source || "caasm_ports"}</Tag>
            </Space>

            <Progress percent={getCampaignProgress(selectedCampaign)} />

            <Descriptions bordered size="small" column={1} title="Campaign">
              <Descriptions.Item label="Request ID">{selectedCampaign.request_id || "-"}</Descriptions.Item>
              <Descriptions.Item label="Scanner status">
                {getDetailValue(selectedCampaign.scanner_status || getMetadataText(selectedCampaign, ["scanner_status"]))}
              </Descriptions.Item>
              <Descriptions.Item label="Created">{formatDate(selectedCampaign.created_at || selectedCampaign.submitted_at)}</Descriptions.Item>
              <Descriptions.Item label="Submitted">{formatDate(selectedCampaign.submitted_at)}</Descriptions.Item>
              <Descriptions.Item label="Completed">{formatDate(selectedCampaign.completed_at)}</Descriptions.Item>
              <Descriptions.Item label="Last synced">{formatDate(selectedCampaign.last_synced_at)}</Descriptions.Item>
              <Descriptions.Item label="Updated">{formatDate(selectedCampaign.updated_at)}</Descriptions.Item>
              <Descriptions.Item label="Operator">{selectedCampaign.created_by_name || "Unknown operator"}</Descriptions.Item>
            </Descriptions>

            <Descriptions bordered size="small" column={1} title="Batch progress">
              <Descriptions.Item label="Total batches">{formatNumber(selectedCampaign.batch_count)}</Descriptions.Item>
              <Descriptions.Item label="Completed batches">{formatNumber(selectedCampaign.completed_batches)}</Descriptions.Item>
              <Descriptions.Item label="Failed batches">{formatNumber(selectedCampaign.failed_batches)}</Descriptions.Item>
            </Descriptions>

            <Descriptions bordered size="small" column={1} title="Targets">
              <Descriptions.Item label="Total">{formatNumber(selectedCampaign.target_count)}</Descriptions.Item>
              <Descriptions.Item label="Accepted">{formatNumber(selectedCampaign.accepted_targets)}</Descriptions.Item>
              <Descriptions.Item label="Rejected">{formatNumber(selectedCampaign.rejected_targets)}</Descriptions.Item>
              <Descriptions.Item label="Queued jobs">{formatNumber(selectedCampaign.queued_jobs)}</Descriptions.Item>
              <Descriptions.Item label="Running jobs">{formatNumber(selectedCampaign.running_jobs)}</Descriptions.Item>
              <Descriptions.Item label="Succeeded jobs">{formatNumber(selectedCampaign.succeeded_jobs)}</Descriptions.Item>
              <Descriptions.Item label="Failed jobs">{formatNumber(selectedCampaign.failed_jobs)}</Descriptions.Item>
              <Descriptions.Item label="Skipped jobs">{formatNumber(selectedCampaign.skipped_jobs)}</Descriptions.Item>
            </Descriptions>

            <Descriptions bordered size="small" column={1} title="Results">
              <Descriptions.Item label="Confirmed open">
                {formatNumber(selectedCampaign.result_counts?.confirmed_open)}
              </Descriptions.Item>
              <Descriptions.Item label="Needs rescan">
                {formatNumber(selectedCampaign.result_counts?.unknown_or_filtered)}
              </Descriptions.Item>
              <Descriptions.Item label="Skipped">{formatNumber(selectedCampaign.result_counts?.skipped)}</Descriptions.Item>
            </Descriptions>

            <Descriptions bordered size="small" column={1} title="Callbacks and cancel">
              <Descriptions.Item label="Latest callback">{getDetailValue(selectedCampaign.latest_callback_status)}</Descriptions.Item>
              <Descriptions.Item label="Latest callback at">{formatDate(selectedCampaign.latest_callback_at)}</Descriptions.Item>
              <Descriptions.Item label="Callback failures">
                {formatNumber(selectedCampaign.callback_failure_count)}
              </Descriptions.Item>
              <Descriptions.Item label="Latest callback error">
                {getDetailValue(selectedCampaign.latest_callback_error || getMetadataText(selectedCampaign, ["latest_callback_error", "callback_error"]))}
              </Descriptions.Item>
              <Descriptions.Item label="Cancel requested">{formatDate(selectedCampaign.cancel_requested_at)}</Descriptions.Item>
              <Descriptions.Item label="Cancel request ID">
                {getDetailValue(selectedCampaign.cancel_request_id || getMetadataText(selectedCampaign, ["cancel_request_id"]))}
              </Descriptions.Item>
              <Descriptions.Item label="Cancel requested by">
                {getDetailValue(selectedCampaign.cancel_requested_by_name)}
              </Descriptions.Item>
              <Descriptions.Item label="Canceled">{formatDate(selectedCampaign.canceled_at)}</Descriptions.Item>
              <Descriptions.Item label="Cancel status">
                {getDetailValue(selectedCampaign.latest_cancel_status || getMetadataText(selectedCampaign, ["latest_cancel_status", "cancel_status"]))}
              </Descriptions.Item>
              <Descriptions.Item label="Cancel error">
                {getDetailValue(selectedCampaign.cancel_error || getMetadataText(selectedCampaign, ["cancel_error"]))}
              </Descriptions.Item>
            </Descriptions>

            {selectedCampaign.scanner_error ? (
              <Alert type="error" showIcon message="Scanner error" description={selectedCampaign.scanner_error} />
            ) : null}
          </Space>
        ) : null}
      </Drawer>

      <Drawer
        title={selectedBatch ? selectedBatch.request_id || `Batch #${selectedBatch.id}` : "Scan batch detail"}
        width={620}
        placement="right"
        open={Boolean(selectedBatch)}
        onClose={() => setSelectedBatch(null)}
        extra={selectedBatch ? (
          <Space>
            <Button
              icon={<SyncOutlined />}
              disabled={!canManage}
              loading={syncingBatchId === selectedBatch.id}
              onClick={() => void handleSync(selectedBatch.id)}
            >
              Sync
            </Button>
            {isScanStopping(selectedBatch) ? (
              <Button icon={<StopOutlined />} disabled>
                {selectedBatch.status === "cancel_requested" ? "Cancel requested" : "Stopping"}
              </Button>
            ) : (
              <Button
                danger
                icon={<StopOutlined />}
                disabled={!canManage || !isScanCancelable(selectedBatch)}
                loading={cancelingBatchIds.has(selectedBatch.id)}
                onClick={() => handleCancelBatch(selectedBatch)}
              >
                Cancel
              </Button>
            )}
          </Space>
        ) : null}
      >
        {selectedBatch ? (
          <Space direction="vertical" size={18} style={{ width: "100%" }}>
            <Space wrap>
              {getBatchStatusTag(selectedBatch.status)}
              <Tag>{selectedBatch.source || "caasm_ports"}</Tag>
            </Space>

            <Descriptions bordered size="small" column={1} title="Batch">
              <Descriptions.Item label="Request ID">{selectedBatch.request_id || "-"}</Descriptions.Item>
              <Descriptions.Item label="Scanner batch">{selectedBatch.scanner_batch_id || "-"}</Descriptions.Item>
              <Descriptions.Item label="Scanner status">
                {getDetailValue(selectedBatch.scanner_status || getMetadataText(selectedBatch, ["scanner_status"]))}
              </Descriptions.Item>
              <Descriptions.Item label="Created">{formatDate(selectedBatch.created_at || selectedBatch.submitted_at)}</Descriptions.Item>
              <Descriptions.Item label="Submitted">{formatDate(selectedBatch.submitted_at)}</Descriptions.Item>
              <Descriptions.Item label="Completed">{formatDate(selectedBatch.completed_at)}</Descriptions.Item>
              <Descriptions.Item label="Last synced">{formatDate(selectedBatch.last_synced_at)}</Descriptions.Item>
              <Descriptions.Item label="Updated">{formatDate(selectedBatch.updated_at)}</Descriptions.Item>
            </Descriptions>

            <Descriptions bordered size="small" column={1} title="Targets">
              <Descriptions.Item label="Total">{formatNumber(selectedBatch.target_count)}</Descriptions.Item>
              <Descriptions.Item label="Accepted">{formatNumber(selectedBatch.accepted_targets)}</Descriptions.Item>
              <Descriptions.Item label="Rejected">{formatNumber(selectedBatch.rejected_targets)}</Descriptions.Item>
              <Descriptions.Item label="Queued jobs">{formatNumber(selectedBatch.queued_jobs)}</Descriptions.Item>
              <Descriptions.Item label="Running jobs">{formatNumber(selectedBatch.running_jobs)}</Descriptions.Item>
              <Descriptions.Item label="Succeeded jobs">{formatNumber(selectedBatch.succeeded_jobs)}</Descriptions.Item>
              <Descriptions.Item label="Failed jobs">{formatNumber(selectedBatch.failed_jobs)}</Descriptions.Item>
              <Descriptions.Item label="Skipped jobs">{formatNumber(selectedBatch.skipped_jobs)}</Descriptions.Item>
            </Descriptions>

            <Descriptions bordered size="small" column={1} title="Results">
              <Descriptions.Item label="Confirmed open">
                {formatNumber(selectedBatch.result_counts?.confirmed_open)}
              </Descriptions.Item>
              <Descriptions.Item label="Needs rescan">
                {formatNumber(selectedBatch.result_counts?.unknown_or_filtered)}
              </Descriptions.Item>
              <Descriptions.Item label="Skipped">{formatNumber(selectedBatch.result_counts?.skipped)}</Descriptions.Item>
            </Descriptions>

            <Descriptions bordered size="small" column={1} title="Callbacks and cancel">
              <Descriptions.Item label="Latest callback">{getDetailValue(selectedBatch.latest_callback_status)}</Descriptions.Item>
              <Descriptions.Item label="Latest callback at">{formatDate(selectedBatch.latest_callback_at)}</Descriptions.Item>
              <Descriptions.Item label="Callback failures">
                {formatNumber(selectedBatch.callback_failure_count)}
              </Descriptions.Item>
              <Descriptions.Item label="Latest callback error">
                {getDetailValue(selectedBatch.latest_callback_error || getMetadataText(selectedBatch, ["latest_callback_error", "callback_error"]))}
              </Descriptions.Item>
              <Descriptions.Item label="Cancel requested">{formatDate(selectedBatch.cancel_requested_at)}</Descriptions.Item>
              <Descriptions.Item label="Cancel request ID">
                {getDetailValue(selectedBatch.cancel_request_id || getMetadataText(selectedBatch, ["cancel_request_id"]))}
              </Descriptions.Item>
              <Descriptions.Item label="Cancel requested by">
                {getDetailValue(selectedBatch.cancel_requested_by_name)}
              </Descriptions.Item>
              <Descriptions.Item label="Canceled">{formatDate(selectedBatch.canceled_at)}</Descriptions.Item>
              <Descriptions.Item label="Cancel status">
                {getDetailValue(selectedBatch.latest_cancel_status || getMetadataText(selectedBatch, ["latest_cancel_status", "cancel_status"]))}
              </Descriptions.Item>
              <Descriptions.Item label="Cancel error">
                {getDetailValue(selectedBatch.cancel_error || getMetadataText(selectedBatch, ["cancel_error"]))}
              </Descriptions.Item>
            </Descriptions>

            {selectedBatch.scanner_error ? (
              <Alert type="error" showIcon message="Scanner error" description={selectedBatch.scanner_error} />
            ) : null}
          </Space>
        ) : null}
      </Drawer>

      <Modal
        title="Create port exposure scan"
        open={createOpen}
        confirmLoading={creating}
        onCancel={() => setCreateOpen(false)}
        onOk={() => form.submit()}
        okText="Create scan"
        okButtonProps={{ disabled: !canCreate }}
      >
        <Form
          form={form}
          layout="vertical"
          initialValues={{ source: "caasm_ports", target_scope: "all_public_ports" }}
          onFinish={(values) => void createScan(values)}
        >
          <Form.Item name="source" label="Target source" rules={[{ required: true }]}>
            <Select
              options={[
                { label: "CAASM Ports", value: "caasm_ports" },
              ]}
            />
          </Form.Item>
          <Form.Item name="target_scope" label="Scan scope" rules={[{ required: true }]}>
            <Radio.Group
              options={[
                { label: "Selected CAASM ports", value: "selected_ports" },
                { label: "All public ports", value: "all_public_ports" },
              ]}
            />
          </Form.Item>
          {targetScope === "all_public_ports" ? (
            <Alert
              type="info"
              showIcon
              style={{ marginBottom: 16 }}
              message="All public ports will be scanned"
              description="The backend will resolve every public CAASM port. Ports outside the current scanner transport scope are kept in the batch as rejected targets."
            />
          ) : (
            <Form.Item
              name="target_ids"
              label="Target IDs"
              rules={[{ required: true, message: "Enter at least one target ID." }]}
              extra="Use CAASM port IDs for CAASM Ports, separated by commas or spaces."
            >
              <Input.TextArea rows={4} placeholder="1024, 1025, 1026" />
            </Form.Item>
          )}
          <Form.Item name="note" label="Operator note">
            <Input placeholder="Optional context for this scan" />
          </Form.Item>
          <Alert
            type="warning"
            showIcon
            message="Backend validates scanner scope"
            description="All public CAASM ports are considered. Valid public IPv4 targets are submitted with their CAASM protocol preserved; invalid or scanner-rejected rows are recorded as rejected targets."
          />
        </Form>
      </Modal>
    </Space>
  );
}
