#ifndef WIFI_EXTENDED_SCAN_H
#define WIFI_EXTENDED_SCAN_H

// ============================================================================
// wifi_extended_scan.h — Additional passive WiFi-space capabilities
// (ADDITIVE, whitehat/defensive only — same discipline as
// wifi_security_scan.cpp: no injection, no active attacks, observes only).
//
// Five capabilities, each independently scoped and honestly caveated:
//
//   1. RTT/802.11mc ranging detection -- flags nearby devices performing
//      Fine Timing Measurement (FTM) ranging (802.11 Radio Measurement
//      Action frames). Purely observational: "ranging activity seen from
//      BSSID X," not a range estimate itself (this sensor doesn't
//      participate in the FTM exchange, only observes it happening).
//
//   2. KRACK / PMKID-harvest detection -- two distinct, real signatures
//      from EAPOL 4-way-handshake frames:
//        - KRACK: same (BSSID, client, nonce) tuple seen in EAPOL Message-3
//          twice -- a legitimate AP retransmit increments the replay
//          counter; a forced nonce-reuse replay (the actual KRACK attack)
//          does not.
//        - PMKID harvest: an unusual burst of EAPOL Message-1 frames from
//          one BSSID within a short window, well above the rate a real
//          population of clients organically reconnecting would produce.
//      HONEST LIMITATION: cannot decrypt EAPOL Message-2/3 payloads
//      (needs the PSK), so this detects the ATTACK PATTERN (replay,
//      burst rate) from unencrypted EAPOL header fields, not the captured
//      credential material itself -- this sensor never stores or forwards
//      anything that could itself be used to crack a network.
//
//   3. WPA3 downgrade detection -- if a BSSID is ever observed advertising
//      SAE (WPA3) in its RSN AKM suite, and a LATER beacon/probe-response
//      from the SAME BSSID advertises only PSK (WPA2) with SAE gone, that
//      is the classic downgrade-attack signature (forcing weaker auth to
//      enable offline cracking).
//
//   4. Rogue DHCP / DNS hijack detection -- HONEST LIMITATION: only
//      possible on OPEN (unencrypted) networks this sensor can passively
//      decode; WPA2/3-encrypted DHCP/DNS traffic is opaque without the
//      network key, same wall as capability 2. On open networks: flags
//      multiple DHCP servers answering the same transaction (rogue DHCP)
//      and multiple differing DNS answers for the same query within a
//      short window (DNS spoofing/hijack).
//
//   5. Channel-hop sweep -- FOUND DURING REVIEW, before this ever shipped:
//      main.cpp's capture_rf_spectrum() (the drone_classifier's spectrum
//      input) already sweeps all 13 2.4GHz channels every single ~1 Hz
//      main-loop cycle and explicitly restores the radio to the AP's
//      channel afterward so an active WiFi STA connection keeps
//      receiving. An independent channel-hop timer in this module would
//      have raced that restore for zero benefit -- this module's frame
//      observation already gets full-channel coverage for free, since
//      its callback fires for whatever channel is active at that instant
//      regardless of which subsystem set it. wifi_ext_next_channel() is
//      kept as a tested pure scheduling utility (in case a future
//      redesign wants this module to own hopping independently of
//      capture_rf_spectrum()) but is NOT wired to drive the live radio --
//      see wifi_extended_scan_tick()'s own comment in the .cpp for the
//      full reasoning. Capability delivered by relying on existing
//      coverage, not by adding a second, conflicting sweep.
//
// All five reuse the SAME promiscuous WiFi capture already running for
// network_id.cpp / wifi_security_scan.cpp -- no additional radio duty
// cycle, only additional frame parsing on frames already being received.
// ============================================================================

#include <cstdint>
#include <cstddef>

void wifi_extended_scan_init();

// Feed a raw 802.11 frame (management OR data) from the existing
// promiscuous callback. IRAM-safe, no malloc, no blocking -- same
// contract as wifi_security_scan_process_frame(). frame_ctl_type/subtype
// let the caller avoid a second frame-control parse. now_ms is the
// caller's millis() timestamp -- passed explicitly (not read internally)
// so the burst/window/replay tracking is deterministic and unit-testable
// without a real clock.
void wifi_extended_scan_process_frame(const uint8_t* frame, size_t len,
                                      uint8_t frame_type, uint8_t frame_subtype,
                                      uint32_t now_ms);

// Call periodically (main loop cadence) -- drives the channel-hop sweep and
// any time-windowed bookkeeping (EAPOL burst window, DNS-answer window).
// Internally no-ops unless the relevant interval has elapsed.
void wifi_extended_scan_tick();

// ---------------------------------------------------------------------------
// 1. RTT / 802.11mc ranging
// ---------------------------------------------------------------------------
// Category 5 = Radio Measurement; FTM uses Public Action (category 4) with
// action codes for FTM Request(32)/FTM(33) per 802.11-2020 9.6.8, and
// Radio Measurement category 5 for some measurement-request variants.
// Pure classifier -- no radio access, directly unit-testable.
bool wifi_ext_is_ftm_action_frame(uint8_t category, uint8_t action_code);

struct RttFinding {
    uint8_t  bssid[6];
    uint32_t last_seen_ms;
    uint32_t observation_count;
};

// ---------------------------------------------------------------------------
// 2. KRACK / PMKID-harvest
// ---------------------------------------------------------------------------
enum EapolHandshakeMsg : uint8_t {
    EAPOL_MSG_UNKNOWN = 0,
    EAPOL_MSG_1 = 1,
    EAPOL_MSG_2 = 2,
    EAPOL_MSG_3 = 3,
    EAPOL_MSG_4 = 4,
};

// Classifies a parsed EAPOL-Key key_info field (802.11-2020 12.7.2) into
// which of the 4-way-handshake's 4 messages it is. Pure, unit-testable.
EapolHandshakeMsg eapol_classify_key_info(uint16_t key_info);

// PMKID-harvest burst threshold: this many (or more) EAPOL Message-1 frames
// from the same BSSID within the window below is anomalous. A real
// population of clients reconnecting organically doesn't cluster this
// tightly; a PMKID-harvest tool requesting from many spoofed/real client
// MACs in rapid succession does.
#define WIFI_EXT_PMKID_BURST_THRESHOLD   5u
#define WIFI_EXT_PMKID_BURST_WINDOW_MS   5000u

struct KrackFinding {
    uint8_t  bssid[6];
    uint8_t  client_mac[6];
    uint32_t detected_at_ms;
};

struct PmkidBurstFinding {
    uint8_t  bssid[6];
    uint32_t msg1_count_in_window;
    uint32_t window_start_ms;
};

// ---------------------------------------------------------------------------
// 3. WPA3 downgrade
// ---------------------------------------------------------------------------
struct Wpa3DowngradeFinding {
    uint8_t  bssid[6];
    char     ssid[33];
    uint32_t sae_last_seen_ms;
    uint32_t downgrade_detected_ms;
};

// ---------------------------------------------------------------------------
// 4. Rogue DHCP / DNS hijack (open networks only -- see header note above)
// ---------------------------------------------------------------------------
struct RogueDhcpFinding {
    uint32_t transaction_xid;
    uint8_t  server_count;         // distinct DHCP servers answering the same xid
    uint32_t detected_at_ms;
};

struct DnsHijackFinding {
    uint16_t query_id;
    uint8_t  distinct_answer_count;
    uint32_t detected_at_ms;
};

// ---------------------------------------------------------------------------
// 5. Channel-hop sweep
// ---------------------------------------------------------------------------
#define WIFI_EXT_CHANNEL_MIN   1u
#define WIFI_EXT_CHANNEL_MAX  13u
#define WIFI_EXT_HOP_INTERVAL_MS  3000u   // dwell per channel before hopping

// Pure scheduling step: given the current channel and elapsed time since
// the last hop, returns the channel that should be active now. Wraps
// 13 -> 1. No radio access -- directly unit-testable.
uint8_t wifi_ext_next_channel(uint8_t current_channel);

// ---------------------------------------------------------------------------
// Stats + findings retrieval
// ---------------------------------------------------------------------------
struct WifiExtendedScanStats {
    uint16_t rtt_devices_observed;
    uint16_t krack_suspects;
    uint16_t pmkid_burst_suspects;
    uint16_t wpa3_downgrade_suspects;
    uint16_t rogue_dhcp_suspects;
    uint16_t dns_hijack_suspects;
    uint8_t  current_channel;
    uint32_t channel_hops_completed;
};

void wifi_extended_scan_get_stats(WifiExtendedScanStats* out);

size_t wifi_ext_list_rtt(RttFinding* out, size_t max);
size_t wifi_ext_list_krack(KrackFinding* out, size_t max);
size_t wifi_ext_list_pmkid_bursts(PmkidBurstFinding* out, size_t max);
size_t wifi_ext_list_wpa3_downgrades(Wpa3DowngradeFinding* out, size_t max);
size_t wifi_ext_list_rogue_dhcp(RogueDhcpFinding* out, size_t max);
size_t wifi_ext_list_dns_hijacks(DnsHijackFinding* out, size_t max);

#endif // WIFI_EXTENDED_SCAN_H
