#ifndef C2_PROXY_TUNNEL_H
#define C2_PROXY_TUNNEL_H

// ============================================================================
// c2_proxy_tunnel.h — BLE↔LoRa C2 API tunnel for the master-of-mesh phone
//
// Purpose
// -------
// The Android PDL Tactical app uses its bonded T1 sensor as a fallback C2
// transport when neither cellular nor local-WiFi to T2 are reachable. The
// phone serializes a tiny HTTP-style envelope:
//
//     { "request_id":  uint32,
//       "method":      "GET" | "POST" | "PUT" | "DELETE",
//       "path":        "/api/...",
//       "headers":     { "Authorization": "Bearer ...", ... },
//       "body":        "<utf-8 string or empty>" }
//
// gzips it, fragments into MSG_TYPE_C2_PROXY_REQ (0x24) chunks, and writes
// each chunk to BLE characteristic ProxyTx (pdl0008). This module accepts
// those writes, forwards them as MSG_TYPE_C2_PROXY_REQ packets over LoRa to
// T2 (which calls OpenForge C2 over HTTPS and returns a gzipped envelope as
// MSG_TYPE_C2_PROXY_RSP (0x25) chunks). Reassembled responses are emitted on
// BLE characteristic ProxyRx (pdl0009) as notify.
//
// Wire format
// -----------
// Both REQ and RSP reuse the EXACT same 24-byte chunk header pattern as
// MSG_TYPE_PAYLOAD_CHUNK (0x14) — see payload_buffer.h for the canonical
// layout:
//   [0]      = 0x24 (REQ) or 0x25 (RSP)
//   [1..4]   = request_id (LE uint32_t — same field as payload_id)
//   [5..6]   = total_chunks (LE uint16_t)
//   [7..8]   = chunk_idx    (LE uint16_t, 0-based)
//   [9..12]  = total_size   (LE uint32_t, gzipped envelope size)
//   [13..16] = chunk_crc32  (LE uint32_t, per-chunk CRC32; final chunk holds
//                            full-payload CRC32 — same dual-meaning rule as
//                            payload_buffer)
//   [17..23] = reserved (7 bytes)
//   [24..]   = chunk data (variable, ≤ PAYLOAD_CHUNK_MAX_DATA bytes)
//
// Reassembly is delegated to a small in-module ring of slots (independent of
// payload_buffer.cpp, so the existing C2-payload delivery path is untouched
// even when a phone is mid-tunnel). The scheme is identical so behaviour can
// be reasoned about uniformly: equal-sized chunks except possibly the last,
// CRC32 per chunk, full-payload CRC32 in final chunk's chunk_crc32 field.
//
// Coexistence
// -----------
//   * BLE callbacks run on the NimBLE task; this module never calls into
//     RadioLib or ble_companion from a callback. NimBLE writes are queued and
//     drained in c2_proxy_tunnel_tick() on the main loop core (1 Hz cadence).
//   * LoRa transmits use the existing radio_scheduler arbitration so REQ
//     traffic time-shares with detections and command acks.
//
// Auth
// ----
// The envelope's "Authorization: Bearer <token>" header is the operator's
// OpenForge C2 session token, opaque to the firmware. T2 forwards it to the
// C2 worker. T1 NEVER inspects or stores it.
//
// Master-of-mesh
// --------------
// When the phone wants to command another sensor in the bonded sensor's LoRa
// range (Chae01/Eric01/Spencer01/Mark01), it includes a path of the form:
//     POST /api/sensors/{sensor_name}/cmd
// The C2 worker dispatches; T2 receives the HTTP response back; the bonded
// T1 emits the response to the phone the same way. The bonded T1 is not
// special-cased — every API call is opaque.
//
// Build gating
// ------------
// Compiled only when PDL_BLE_COMPANION_ENABLED is defined (same env that
// enables ble_companion.cpp). main.cpp wires the dispatch hooks documented
// in PROXY_TUNNEL_INTEGRATION_NOTES.md.
// ============================================================================

#include <cstdint>
#include <cstddef>
#include "config.h"

#ifdef __cplusplus
extern "C" {
#endif

// ---------------------------------------------------------------------------
// MSG_TYPEs (see PROXY_TUNNEL_INTEGRATION_NOTES.md — must be added to
// config.h by the integrator):
//
//     #define MSG_TYPE_C2_PROXY_REQ  0x24
//     #define MSG_TYPE_C2_PROXY_RSP  0x25
//
// We declare local guarded fallbacks so this module compiles even before the
// integrator extends config.h.
// ---------------------------------------------------------------------------
#ifndef MSG_TYPE_C2_PROXY_REQ
#define MSG_TYPE_C2_PROXY_REQ  0x24
#endif
#ifndef MSG_TYPE_C2_PROXY_RSP
#define MSG_TYPE_C2_PROXY_RSP  0x25
#endif

// Reuse the same chunking constants as payload_buffer for byte-exact
// compatibility on the wire (same 24-byte header, same 200-byte max data).
#define C2_PROXY_HDR_LEN       24
#define C2_PROXY_MAX_DATA      200
#define C2_PROXY_MAX_PAYLOAD   (32 * 1024)   // 32 KB cap per request/response
#define C2_PROXY_CONCURRENT    2              // max simultaneous in-flight
#define C2_PROXY_BLE_TX_QUEUE  16             // pending BLE writes drain depth

// Status bytes returned by c2_proxy_handle_ble_write() / dispatch entry points
typedef enum : uint8_t {
    C2P_OK              = 0,
    C2P_BAD_HDR         = 1,
    C2P_BAD_CRC         = 2,
    C2P_TOO_LARGE       = 3,
    C2P_FULL            = 4,
    C2P_DUPLICATE       = 5,
    C2P_NOT_AUTH        = 6,    // BLE write before auth window opened
    C2P_BLE_TX_FAIL     = 7,
    C2P_LORA_TX_FAIL    = 8,
} C2ProxyStatus;

// ---------------------------------------------------------------------------
// Stats snapshot returned by c2_proxy_get_stats().
// ---------------------------------------------------------------------------
struct C2ProxyStats {
    uint32_t ble_in_chunks;          // chunks received from phone via ProxyTx
    uint32_t ble_in_envelopes;       // complete envelopes assembled
    uint32_t lora_out_chunks;        // chunks emitted as MSG_TYPE_C2_PROXY_REQ
    uint32_t lora_in_chunks;         // chunks received as MSG_TYPE_C2_PROXY_RSP
    uint32_t lora_in_envelopes;      // complete responses assembled
    uint32_t ble_out_chunks;         // chunks emitted on ProxyRx notify
    uint32_t ble_out_envelopes;      // complete responses delivered to phone
    uint32_t dropped_no_slot;
    uint32_t dropped_bad_crc;
    uint32_t dropped_not_auth;
    uint32_t in_flight_req;
    uint32_t in_flight_rsp;
};

// ---------------------------------------------------------------------------
// Lifecycle
// ---------------------------------------------------------------------------

// Initialise the tunnel module. Safe to call multiple times.
void c2_proxy_tunnel_init();

// Drive periodic work: drain queued BLE writes and emit them as LoRa REQ
// chunks; drain queued LoRa RSP chunks and emit them as ProxyRx notifies.
// Call once per main-loop cycle (existing 1 Hz cadence). Must be called on
// the main core, NOT inside a NimBLE callback.
void c2_proxy_tunnel_tick();

// ---------------------------------------------------------------------------
// BLE-side entry point (called from the ProxyTx write callback registered by
// ble_proxy_chars). The 24-byte+data buffer is the EXACT same wire format as
// MSG_TYPE_C2_PROXY_REQ — except buf[0] may be either 0x24 or omitted (the
// helper inside ble_proxy_chars stamps 0x24 before calling us).
//
// The call is non-blocking: chunks are queued for tick() to actually transmit
// over LoRa. Returns C2P_OK if the chunk was accepted into the queue.
// ---------------------------------------------------------------------------
C2ProxyStatus c2_proxy_handle_ble_write(const uint8_t* buf, size_t len);

// ---------------------------------------------------------------------------
// LoRa-side entry point (called from main.cpp dispatch when MSG_TYPE_C2_PROXY_RSP
// is received from T2). Reassembles the response and queues a BLE notify
// fan-out for the originating request.
// ---------------------------------------------------------------------------
C2ProxyStatus c2_proxy_handle_lora_rsp(const uint8_t* buf, size_t len);

// ---------------------------------------------------------------------------
// Optional: T2 may also send a C2_PROXY_REQ to a NEIGHBOR T1 if the
// originating phone targeted a non-bonded sensor (master-of-mesh fan-out).
// In that case the neighbor T1 needs to convert REQ → C2 call → RSP, which
// is T2's job — but to enable cross-mesh dispatch a future revision could
// route REQs through this hook. Today this is a passthrough that simply
// counts and discards (kept for protocol symmetry with future fan-out).
// ---------------------------------------------------------------------------
C2ProxyStatus c2_proxy_handle_lora_req(const uint8_t* buf, size_t len);

// ---------------------------------------------------------------------------
// Snapshot stats. out must be non-null.
// ---------------------------------------------------------------------------
void c2_proxy_get_stats(C2ProxyStats* out);

// ---------------------------------------------------------------------------
// Hooks the integrator MUST provide (defined as weak symbols in
// c2_proxy_tunnel.cpp; main.cpp can override).
//
//   c2_proxy_lora_tx(buf, len)   — actually transmit `len` bytes over LoRa.
//                                  Default impl forwards to a weak
//                                  __attribute__((weak)) implementation that
//                                  the integrator wires to radio_scheduler /
//                                  RadioLib's transmit.
//
//   c2_proxy_ble_notify(buf,len) — actually push `len` bytes via the ProxyRx
//                                  GATT notify. Wired by ble_proxy_chars.cpp.
//
//   c2_proxy_is_authenticated() — return true if the phone is currently
//                                  past the BLE auth gate. Default impl
//                                  delegates to ble_companion_is_authenticated
//                                  (now_ms = millis()).
// ---------------------------------------------------------------------------
bool c2_proxy_lora_tx(const uint8_t* buf, size_t len);
bool c2_proxy_ble_notify(const uint8_t* buf, size_t len);
bool c2_proxy_is_authenticated();

#ifdef UNIT_TEST
// Reset module state — host-test only.
void c2_proxy_tunnel_test_reset();
// Force-flush queued LoRa TX (host tests don't run the tick loop).
void c2_proxy_tunnel_test_drain_lora_queue();
// Force-flush queued BLE notify TX.
void c2_proxy_tunnel_test_drain_ble_queue();
#endif

#ifdef __cplusplus
}
#endif

#endif // C2_PROXY_TUNNEL_H
