use std::sync::Arc;

use core_types::{BufferId, SessionId, StreamId, Timestamp, TransportDomain};

use crate::schema::{SchemaId, SchemaVersion};

/// Reference-counted byte slice used by [`DataPayload::Inline`] so the bus and bridge
/// fan-out paths can share the same allocation across multiple subscribers / peers
/// instead of cloning the underlying `Vec<u8>`.
pub type SharedBytes = Arc<[u8]>;

/// Wrap a `Vec<u8>` into the shared `Arc<[u8]>` representation without copying the
/// payload buffer (the `Vec` is consumed and its buffer is reused for the `Arc`).
#[inline]
pub fn shared_bytes_from_vec(v: Vec<u8>) -> SharedBytes {
    Arc::<[u8]>::from(v.into_boxed_slice())
}

/// W3C Trace Context identifiers for distributed tracing (OpenTelemetry-compatible).
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub struct TraceContext {
    pub trace_id: u128,
    pub span_id: u64,
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct PacketHeader {
    pub version: u16,
    pub domain: TransportDomain,
    pub session_id: Option<SessionId>,
    pub stream_id: Option<StreamId>,
    pub sequence: u64,
    pub ack: Option<u64>,
    pub timestamp: Timestamp,
    pub schema_id: SchemaId,
    pub schema_version: SchemaVersion,
    pub trace_context: Option<TraceContext>,
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ControlEnvelope {
    pub header: PacketHeader,
    pub label: String,
    pub payload: Vec<u8>,
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ExternalBufferRef {
    pub buffer_id: BufferId,
    pub offset: usize,
    pub len: usize,
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub enum DataPayload {
    Inline(SharedBytes),
    External(ExternalBufferRef),
}

impl DataPayload {
    /// Construct an inline payload from any value convertible to `SharedBytes`
    /// (`Vec<u8>`, `&[u8]`, `Box<[u8]>`, `Arc<[u8]>`).
    #[inline]
    pub fn inline(bytes: impl Into<SharedBytes>) -> Self {
        DataPayload::Inline(bytes.into())
    }
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct DataEnvelope {
    pub header: PacketHeader,
    pub payload: DataPayload,
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Packet {
    Control(ControlEnvelope),
    Data(DataEnvelope),
}
