#ifndef CONFIG_H
#define CONFIG_H

#include <cstdint>

// ============================================================================
// Tier-1 RID Sensor Firmware — Configuration
// Hardware: Heltec WiFi LoRa V4 (ESP32-S3 + SX1262)
// ============================================================================

// --- Firmware version ---
#ifndef SENSOR_FW_VERSION
#define SENSOR_FW_VERSION "1.0.0"
#endif

// --- Sensor identity ---
// Unique 4-byte sensor ID. Override per-device via NVS or build flag.
#ifndef DEFAULT_SENSOR_ID
#define DEFAULT_SENSOR_ID 0x00000001
#endif
#ifndef SENSOR_NAME
#define SENSOR_NAME "T1-default"
#endif

// ===========================================================================
// Hardware pin definitions — BOARD CONDITIONAL
//
// Default (no board flag) = Heltec WiFi LoRa V3/V4, byte-identical to what
// every Spencer/Gateway unit has always been built with. Define
// PDL_BOARD_TDECK to select the LilyGo T-Deck Plus instead.
//
// These MUST stay conditional rather than forked into a second config: the
// two boards collide on real pins, so a copy-paste would silently break one
// of them.
//   - GPIO 9  : Heltec LoRa SCK  vs  T-Deck LoRa CS
//   - GPIO 13 : LoRa BUSY on both (the only lucky overlap)
//   - GPIO 43/44 : Heltec USB-serial console  vs  T-Deck Plus GPS UART
//   - GPIO 35/36/37 : Heltec LED / Vext  vs  T-Deck OCTAL PSRAM DATA LINES
//   - GPIO 1  : Heltec battery ADC  vs  T-Deck trackball
//
// That fourth one is the dangerous one. The T-Deck is an ESP32-S3R8 with
// 8 MB OCTAL PSRAM, and octal PSRAM consumes GPIO 35/36/37. Driving them as
// an LED or a Vext rail — which the Heltec build does unconditionally — would
// corrupt PSRAM access on a board whose entire advantage is having PSRAM.
// Hence PDL_HAS_LED / PDL_HAS_VEXT below rather than remapping to some other
// pin: the T-Deck simply has neither peripheral, and the code must skip them.
// ===========================================================================

#ifdef PDL_BOARD_TDECK
// --------------------------- LilyGo T-Deck Plus ---------------------------
// Pinout per LilyGo wiki (wiki.lilygo.cc/products/t-deck-series/t-deck/).
// Silicon confirmed on the physical units 2026-08-02 via `esptool flash-id`:
// ESP32-S3 rev v0.2, "Embedded PSRAM 8MB (AP_3v3)", 16 MB flash.

// SX1262 LoRa radio
#define LORA_SS       9    // NSS / CS
#define LORA_DIO1     45   // DIO1 / IRQ
#define LORA_RST      17   // NRST
#define LORA_BUSY     13   // BUSY

// Shared SPI bus — LoRa, ST7789 display and the microSD slot all sit on it.
#define LORA_SCK      40
#define LORA_MOSI     41
#define LORA_MISO     38

// Board power enable. NOTHING on this board powers up until GPIO10 is driven
// HIGH — not the radio, not the display, not the keyboard. Missing this looks
// exactly like dead hardware.
#define TDECK_POWERON_PIN 10

// ST7789 display (320x240), on the shared SPI bus above.
#define TDECK_TFT_CS   12
#define TDECK_TFT_DC   11
#define TDECK_TFT_BL   42

// BlackBerry-style keyboard: separate I2C bus, driven by an on-board ESP32-C3.
#define TDECK_KB_SDA   18
#define TDECK_KB_SCL   8
#define TDECK_KB_ADDR  0x55
#define TDECK_KB_INT   46

// Trackball (4 direction lines). Note GPIO1 here is NOT a battery ADC.
#define TDECK_TRACKBALL_UP    3
#define TDECK_TRACKBALL_DOWN  15
#define TDECK_TRACKBALL_LEFT  1
#define TDECK_TRACKBALL_RIGHT 2

#define TDECK_SD_CS    39

// GPS (T-Deck *Plus* only) — u-blox on its own UART.
#define TDECK_GPS_TX   43
#define TDECK_GPS_RX   44

// No user LED and no switchable Vext rail on this board. See the octal-PSRAM
// warning above for why these are absent rather than remapped.
// (PDL_HAS_LED / PDL_HAS_VEXT intentionally left undefined.)

// Battery sense. FLAGGED AS UNVERIFIED: not listed in the wiki pinout table I
// worked from, and GPIO1 (the Heltec value) is a trackball line here, so the
// Heltec default is definitely wrong. Reading the wrong pin yields a bogus
// battery percentage — cosmetic, not destructive — but do not trust the
// reported battery level on a T-Deck until this is measured on the bench.
#define BATT_ADC_PIN  4
#define BATT_ADC_DIVIDER 2.0f
#define BATT_FULL_MV  4200
#define BATT_EMPTY_MV 3200

#else
// ------------------------- Heltec WiFi LoRa V3/V4 -------------------------
// Unchanged values, unchanged behaviour. Measured after the T-Deck split:
// Spencer03 built 2,403,233 bytes vs 2,403,629 before — 396 bytes smaller,
// from the preprocessor restructuring, NOT byte-identical. The emitted logic
// is the same (PDL_LED_WRITE expands to the same digitalWrite, and the Heltec
// branches are untouched), but do not claim bit-for-bit equivalence.

// SX1262 LoRa radio pins
#define LORA_SS       8    // NSS / CS
#define LORA_DIO1     14   // DIO1 / IRQ
#define LORA_RST      12   // NRST
#define LORA_BUSY     13   // BUSY

// SPI bus (VSPI on ESP32-S3)
#define LORA_SCK      9
#define LORA_MOSI     10
#define LORA_MISO     11

// On-board LED
#define PDL_HAS_LED   1
#define LED_PIN       35

// Battery ADC (voltage divider)
#define BATT_ADC_PIN  1
#define BATT_ADC_DIVIDER 2.0f   // voltage divider ratio
#define BATT_FULL_MV  4200
#define BATT_EMPTY_MV 3200

// Vext power control (for peripherals)
#define PDL_HAS_VEXT  1
#define VEXT_PIN      36

#endif // PDL_BOARD_TDECK

// ---------------------------------------------------------------------------
// Build identity.
//
// SENSOR_FW_VERSION alone ("1.0.0") is useless for telling units apart — every
// image built tonight carried the same string while behaving very differently.
// That caused real confusion: Gateway01 and Spencer02 silently ran an older
// image than the rest, and the only way to tell was to observe which BUG they
// still had (bare DIRECT_WIFI_BRIDGE gateway_id, MQTT never publishing).
//
// PDL_BUILD_ID is a hash of the SOURCE TREE, injected by scripts/build_id.py.
//
// It deliberately replaces an earlier __DATE__ " " __TIME__ stamp. That
// captured COMPILE time, and because every PlatformIO environment compiles
// separately, one unchanged source tree produced six different stamps across
// six units — making the fleet-divergence alert fire permanently. Hashing the
// source gives the property actually wanted: same code, same id, on every
// board regardless of when each was built.
//
// Falls back to a literal when built without the script (e.g. the native test
// env) so the field is always present rather than failing to compile.
// ---------------------------------------------------------------------------
#ifndef PDL_BUILD_ID
#  define PDL_BUILD_ID "unknown"
#endif
#define PDL_BUILD_STAMP  PDL_BUILD_ID

// Board type string reported to C2 in the health payload. This was hardcoded
// "HELTEC_V4" for every unit, so V3s and T-Decks misreported their own
// hardware — which matters operationally, because a V3 has no PSRAM and
// cannot run models while a V4 or T-Deck can.
#ifdef PDL_BOARD_TDECK
#  define PDL_BOARD_TYPE "LILYGO_TDECK_PLUS"
#elif defined(PDL_BOARD_HELTEC_V3)
#  define PDL_BOARD_TYPE "HELTEC_V3"
#else
#  define PDL_BOARD_TYPE "HELTEC_V4"
#endif

// LED helpers. On a board with no user LED (T-Deck) these compile to nothing,
// which keeps the ~5 blink call sites in main.cpp free of #ifdef clutter and,
// more importantly, guarantees we never drive GPIO35 — an octal-PSRAM data
// line on that board. Arduino's digitalWrite() is NOT safe to call with an
// invalid pin here, so "define LED_PIN to something harmless" is not an
// option; the calls have to actually disappear.
#ifdef PDL_HAS_LED
#  define PDL_LED_WRITE(v)  digitalWrite(LED_PIN, (v))
#  define PDL_LED_TOGGLE()  digitalWrite(LED_PIN, !digitalRead(LED_PIN))
#else
#  define PDL_LED_WRITE(v)  do { } while (0)
#  define PDL_LED_TOGGLE()  do { } while (0)
#endif

// --- LoRa radio configuration ---
#define LORA_FREQ_MHZ       915.0f
#define LORA_BANDWIDTH_KHZ  125.0f
#define LORA_SPREAD_FACTOR  10
#define LORA_CODING_RATE    5       // 4/5
#define LORA_TX_POWER_DBM   22
#define LORA_SYNC_WORD      0x34    // custom mesh sync word
#define LORA_PREAMBLE_LEN   8

// --- Mesh protocol ---
#define MESH_MAX_HOPS       3
#define MESH_TTL_DEFAULT    3

// --- TFLite classifier (legacy single-model — still used by rf_classifier.h) ---
#define MODEL_PATH          "/models/drone_classifier.tflite"
#define TFLITE_ARENA_SIZE   (32 * 1024)   // 32 KB inference arena in PSRAM
#define SPECTRUM_BINS       128           // 128 frequency bins (2.4 GHz band)
#define NUM_CLASSES         7             // noise, ocusync, fpv_fhss, skydio, unknown_drone, wifi, bluetooth

// Classification thresholds
#define CONFIDENCE_THRESHOLD     0.65f    // minimum confidence to trigger FFPS candidate
#define NOISE_SUPPRESS_THRESHOLD 0.90f    // if noise class > this, skip entirely

// --- Multi-Model Manager ---
#define MODEL_MAX_SLOTS          8        // maximum concurrent model registrations
#define MODEL_NAME_MAX_LEN       24       // max chars in model name
#define MODEL_MAX_OUTPUT_CLASSES  16      // max output classes any single model can have
#define MODEL_ARENA_SIZE_PER_SLOT (32 * 1024)  // 32 KB arena per model slot

// PSRAM budget allocation (ESP32-S3 has 8 MB PSRAM)
// Total 8192 KB: models+arenas get 4 MB, OTA double-buffer gets 1 MB, rest for heap
#define MODEL_PSRAM_BUDGET       (4 * 1024 * 1024)   // 4 MB for model manager
#define OTA_PSRAM_BUDGET         (1 * 1024 * 1024)    // 1 MB for OTA double-buffer

// Paging confidence: specialist models page-in when triage confidence exceeds this
#define PAGING_CONFIDENCE_THRESHOLD 0.55f

// --- Model SPIFFS paths ---
#define MODEL_PATH_DRONE_CLASSIFIER  "/models/drone_classifier.tflite"
#define MODEL_PATH_RF_ATTACK         "/models/rf_attack_classifier.tflite"
#define MODEL_PATH_MAVLINK_ANOMALY   "/models/mavlink_anomaly.tflite"
#define MODEL_PATH_LORA_MESH_ATTACK  "/models/lora_mesh_attack.tflite"
#define MODEL_PATH_PROTOCOL_ANOMALY  "/models/protocol_anomaly.tflite"

// Cactus full_mode .cactus model
#define MODEL_PATH_THREAT_CACTUS     "/models/threat.cactus"
// Filesystem now LittleFS (SPIFFS-named API in source kept for compatibility).

// Markov RID baseline state table
#define MARKOV_BASELINE_PATH         "/models/markov_baseline.bin"

// --- Model input/output dimensions ---
// Drone classifier: 128 spectrum bins → 7 classes
#define MODEL_DRONE_INPUT_SIZE    128
#define MODEL_DRONE_OUTPUT_CLASSES 7

// RF attack classifier: 128 spectrum bins → 8 classes
#define MODEL_RF_ATTACK_INPUT_SIZE    128
#define MODEL_RF_ATTACK_OUTPUT_CLASSES 8

// MAVLink anomaly detector: 128 features → 4 classes
#define MODEL_MAVLINK_INPUT_SIZE      128
#define MODEL_MAVLINK_OUTPUT_CLASSES  4

// LoRa mesh attack detector: 128 features → 5 classes
#define MODEL_LORA_MESH_INPUT_SIZE    128
#define MODEL_LORA_MESH_OUTPUT_CLASSES 5

// Protocol anomaly detector: 128 features → 6 classes
#define MODEL_PROTOCOL_INPUT_SIZE     128
#define MODEL_PROTOCOL_OUTPUT_CLASSES 6

// --- RF Attack classifier class labels ---
// (separate from the drone ClassLabel enum — these are for the rf_attack model)
#define ATTACK_CLASS_NONE       0
#define ATTACK_CLASS_DEAUTH     1
#define ATTACK_CLASS_EVIL_TWIN  2
#define ATTACK_CLASS_JAMMING    3
#define ATTACK_CLASS_REPLAY     4
#define ATTACK_CLASS_PORT_SCAN  5
#define ATTACK_CLASS_GPS_SPOOF  6
#define ATTACK_CLASS_C2_BEACON  7

// --- Threat Correlator ---
#define CORRELATOR_BUFFER_SIZE          64       // rolling detection buffer entries
#define CORRELATOR_WINDOW_MS            30000    // 30-second rolling window
#define CORRELATOR_MIN_CONFIDENCE       0.50f    // minimum confidence to enter buffer
#define CORRELATOR_DEDUP_MS             10000    // don't re-emit same composite within 10s
#define CORRELATOR_MAX_THREATS_PER_CYCLE 4       // max composites per correlation run

// Correlation rule time windows
#define CORR_RULE_CRED_HARVEST_WINDOW_MS     5000   // deauth + evil_twin
#define CORR_RULE_DRONE_HIJACK_WINDOW_MS     10000  // drone + gps_spoof
#define CORR_RULE_MESH_COMPROMISE_WINDOW_MS  5000   // jamming + replay
#define CORR_RULE_ACTIVE_INTRUSION_WINDOW_MS 10000  // c2_beacon + any_attack

// --- Composite threat classifications ---
enum CompositeThreatClass : uint8_t {
    COMPOSITE_CREDENTIAL_HARVEST = 0,  // deauth + evil_twin
    COMPOSITE_DRONE_HIJACK       = 1,  // drone_detected + gps_spoof
    COMPOSITE_MESH_COMPROMISE    = 2,  // jamming + replay
    COMPOSITE_ACTIVE_INTRUSION   = 3,  // c2_beacon + any_attack
    COMPOSITE_CLASS_COUNT        = 4
};

// Base offset for composite class labels in DetectionEvent.class_label
// Allows distinguishing composite threats from individual model classes
#define COMPOSITE_CLASS_BASE  0x80

// --- FFPS (Federated False-Positive Suppression) ---
#define FFPS_CORROBORATION_WINDOW_MS  2000   // 2 second window
#define FFPS_MIN_CORROBORATORS        1      // K: need at least 1 other sensor to corroborate
#define FFPS_MAX_CORROBORATORS        5      // N: max sensors in local cluster
#define FFPS_CANDIDATE_TTL_MS         3000   // candidate expires after 3s

// --- Health reporting ---
#define HEALTH_REPORT_INTERVAL_MS     60000  // every 60 seconds

// --- Main loop timing ---
#define MAIN_LOOP_PERIOD_MS           1000   // target 1 Hz scan cycle
#define WIFI_SCAN_DWELL_MS            100    // per-channel dwell time for RF scan

// --- Blue Force (friendly whitelist) ---
#define BLUE_FORCE_MAX_ENTRIES        32     // max whitelisted drone serial numbers

// --- OTA model update ---
#define OTA_MODEL_SLOT_SIZE           (512 * 1024)  // 512 KB per model slot in PSRAM
#define OTA_DOUBLE_BUFFER             true

// --- Packet format message types ---
// NOTE: 0x05/0x06 are reserved for the T2->T1 command/ack protocol and MUST
// match what T2's sensor_commander.py sends on the wire. OTA_CHUNK and
// BLUE_FORCE were renumbered to 0x08/0x09 when the command channel was added.
#define MSG_TYPE_DETECTION    0x01
#define MSG_TYPE_CANDIDATE    0x02
#define MSG_TYPE_CORROBORATE  0x03
#define MSG_TYPE_HEALTH       0x04
#define MSG_TYPE_COMMAND      0x05  // T2 -> T1 command packet (32 bytes)
#define MSG_TYPE_CMD_ACK      0x06  // T1 -> T2 command acknowledgement (16 bytes)
#define MSG_TYPE_COMPOSITE    0x07  // composite threat from correlator
#define MSG_TYPE_OTA_CHUNK    0x08  // was 0x05; moved to clear room for COMMAND
#define MSG_TYPE_BLUE_FORCE   0x09  // was 0x06; moved to clear room for CMD_ACK
#define MSG_TYPE_WFX_FEATURES 0x10  // T1 -> T2: WaveForge L0/L1 feature vector
#define MSG_TYPE_SPOOF_SCORE  0x11  // T2 -> T1: per-track rid_spoof_gate score
#define MSG_TYPE_PEC_FEEDBACK 0x12  // T2 -> T1: PEC-cleaned class probability vector
#define MSG_TYPE_BEHAVIORAL_NLL 0x13 // T1 -> T2: Markov behavioral NLL telemetry
#define MSG_TYPE_PAYLOAD_CHUNK  0x14 // T2 -> T1: payload chunk for active delivery
#define MSG_TYPE_NETWORK_LIST   0x15 // T1 -> T2: observed 802.11 networks
#define MSG_TYPE_PAYLOAD_ACK    0x16 // T1 -> T2: delivery result + audit hash
#define MSG_TYPE_ENROLLMENT     0x17 // T1 -> T2: first-boot self-registration
#define MSG_TYPE_ENROLLMENT_RESPONSE 0x18 // T2 -> T1: assigned ID + position
#define MSG_TYPE_TIME_SYNC      0x19 // T2 -> T1: UTC epoch broadcast
#define MSG_TYPE_JAMMING_ALERT  0x1A // T1 -> T2: counter-RF / jamming alarm
#define MSG_TYPE_FED_UPDATE     0x1B // T2 -> T1: federated weight-delta chunk
#define MSG_TYPE_COGNITIVE_RF   0x1C // T1 -> T2: per-channel EWMA baselines
#define MSG_TYPE_AUTH_TOKEN     0x1D // T2 -> T1: Ed25519 token for DELIVER_PAYLOAD
#define MSG_TYPE_PAYLOAD_CHUNK_ACK 0x94 // T1 -> T2: per-chunk ACK (0x14 | 0x80)
#define MSG_TYPE_C2_PROXY_REQ   0x24 // T1 -> T2: chunked HTTP request envelope (BLE-tunnel)
#define MSG_TYPE_C2_PROXY_RSP   0x25 // T2 -> T1: chunked HTTP response envelope (BLE-tunnel)

// ----- Interop (Meshtastic / RNode / MQTT) extensions -----
// These are added by the interop layer (radio_scheduler + meshtastic_emit +
// rnode_bridge + mqtt_client + interop_router). Wire format documented in
// INTEROP_INTEGRATION_NOTES.md.
#define MSG_TYPE_INTEROP_RX  0x21  // T1 -> T2 / T1 -> T1: inbound MT/RN packet
#define MSG_TYPE_INTEROP_TX  0x22  // T2 -> T1: emit MT/RN packet
#define MSG_TYPE_RADIO_MODE  0x23  // T2 -> T1: change time-slice ratios at runtime

// --- Detection packet size ---
#define DETECTION_PACKET_SIZE 33   // bytes (see packet format spec)

// --- Class labels ---
enum ClassLabel : uint8_t {
    CLASS_NOISE         = 0,
    CLASS_OCUSYNC       = 1,
    CLASS_FPV_FHSS      = 2,
    CLASS_SKYDIO        = 3,
    CLASS_UNKNOWN_DRONE = 4,
    CLASS_WIFI          = 5,
    CLASS_BLUETOOTH     = 6,
    // Appended (not renumbered) — a decoded ASTM F3411/OpenDroneID BLE
    // broadcast (see ble_odid_scanner.cpp). Unlike 0-6, which are TFLite
    // classifier outputs over an RF spectrum, this is direct message
    // content: an actual serial number and/or GPS position read off the
    // drone's own Remote ID broadcast, not an inference.
    CLASS_RID_BLE_DECODED = 7,
    // A decoded ASTM F3411/OpenDroneID WiFi Beacon vendor-IE broadcast
    // (see wifi_odid_scanner.cpp). Same "direct message content, not an
    // inference" rationale as CLASS_RID_BLE_DECODED.
    CLASS_RID_WIFI_DECODED = 8
};

// --- Detection flags ---
#define FLAG_PROMOTED     0x01  // bit 0: promoted through FFPS
#define FLAG_BLUE_FORCE   0x02  // bit 1: matched blue force whitelist
#define FLAG_SUPPRESSED   0x04  // bit 2: suppressed by FFPS

// --- Detection event structure ---
struct DetectionEvent {
    uint8_t  msg_type;
    uint32_t sensor_id;
    uint32_t timestamp_utc;
    uint8_t  class_label;
    uint8_t  confidence;     // 0-255 mapped to 0.0-1.0
    int16_t  rssi;
    int16_t  noise_floor;
    uint8_t  signature_hash[8];
    int32_t  lat_fixed;      // fixed-point latitude  (deg * 1e7)
    int32_t  lon_fixed;      // fixed-point longitude (deg * 1e7)
    uint8_t  battery_pct;
    uint8_t  flags;
    // Decoded Remote-ID serial of the DRONE (ASTM F3411 Basic ID), empty when
    // this detection came from an RF/no-serial path. NOT part of the 33-byte
    // LoRa wire format (mesh_serialize_event packs only the fields above) — it
    // is carried only on the direct-WiFi C2 path as platform_id, so the map can
    // identify WHICH drone was seen, not just "a drone".
    char     drone_serial[21];
};

#endif // CONFIG_H
