#ifndef PEC_FEEDBACK_H
#define PEC_FEEDBACK_H

// ============================================================================
// PEC Feedback Client — T1 consumer of gateway mitigation.py PEC vectors
//
// The T2 gateway (pdl_platform/pdl_quantum/mitigation.py) applies
// Probabilistic Error Cancellation to raw model outputs and broadcasts a
// cleaned class-probability vector over LoRa mesh as MSG_TYPE_PEC_FEEDBACK
// (0x12).
//
// This module maintains a 4-entry LRU cache (one per active track) of the
// last received PEC probability vector, expiring entries after 30 seconds.
// threat_correlator.cpp can blend these corrected probs in place of raw
// on-device softmax when available.
//
// Wire-up (do NOT edit existing files — see SPOOF_PEC_INTEGRATION_NOTES.md):
//   - config.h: add #define MSG_TYPE_PEC_FEEDBACK 0x12
//   - main.cpp setup(): call pec_feedback_init();
//   - main.cpp loop() dispatch switch: add
//       case MSG_TYPE_PEC_FEEDBACK:
//           pec_feedback_handle_packet(rx_buf, rx_len);
//           break;
//   - threat_correlator.cpp correlator_run(): overlay PEC probs (see notes)
// ============================================================================

#include <cstdint>
#include <cstddef>

// Maximum number of class probabilities in one PEC message (matches
// MODEL_MAX_OUTPUT_CLASSES from config.h — redefined here so pec_feedback.h
// is self-contained for unit tests that don't include config.h).
#ifndef MODEL_MAX_OUTPUT_CLASSES
#define MODEL_MAX_OUTPUT_CLASSES 16
#endif

// Cache configuration (static memory only)
#define PEC_FEEDBACK_CACHE_SIZE    4u      // max concurrent tracks
#define PEC_FEEDBACK_ENTRY_TTL_MS  30000u  // entries expire after 30 seconds

// ---------------------------------------------------------------------------
// On-wire message layout (20 bytes, packed, little-endian)
// ---------------------------------------------------------------------------
// Byte 0       : msg_type = MSG_TYPE_PEC_FEEDBACK (0x12) — consumed by main
// Byte 1       : num_classes  (1..MODEL_MAX_OUTPUT_CLASSES)
// Bytes 2-17   : class_probs_q8[16]  (q8 = prob * 255; unused slots = 0)
// Bytes 18-21  : source_track_id (uint32_t LE)
//
// Total on-wire: 22 bytes (1 msg_type + sizeof(PecFeedbackMsg))
// ---------------------------------------------------------------------------
struct PecFeedbackMsg {
    uint8_t  num_classes;                           // 1..MODEL_MAX_OUTPUT_CLASSES
    uint8_t  class_probs_q8[MODEL_MAX_OUTPUT_CLASSES]; // q8 probabilities
    uint32_t source_track_id;
} __attribute__((packed));

// Minimum accepted packet length (bytes):
//   1 (msg_type) + 1 (num_classes) + MODEL_MAX_OUTPUT_CLASSES (probs) + 4 (track_id)
// Defined as a compile-time integer rather than sizeof(PecFeedbackMsg) because
// compiler struct-padding rules can differ across toolchains; the on-wire
// format is always the fixed layout above.
#define PEC_FEEDBACK_MSG_MIN_LEN (1u + 1u + MODEL_MAX_OUTPUT_CLASSES + 4u)

// ---------------------------------------------------------------------------
// Public API
// ---------------------------------------------------------------------------

// Call once from setup().
void pec_feedback_init();

// Feed a raw LoRa rx_buf of length len.  The first byte is msg_type (0x12).
// Silently ignores malformed or undersized packets.
void pec_feedback_handle_packet(const uint8_t* buf, size_t len);

// Retrieve the most recent PEC probability vector for track_id.
//   probs_out : caller-allocated array of at least MODEL_MAX_OUTPUT_CLASSES floats
//   n_out     : set to the num_classes field from the cached message
//   max_age_ms: reject entries older than this many milliseconds
// Returns true if a valid, non-expired entry exists and probs_out was filled.
// Returns false if track unknown, expired, or older than max_age_ms.
bool pec_feedback_get_latest(uint32_t track_id,
                             float*   probs_out,
                             uint8_t* n_out,
                             uint32_t max_age_ms);

#endif // PEC_FEEDBACK_H
