#ifndef PDL_RADIO_ABSTRACTION_H
#define PDL_RADIO_ABSTRACTION_H

#include <cstdint>
#include <cstddef>

// ============================================================================
// Radio abstraction layer
//
// A T1 sensor may carry multiple radios (primary 915 MHz LoRa via SX1262,
// plus optional daughterboards on CC1101 for sub-GHz alternates, nRF24L01+
// for 2.4 GHz, ESP32-C5 for 5.8 GHz). This layer provides a uniform RX/TX
// API so the band_bridge module can relay frames between any pair of
// available bands without knowing the driver details.
//
// Compile-time include: only the radios the sensor is actually built with
// get their full drivers linked in. Stubbed interfaces return false + a
// "not supported" flag for bands that were not compiled in.
//
// Gated by PDL_ENABLE_BAND_BRIDGE (in platformio.ini or INTEGRATION doc).
// ============================================================================

enum class BandId : uint8_t {
    LORA_433   = 0,
    LORA_868   = 1,
    LORA_915   = 2,
    WIFI_2G4   = 3,
    NRF24_2G4  = 4,
    ESP32_5G   = 5,
    CC1101_433 = 6,
    CC1101_868 = 7,
    CC1101_915 = 8,
    BAND_COUNT = 9,
};

struct RadioInterface {
    BandId     band;
    bool       (*init)();
    bool       (*tx)(const uint8_t* buf, size_t len, uint16_t timeout_ms);
    bool       (*rx)(uint8_t* buf, size_t* len, uint16_t timeout_ms);
    int        (*rssi_last)();
    const char* name;
};

// Compile-time registry (declared in the .cpp). Entries for non-built radios
// have null fn pointers and radio_available() skips them.
extern RadioInterface g_radios[(size_t)BandId::BAND_COUNT];

// ---------------------------------------------------------------------------
// Initialize the primary LoRa 915 interface by adopting the existing mesh
// radio (SX1262 via RadioLib from mesh_publish.cpp). Must be called from
// setup() AFTER mesh_init() has succeeded. Other radios (CC1101, nRF24,
// ESP32-C5) are initialized lazily on first use and only if their build
// flag is defined.
// ---------------------------------------------------------------------------
void radio_abstraction_init();

// ---------------------------------------------------------------------------
// Populate `out` with all bands that have a live driver. Returns the count.
// `max` bounds the write.
// ---------------------------------------------------------------------------
size_t radio_available(BandId* out, size_t max);

// ---------------------------------------------------------------------------
// Fan-out TX helper. Selects the radio for `band` and calls its tx().
// Returns false if the band isn't available or TX fails.
// ---------------------------------------------------------------------------
bool radio_tx(BandId band, const uint8_t* buf, size_t len, uint16_t timeout_ms);

// ---------------------------------------------------------------------------
// Duty-cycle tracking. EU 868 / US 915 ISM caps are enforced here so the
// band_bridge never puts a sensor over the limit. Returns false if the TX
// would exceed the 1% (868) / 0.1% (869.4) / unlimited-with-LBT (915) duty
// budget for this hour.
//
// Called internally by radio_tx; exposed here for band_bridge policy pre-check.
// ---------------------------------------------------------------------------
bool radio_duty_ok(BandId band, size_t airtime_ms);
void radio_duty_record(BandId band, size_t airtime_ms);
size_t radio_duty_budget_remaining_ms(BandId band);

// ---------------------------------------------------------------------------
// Compile-time guard for test-only radios. A test build can register a
// mock driver by providing its own init / tx / rx in the registry.
// ---------------------------------------------------------------------------
void radio_register_mock(BandId band, const RadioInterface& iface);

#endif // PDL_RADIO_ABSTRACTION_H
