/* nginz-iot bridge C SDK (ABI v1/v2 compatibility plus negotiated ABI v3)
 *
 * Versioned, bounded, pointer-free records for out-of-tree sink adapters.
 * Adapters implement the vtable only; retry/dead-letter/replay/backpressure
 * remain owned by nginz-iot-bridge.
 *
 * Ownership rules:
 * - Config strings are valid only during init(); adapters that need them later
 *   must copy them into adapter-owned state and release that state in close().
 * - secret_ref is a reference such as env:NAME, never the secret value itself.
 * - Event and payload buffers are valid only for send()/send_batch().
 * - Error messages written by the adapter must be null-terminated and fit in
 *   NGINZ_IOT_BRIDGE_SDK_MAX_ERROR.
 * - Metrics labels must not include secrets, DSNs, passwords, or payload bytes.
 *
 * Lifecycle rules:
 * - The host negotiates ABI and capabilities before lifecycle callbacks.
 * - For each adapter instance the host calls init() once, then connect() once.
 * - send() and send_batch() calls are serialized by the host.
 * - On normal shutdown the host calls flush(), drain(), then close().
 * - Once init() is invoked, every open failure is followed by close().
 * - close() must tolerate a partially initialized or disconnected instance.
 * - Retry, durable replay, backpressure, health, and dead-letter ownership stay
 *   with the host daemon; adapters only classify individual send outcomes.
 *
 * Process-isolation rules:
 * - The host loads one configured adapter target per bridge worker process.
 * - One shared guardian supervises the bridge workers but loads no adapters.
 * - A native adapter fault makes only that bridge unhealthy; the guardian
 *   replaces only its worker without restarting sibling bridges, nginx, or
 *   broker authority.
 * - A callback that outlives the configured provider deadline stops the worker
 *   heartbeat and causes process replacement. Interrupted work may replay, and
 *   forced termination cannot guarantee flush(), drain(), or close().
 * - This contains accidental faults. It is not a sandbox for hostile code or
 *   a substitute for a durable outbox.
 */

#ifndef NGINZ_IOT_BRIDGE_SDK_H
#define NGINZ_IOT_BRIDGE_SDK_H

#include <stddef.h>
#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif

#define NGINZ_IOT_BRIDGE_SDK_VERSION_MAJOR 3
#define NGINZ_IOT_BRIDGE_SDK_VERSION_MINOR 0
#define NGINZ_IOT_BRIDGE_SDK_VERSION_PATCH 0
#define NGINZ_IOT_BRIDGE_SDK_ABI_VERSION_V1 1
#define NGINZ_IOT_BRIDGE_SDK_ABI_VERSION_V2 2
#define NGINZ_IOT_BRIDGE_SDK_ABI_VERSION_V3 3
#define NGINZ_IOT_BRIDGE_SDK_ABI_VERSION_LATEST NGINZ_IOT_BRIDGE_SDK_ABI_VERSION_V3
/* Source compatibility for v1 adapters. New adapters use the explicit
 * version macro matching their vtable.
 */
#define NGINZ_IOT_BRIDGE_SDK_ABI_VERSION NGINZ_IOT_BRIDGE_SDK_ABI_VERSION_V1
#define NGINZ_IOT_BRIDGE_SDK_ABI_MIN_SUPPORTED 1
#define NGINZ_IOT_BRIDGE_SDK_ABI_MAX_SUPPORTED 3

#define NGINZ_IOT_BRIDGE_SDK_MAX_ID 64
#define NGINZ_IOT_BRIDGE_SDK_MAX_TOPIC 256
#define NGINZ_IOT_BRIDGE_SDK_MAX_TARGET 256
#define NGINZ_IOT_BRIDGE_SDK_MAX_KEY 256
#define NGINZ_IOT_BRIDGE_SDK_MAX_ERROR 128
#define NGINZ_IOT_BRIDGE_SDK_MAX_CONFIG 512
#define NGINZ_IOT_BRIDGE_SDK_MAX_INLINE_PAYLOAD 256
#define NGINZ_IOT_BRIDGE_SDK_MAX_BATCH_EVENTS 32
#define NGINZ_IOT_BRIDGE_SDK_MAX_BATCH_BYTES 1048576
#define NGINZ_IOT_BRIDGE_SDK_MAX_BATCH_LINGER_MS 60000
#define NGINZ_IOT_BRIDGE_SDK_V3_MAX_BRIDGE_ID 64
#define NGINZ_IOT_BRIDGE_SDK_V3_MAX_TENANT 48
#define NGINZ_IOT_BRIDGE_SDK_V3_MAX_CLIENT_ID 128
#define NGINZ_IOT_BRIDGE_SDK_V3_MAX_SCHEMA_ID 64
#define NGINZ_IOT_BRIDGE_SDK_V3_MAX_INLINE_PAYLOAD 512
#define NGINZ_IOT_BRIDGE_SDK_V3_IDEMPOTENCY_KEY_LEN 64

enum nginz_iot_bridge_sdk_sink_kind {
    NGINZ_IOT_BRIDGE_SDK_SINK_NONE = 0,
    NGINZ_IOT_BRIDGE_SDK_SINK_POSTGRES = 1,
    NGINZ_IOT_BRIDGE_SDK_SINK_WEBHOOK = 2,
    NGINZ_IOT_BRIDGE_SDK_SINK_MQTT = 3,
    NGINZ_IOT_BRIDGE_SDK_SINK_KAFKA = 4,
    NGINZ_IOT_BRIDGE_SDK_SINK_REDIS = 5,
    NGINZ_IOT_BRIDGE_SDK_SINK_CLICKHOUSE = 6,
    NGINZ_IOT_BRIDGE_SDK_SINK_OBJECT = 7,
    NGINZ_IOT_BRIDGE_SDK_SINK_INFLUXDB = 8,
    NGINZ_IOT_BRIDGE_SDK_SINK_TIMESCALEDB = 9,
    NGINZ_IOT_BRIDGE_SDK_SINK_RABBITMQ = 10,
    NGINZ_IOT_BRIDGE_SDK_SINK_EXTERNAL = 255
};

enum nginz_iot_bridge_sdk_failure_class {
    NGINZ_IOT_BRIDGE_SDK_SUCCESS = 0,
    NGINZ_IOT_BRIDGE_SDK_RETRYABLE = 1,
    NGINZ_IOT_BRIDGE_SDK_PERMANENT = 2,
    NGINZ_IOT_BRIDGE_SDK_CONFIGURATION = 3,
    NGINZ_IOT_BRIDGE_SDK_TIMEOUT = 4
};

enum nginz_iot_bridge_sdk_capability {
    NGINZ_IOT_BRIDGE_SDK_CAP_SEND = 1u << 0,
    /* Reserved and rejected in ABI v1; implemented by ABI v2. */
    NGINZ_IOT_BRIDGE_SDK_CAP_BATCH = 1u << 1,
    NGINZ_IOT_BRIDGE_SDK_CAP_FLUSH = 1u << 2,
    NGINZ_IOT_BRIDGE_SDK_CAP_DRAIN = 1u << 3,
    NGINZ_IOT_BRIDGE_SDK_CAP_REDACT = 1u << 4
};

#define NGINZ_IOT_BRIDGE_SDK_CAP_REQUIRED_SINK_V1 \
    (NGINZ_IOT_BRIDGE_SDK_CAP_SEND |             \
     NGINZ_IOT_BRIDGE_SDK_CAP_FLUSH |            \
     NGINZ_IOT_BRIDGE_SDK_CAP_DRAIN |            \
     NGINZ_IOT_BRIDGE_SDK_CAP_REDACT)

/* ABI v1 supports only the required sink lifecycle. Batching, source
 * ingestion, acknowledgement, checkpointing, and bidirectional behavior need
 * a later versioned public ABI before an adapter may advertise them.
 */
#define NGINZ_IOT_BRIDGE_SDK_CAP_SUPPORTED_V1 \
    NGINZ_IOT_BRIDGE_SDK_CAP_REQUIRED_SINK_V1

#define NGINZ_IOT_BRIDGE_SDK_CAP_REQUIRED_SINK_V2 \
    (NGINZ_IOT_BRIDGE_SDK_CAP_REQUIRED_SINK_V1 |  \
     NGINZ_IOT_BRIDGE_SDK_CAP_BATCH)

#define NGINZ_IOT_BRIDGE_SDK_CAP_SUPPORTED_V2 \
    NGINZ_IOT_BRIDGE_SDK_CAP_REQUIRED_SINK_V2

#define NGINZ_IOT_BRIDGE_SDK_CAP_REQUIRED_SINK_V3 \
    NGINZ_IOT_BRIDGE_SDK_CAP_REQUIRED_SINK_V2

#define NGINZ_IOT_BRIDGE_SDK_CAP_SUPPORTED_V3 \
    NGINZ_IOT_BRIDGE_SDK_CAP_REQUIRED_SINK_V3

struct nginz_iot_bridge_sdk_event {
    uint32_t abi_version;
    uint8_t sink_kind;
    uint8_t qos;
    uint8_t retain;
    uint8_t _pad0;
    uint64_t event_seq;
    uint32_t payload_len;
    uint16_t tenant_len;
    uint16_t client_id_len;
    uint16_t topic_len;
    uint16_t target_len;
    uint16_t key_len;
    uint16_t payload_inline_len;
    char tenant[NGINZ_IOT_BRIDGE_SDK_MAX_ID];
    char client_id[NGINZ_IOT_BRIDGE_SDK_MAX_ID];
    char topic[NGINZ_IOT_BRIDGE_SDK_MAX_TOPIC];
    char target[NGINZ_IOT_BRIDGE_SDK_MAX_TARGET];
    char key[NGINZ_IOT_BRIDGE_SDK_MAX_KEY];
    uint8_t payload_inline[NGINZ_IOT_BRIDGE_SDK_MAX_INLINE_PAYLOAD];
    uint8_t payload_digest[32];
};

struct nginz_iot_bridge_sdk_config {
    uint32_t abi_version;
    uint8_t sink_kind;
    uint8_t _pad0[3];
    uint32_t timeout_ms;
    uint32_t capabilities;
    char bridge_id[NGINZ_IOT_BRIDGE_SDK_MAX_ID];
    char endpoint[NGINZ_IOT_BRIDGE_SDK_MAX_CONFIG];
    /* Secret-bearing fields must never be copied into diagnostics. */
    char secret_ref[NGINZ_IOT_BRIDGE_SDK_MAX_ID];
};

/* ABI v2 appends bounded batch policy without changing the ABI v1 config. */
struct nginz_iot_bridge_sdk_config_v2 {
    struct nginz_iot_bridge_sdk_config base;
    uint32_t max_batch_events;
    uint32_t max_batch_bytes;
    uint32_t batch_linger_ms;
    uint32_t _pad1;
};

struct nginz_iot_bridge_sdk_result {
    uint32_t abi_version;
    uint8_t failure_class;
    uint8_t _pad0[3];
    char reason[NGINZ_IOT_BRIDGE_SDK_MAX_ERROR];
};

/* All pointers are borrowed for the duration of send_batch(). events,
 * payloads, and payload_lengths each contain event_count entries. The host
 * validates every payload length and the total before the call.
 */
struct nginz_iot_bridge_sdk_batch_v2 {
    uint32_t abi_version;
    uint32_t event_count;
    uint32_t total_payload_bytes;
    uint32_t _pad0;
    const struct nginz_iot_bridge_sdk_event *events;
    const uint8_t *const *payloads;
    const size_t *payload_lengths;
};

/* ABI v3 is an additive, independent event layout. `struct_size` lets hosts
 * and adapters validate the exact minimum they consume while later versions
 * append fields. The idempotency key is a host-rendered lowercase SHA-256 hex
 * token over the canonical bridge/node/shard/generation/term/event identity.
 */
struct nginz_iot_bridge_sdk_event_v3 {
    uint32_t abi_version;
    uint32_t struct_size;
    uint8_t sink_kind;
    uint8_t qos;
    uint8_t retain;
    uint8_t _pad0;
    uint16_t attempt;
    uint16_t bridge_id_len;
    uint16_t tenant_len;
    uint16_t client_id_len;
    uint16_t topic_len;
    uint16_t target_len;
    uint16_t key_len;
    uint16_t schema_id_len;
    uint16_t idempotency_key_len;
    uint16_t payload_inline_len;
    uint32_t payload_len;
    uint32_t node_id;
    uint32_t shard_id;
    uint64_t event_seq;
    uint64_t generation;
    uint64_t writer_term;
    uint64_t published_at_unix_ns;
    char bridge_id[NGINZ_IOT_BRIDGE_SDK_V3_MAX_BRIDGE_ID];
    char tenant[NGINZ_IOT_BRIDGE_SDK_V3_MAX_TENANT];
    char client_id[NGINZ_IOT_BRIDGE_SDK_V3_MAX_CLIENT_ID];
    char topic[NGINZ_IOT_BRIDGE_SDK_MAX_TOPIC];
    char target[NGINZ_IOT_BRIDGE_SDK_MAX_TARGET];
    char key[NGINZ_IOT_BRIDGE_SDK_MAX_KEY];
    char schema_id[NGINZ_IOT_BRIDGE_SDK_V3_MAX_SCHEMA_ID];
    char idempotency_key[NGINZ_IOT_BRIDGE_SDK_V3_IDEMPOTENCY_KEY_LEN];
    uint8_t payload_inline[NGINZ_IOT_BRIDGE_SDK_V3_MAX_INLINE_PAYLOAD];
    uint8_t payload_digest[32];
};

struct nginz_iot_bridge_sdk_config_v3 {
    uint32_t abi_version;
    uint32_t struct_size;
    uint8_t sink_kind;
    uint8_t _pad0[3];
    uint32_t timeout_ms;
    uint32_t capabilities;
    uint32_t max_batch_events;
    uint32_t max_batch_bytes;
    uint32_t batch_linger_ms;
    uint32_t _pad1;
    char bridge_id[NGINZ_IOT_BRIDGE_SDK_MAX_ID];
    char endpoint[NGINZ_IOT_BRIDGE_SDK_MAX_CONFIG];
    char secret_ref[NGINZ_IOT_BRIDGE_SDK_MAX_ID];
};

struct nginz_iot_bridge_sdk_result_v3 {
    uint32_t abi_version;
    uint32_t struct_size;
    uint8_t failure_class;
    uint8_t _pad0[3];
    char reason[NGINZ_IOT_BRIDGE_SDK_MAX_ERROR];
};

struct nginz_iot_bridge_sdk_batch_v3 {
    uint32_t abi_version;
    uint32_t struct_size;
    uint32_t event_count;
    uint32_t total_payload_bytes;
    const struct nginz_iot_bridge_sdk_event_v3 *events;
    const uint8_t *const *payloads;
    const size_t *payload_lengths;
};

struct nginz_iot_bridge_sdk_adapter;
struct nginz_iot_bridge_sdk_adapter_v3;

struct nginz_iot_bridge_sdk_vtable {
    uint32_t abi_version;
    uint32_t capabilities;
    const char *name;
    int (*init)(struct nginz_iot_bridge_sdk_adapter *adapter,
                const struct nginz_iot_bridge_sdk_config *config,
                char *error, size_t error_len);
    int (*connect)(struct nginz_iot_bridge_sdk_adapter *adapter,
                   char *error, size_t error_len);
    int (*send)(struct nginz_iot_bridge_sdk_adapter *adapter,
                const struct nginz_iot_bridge_sdk_event *event,
                const uint8_t *payload, size_t payload_len,
                struct nginz_iot_bridge_sdk_result *out);
    int (*flush)(struct nginz_iot_bridge_sdk_adapter *adapter,
                 char *error, size_t error_len);
    int (*drain)(struct nginz_iot_bridge_sdk_adapter *adapter,
                 char *error, size_t error_len);
    void (*close)(struct nginz_iot_bridge_sdk_adapter *adapter);
    size_t (*redact_config)(struct nginz_iot_bridge_sdk_adapter *adapter,
                            char *out, size_t out_len);
};

/* ABI v2 is a separate vtable so ABI v1 layout and callback offsets remain
 * unchanged. send_batch must fill exactly result_count per-event results even
 * when the provider reports a partial batch failure.
 */
struct nginz_iot_bridge_sdk_vtable_v2 {
    uint32_t abi_version;
    uint32_t capabilities;
    const char *name;
    int (*init)(struct nginz_iot_bridge_sdk_adapter *adapter,
                const struct nginz_iot_bridge_sdk_config_v2 *config,
                char *error, size_t error_len);
    int (*connect)(struct nginz_iot_bridge_sdk_adapter *adapter,
                   char *error, size_t error_len);
    int (*send)(struct nginz_iot_bridge_sdk_adapter *adapter,
                const struct nginz_iot_bridge_sdk_event *event,
                const uint8_t *payload, size_t payload_len,
                struct nginz_iot_bridge_sdk_result *out);
    int (*send_batch)(struct nginz_iot_bridge_sdk_adapter *adapter,
                      const struct nginz_iot_bridge_sdk_batch_v2 *batch,
                      struct nginz_iot_bridge_sdk_result *out_results,
                      size_t result_count);
    int (*flush)(struct nginz_iot_bridge_sdk_adapter *adapter,
                 char *error, size_t error_len);
    int (*drain)(struct nginz_iot_bridge_sdk_adapter *adapter,
                 char *error, size_t error_len);
    void (*close)(struct nginz_iot_bridge_sdk_adapter *adapter);
    size_t (*redact_config)(struct nginz_iot_bridge_sdk_adapter *adapter,
                            char *out, size_t out_len);
};

struct nginz_iot_bridge_sdk_vtable_v3 {
    uint32_t abi_version;
    uint32_t struct_size;
    uint32_t capabilities;
    uint32_t _pad0;
    const char *name;
    int (*init)(struct nginz_iot_bridge_sdk_adapter_v3 *adapter,
                const struct nginz_iot_bridge_sdk_config_v3 *config,
                char *error, size_t error_len);
    int (*connect)(struct nginz_iot_bridge_sdk_adapter_v3 *adapter,
                   char *error, size_t error_len);
    int (*send)(struct nginz_iot_bridge_sdk_adapter_v3 *adapter,
                const struct nginz_iot_bridge_sdk_event_v3 *event,
                const uint8_t *payload, size_t payload_len,
                struct nginz_iot_bridge_sdk_result_v3 *out);
    int (*send_batch)(struct nginz_iot_bridge_sdk_adapter_v3 *adapter,
                      const struct nginz_iot_bridge_sdk_batch_v3 *batch,
                      struct nginz_iot_bridge_sdk_result_v3 *out_results,
                      size_t result_count);
    int (*flush)(struct nginz_iot_bridge_sdk_adapter_v3 *adapter,
                 char *error, size_t error_len);
    int (*drain)(struct nginz_iot_bridge_sdk_adapter_v3 *adapter,
                 char *error, size_t error_len);
    void (*close)(struct nginz_iot_bridge_sdk_adapter_v3 *adapter);
    size_t (*redact_config)(struct nginz_iot_bridge_sdk_adapter_v3 *adapter,
                            char *out, size_t out_len);
};

struct nginz_iot_bridge_sdk_adapter {
    uint32_t abi_version;
    uint32_t capabilities;
    void *user_data;
    const struct nginz_iot_bridge_sdk_vtable *vtable;
};

struct nginz_iot_bridge_sdk_adapter_v3 {
    uint32_t abi_version;
    uint32_t struct_size;
    uint32_t capabilities;
    uint32_t _pad0;
    void *user_data;
    const struct nginz_iot_bridge_sdk_vtable_v3 *vtable;
};

#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
_Static_assert(offsetof(struct nginz_iot_bridge_sdk_event_v3, struct_size) == sizeof(uint32_t), "event_v3 struct_size offset");
_Static_assert(sizeof(struct nginz_iot_bridge_sdk_event_v3) == 1760, "event_v3 stable size");
_Static_assert(offsetof(struct nginz_iot_bridge_sdk_event_v3, event_seq) == 48, "event_v3 event_seq offset");
_Static_assert(offsetof(struct nginz_iot_bridge_sdk_event_v3, bridge_id) == 80, "event_v3 bridge_id offset");
_Static_assert(offsetof(struct nginz_iot_bridge_sdk_event_v3, payload_digest) == 1728, "event_v3 payload_digest offset");
_Static_assert(sizeof(((struct nginz_iot_bridge_sdk_event_v3 *)0)->generation) == sizeof(uint64_t), "event_v3 generation width");
_Static_assert(sizeof(((struct nginz_iot_bridge_sdk_event_v3 *)0)->idempotency_key) == NGINZ_IOT_BRIDGE_SDK_V3_IDEMPOTENCY_KEY_LEN, "event_v3 idempotency width");
_Static_assert(offsetof(struct nginz_iot_bridge_sdk_config_v3, struct_size) == sizeof(uint32_t), "config_v3 struct_size offset");
_Static_assert(sizeof(struct nginz_iot_bridge_sdk_config_v3) == 676, "config_v3 stable size");
_Static_assert(offsetof(struct nginz_iot_bridge_sdk_result_v3, struct_size) == sizeof(uint32_t), "result_v3 struct_size offset");
_Static_assert(sizeof(struct nginz_iot_bridge_sdk_result_v3) == 140, "result_v3 stable size");
_Static_assert(offsetof(struct nginz_iot_bridge_sdk_batch_v3, struct_size) == sizeof(uint32_t), "batch_v3 struct_size offset");
_Static_assert(sizeof(struct nginz_iot_bridge_sdk_batch_v3) == 16 + 3 * sizeof(void *), "batch_v3 stable size");
_Static_assert(offsetof(struct nginz_iot_bridge_sdk_vtable_v3, struct_size) == sizeof(uint32_t), "vtable_v3 struct_size offset");
_Static_assert(sizeof(struct nginz_iot_bridge_sdk_vtable_v3) == 16 + 9 * sizeof(void *), "vtable_v3 stable size");
_Static_assert(offsetof(struct nginz_iot_bridge_sdk_adapter_v3, struct_size) == sizeof(uint32_t), "adapter_v3 struct_size offset");
_Static_assert(sizeof(struct nginz_iot_bridge_sdk_adapter_v3) == 16 + 2 * sizeof(void *), "adapter_v3 stable size");
#endif

/* Selects the newest common ABI. Returns 0 on success and writes selected_abi;
 * returns non-zero for invalid ranges or when no common ABI exists.
 */
static inline int nginz_iot_bridge_sdk_negotiate_abi(
    uint32_t host_min,
    uint32_t host_max,
    uint32_t adapter_min,
    uint32_t adapter_max,
    uint32_t *selected_abi
) {
    uint32_t lower;
    uint32_t upper;
    if (selected_abi == NULL || host_min == 0 || adapter_min == 0 ||
        host_min > host_max || adapter_min > adapter_max) {
        return 1;
    }
    lower = host_min > adapter_min ? host_min : adapter_min;
    upper = host_max < adapter_max ? host_max : adapter_max;
    if (lower > upper) {
        return 1;
    }
    *selected_abi = upper;
    return 0;
}

/* Returns 0 when the exact adapter ABI falls inside the host-supported range.
 * Retained as a source compatibility helper for ABI v1 adapters.
 */
static inline int nginz_iot_bridge_sdk_check_abi(uint32_t adapter_abi) {
    uint32_t selected_abi = 0;
    return nginz_iot_bridge_sdk_negotiate_abi(
        NGINZ_IOT_BRIDGE_SDK_ABI_MIN_SUPPORTED,
        NGINZ_IOT_BRIDGE_SDK_ABI_MAX_SUPPORTED,
        adapter_abi,
        adapter_abi,
        &selected_abi
    );
}

/* Returns 0 when all required ABI v1 capabilities are present and no
 * unsupported/reserved bits are advertised.
 */
static inline int nginz_iot_bridge_sdk_check_capabilities_v1(uint32_t capabilities) {
    if ((capabilities & NGINZ_IOT_BRIDGE_SDK_CAP_REQUIRED_SINK_V1) !=
        NGINZ_IOT_BRIDGE_SDK_CAP_REQUIRED_SINK_V1) {
        return 1;
    }
    return (capabilities & ~NGINZ_IOT_BRIDGE_SDK_CAP_SUPPORTED_V1) == 0 ? 0 : 1;
}

static inline int nginz_iot_bridge_sdk_check_capabilities_v2(uint32_t capabilities) {
    if ((capabilities & NGINZ_IOT_BRIDGE_SDK_CAP_REQUIRED_SINK_V2) !=
        NGINZ_IOT_BRIDGE_SDK_CAP_REQUIRED_SINK_V2) {
        return 1;
    }
    return (capabilities & ~NGINZ_IOT_BRIDGE_SDK_CAP_SUPPORTED_V2) == 0 ? 0 : 1;
}

static inline int nginz_iot_bridge_sdk_check_capabilities_v3(uint32_t capabilities) {
    if ((capabilities & NGINZ_IOT_BRIDGE_SDK_CAP_REQUIRED_SINK_V3) !=
        NGINZ_IOT_BRIDGE_SDK_CAP_REQUIRED_SINK_V3) {
        return 1;
    }
    return (capabilities & ~NGINZ_IOT_BRIDGE_SDK_CAP_SUPPORTED_V3) == 0 ? 0 : 1;
}

/* Trust policy: only load adapters from configured trusted paths. */
enum nginz_iot_bridge_sdk_trust {
    NGINZ_IOT_BRIDGE_SDK_TRUST_REJECT = 0,
    NGINZ_IOT_BRIDGE_SDK_TRUST_ALLOW_PATH = 1,
    NGINZ_IOT_BRIDGE_SDK_TRUST_ALLOW_SIGNED = 2
};

struct nginz_iot_bridge_sdk_trust_policy {
    uint32_t abi_version;
    uint8_t mode;
    uint8_t _pad0[3];
    char trusted_prefix[NGINZ_IOT_BRIDGE_SDK_MAX_CONFIG];
};

static inline int nginz_iot_bridge_sdk_path_has_unsafe_component(const char *path) {
    size_t i;
    size_t start;
    char c;
    if (path == NULL || path[0] != '/') {
        return 1;
    }
    start = 1;
    for (i = 1;; ++i) {
        c = path[i];
        if (c == '/' || c == '\0') {
            if (i == start + 1 && path[start] == '.') {
                return 1;
            }
            if (i == start + 2 && path[start] == '.' && path[start + 1] == '.') {
                return 1;
            }
            start = i + 1;
        }
        if (c == '\0') {
            break;
        }
    }
    return 0;
}

static inline int nginz_iot_bridge_sdk_path_trusted(
    const struct nginz_iot_bridge_sdk_trust_policy *policy,
    const char *path
) {
    size_t i;
    if (policy == NULL || path == NULL) {
        return 0;
    }
    if (policy->abi_version != NGINZ_IOT_BRIDGE_SDK_ABI_VERSION) {
        return 0;
    }
    if (policy->mode == NGINZ_IOT_BRIDGE_SDK_TRUST_REJECT) {
        return 0;
    }
    if (policy->mode == NGINZ_IOT_BRIDGE_SDK_TRUST_ALLOW_SIGNED) {
        return 0;
    }
    if (policy->trusted_prefix[0] == '\0') {
        return 0;
    }
    if (nginz_iot_bridge_sdk_path_has_unsafe_component(path) ||
        nginz_iot_bridge_sdk_path_has_unsafe_component(policy->trusted_prefix)) {
        return 0;
    }
    for (i = 0; policy->trusted_prefix[i] != '\0'; ++i) {
        if (path[i] == '\0') {
            return 0;
        }
        if (path[i] != policy->trusted_prefix[i]) {
            return 0;
        }
    }
    return path[i] == '/' || path[i] == '\0' || policy->trusted_prefix[i - 1] == '/';
}

/* Sample adapter export used by the packaged conformance harness. Production
 * out-of-tree adapters export an equivalent vtable entry point of their own.
 */
const struct nginz_iot_bridge_sdk_vtable *nginz_iot_bridge_sdk_sample_vtable(void);

/* Canonical production-loader entry point. */
const struct nginz_iot_bridge_sdk_vtable *nginz_iot_bridge_sdk_vtable(void);

/* Canonical negotiated ABI v2 entry point. The adapter selects the newest
 * common ABI and writes selected_abi. It returns NULL when no compatible v2
 * contract exists. ABI v1 adapters continue exporting the entry point above.
 */
const struct nginz_iot_bridge_sdk_vtable_v2 *nginz_iot_bridge_sdk_vtable_v2(
    uint32_t host_abi_min,
    uint32_t host_abi_max,
    uint32_t *selected_abi
);

/* Additive ABI v3 entry point. ABI v1/v2 symbols and layouts remain exact. */
const struct nginz_iot_bridge_sdk_vtable_v3 *nginz_iot_bridge_sdk_vtable_v3(
    uint32_t host_abi_min,
    uint32_t host_abi_max,
    uint32_t *selected_abi
);

#ifdef __cplusplus
}
#endif

#endif /* NGINZ_IOT_BRIDGE_SDK_H */
