// Copyright (C) 2021, Cloudflare, Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

use crate::Bytes;
use crate::Token;
use http3::*;
use quic::*;

use serde::Deserialize;
use serde::Serialize;

use std::collections::BTreeMap;

pub type ExData = BTreeMap<String, serde_json::Value>;

pub const LOGLEVEL_URI: &str = "urn:ietf:params:qlog:events:loglevel-13";

pub const QUIC_URI: &str = "urn:ietf:params:qlog:events:quic-12";
pub const HTTP3_URI: &str = "urn:ietf:params:qlog:events:http3-12";

#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Debug, Default)]
#[serde(untagged)]
pub enum EventType {
    QuicEventType(QuicEventType),

    Http3EventType(Http3EventType),

    LogLevelEventType(LogLevelEventType),

    #[default]
    None,
}

use crate::TimeFormat;

#[serde_with::skip_serializing_none]
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct Event {
    pub time: f64,

    // Strictly, the qlog 02 spec says we should have a name field in the
    // `Event` structure. However, serde's autogenerated Deserialize code
    // struggles to read Events properly because the `EventData` types often
    // alias. In order to work around that, we use can use a trick that will
    // give serde autogen all the information that it needs while also produced
    // a legal qlog. Specifically, strongly linking an EventData enum variant
    // with the wire-format name.
    //
    // The trick is to use Adjacent Tagging
    // (https://serde.rs/enum-representations.html#adjacently-tagged) with
    // Struct flattening (https://serde.rs/attr-flatten.html). At a high level
    // this first creates an `EventData` JSON object:
    //
    // {name: <enum variant name>, data: enum variant data }
    //
    // and then flattens those fields into the `Event` object.
    #[serde(flatten)]
    pub data: EventData,

    #[serde(flatten)]
    pub ex_data: Box<ExData>,

    pub group_id: Option<Box<String>>,

    pub time_format: Option<TimeFormat>,

    #[serde(skip)]
    ty: EventType,
}

impl Event {
    /// Returns a new `Event` object with the provided time and data.
    pub fn with_time(time: f64, data: EventData) -> Self {
        Self::with_time_ex(time, data, Default::default())
    }

    /// Returns a new `Event` object with the provided time, data and ex_data.
    pub fn with_time_ex(time: f64, data: EventData, ex_data: ExData) -> Self {
        let ty = EventType::from(&data);
        Event {
            time,
            data,
            ex_data: Box::new(ex_data),
            group_id: Default::default(),
            time_format: Default::default(),
            ty,
        }
    }
}

impl Eventable for Event {
    fn importance(&self) -> EventImportance {
        self.ty.into()
    }

    fn set_time(&mut self, time: f64) {
        self.time = time;
    }
}

impl PartialEq for Event {
    // custom comparison to skip over the `ty` field
    fn eq(&self, other: &Event) -> bool {
        self.time == other.time &&
            self.data == other.data &&
            self.ex_data == other.ex_data &&
            self.group_id == other.group_id &&
            self.time_format == other.time_format
    }
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct JsonEvent {
    pub time: f64,

    #[serde(skip)]
    pub importance: EventImportance,

    pub name: String,
    pub data: serde_json::Value,
}

impl Eventable for JsonEvent {
    fn importance(&self) -> EventImportance {
        self.importance
    }

    fn set_time(&mut self, time: f64) {
        self.time = time;
    }
}

#[derive(Clone, Copy, Debug, Default)]
pub enum EventImportance {
    #[default]
    Core,
    Base,
    Extra,
}

impl EventImportance {
    /// Returns true if this importance level is included by `other`.
    pub fn is_contained_in(&self, other: &EventImportance) -> bool {
        match (other, self) {
            (EventImportance::Core, EventImportance::Core) => true,

            (EventImportance::Base, EventImportance::Core) |
            (EventImportance::Base, EventImportance::Base) => true,

            (EventImportance::Extra, EventImportance::Core) |
            (EventImportance::Extra, EventImportance::Base) |
            (EventImportance::Extra, EventImportance::Extra) => true,

            (..) => false,
        }
    }
}

impl From<EventType> for EventImportance {
    fn from(ty: EventType) -> Self {
        match ty {
            EventType::QuicEventType(QuicEventType::ServerListening) =>
                EventImportance::Extra,
            EventType::QuicEventType(QuicEventType::ConnectionStarted) =>
                EventImportance::Base,
            EventType::QuicEventType(QuicEventType::ConnectionClosed) =>
                EventImportance::Base,
            EventType::QuicEventType(QuicEventType::ConnectionIdUpdated) =>
                EventImportance::Base,
            EventType::QuicEventType(QuicEventType::SpinBitUpdated) =>
                EventImportance::Base,
            EventType::QuicEventType(QuicEventType::ConnectionStateUpdated) =>
                EventImportance::Base,
            EventType::QuicEventType(QuicEventType::TupleAssigned) =>
                EventImportance::Extra,
            EventType::QuicEventType(QuicEventType::MtuUpdated) =>
                EventImportance::Extra,

            EventType::QuicEventType(QuicEventType::VersionInformation) =>
                EventImportance::Core,
            EventType::QuicEventType(QuicEventType::AlpnInformation) =>
                EventImportance::Core,
            EventType::QuicEventType(QuicEventType::ParametersSet) =>
                EventImportance::Core,
            EventType::QuicEventType(QuicEventType::ParametersRestored) =>
                EventImportance::Base,
            EventType::QuicEventType(QuicEventType::PacketSent) =>
                EventImportance::Core,
            EventType::QuicEventType(QuicEventType::PacketReceived) =>
                EventImportance::Core,
            EventType::QuicEventType(QuicEventType::PacketDropped) =>
                EventImportance::Base,
            EventType::QuicEventType(QuicEventType::PacketBuffered) =>
                EventImportance::Base,
            EventType::QuicEventType(QuicEventType::PacketsAcked) =>
                EventImportance::Extra,
            EventType::QuicEventType(QuicEventType::UdpDatagramsSent) =>
                EventImportance::Extra,
            EventType::QuicEventType(QuicEventType::UdpDatagramsReceived) =>
                EventImportance::Extra,
            EventType::QuicEventType(QuicEventType::UdpDatagramDropped) =>
                EventImportance::Extra,
            EventType::QuicEventType(QuicEventType::StreamStateUpdated) =>
                EventImportance::Base,
            EventType::QuicEventType(QuicEventType::FramesProcessed) =>
                EventImportance::Extra,
            EventType::QuicEventType(QuicEventType::StreamDataMoved) =>
                EventImportance::Base,
            EventType::QuicEventType(QuicEventType::DatagramDataMoved) =>
                EventImportance::Base,
            EventType::QuicEventType(
                QuicEventType::ConnectionDataBlockedUpdated,
            ) => EventImportance::Extra,
            EventType::QuicEventType(QuicEventType::StreamDataBlockedUpdated) =>
                EventImportance::Extra,
            EventType::QuicEventType(
                QuicEventType::DatagramDataBlockedUpdated,
            ) => EventImportance::Extra,
            EventType::QuicEventType(QuicEventType::MigrationStateUpdated) =>
                EventImportance::Base,

            EventType::QuicEventType(QuicEventType::KeyUpdated) =>
                EventImportance::Base,
            EventType::QuicEventType(QuicEventType::KeyDiscarded) =>
                EventImportance::Base,

            EventType::QuicEventType(QuicEventType::RecoveryParametersSet) =>
                EventImportance::Base,
            EventType::QuicEventType(QuicEventType::RecoveryMetricsUpdated) =>
                EventImportance::Core,
            EventType::QuicEventType(QuicEventType::CongestionStateUpdated) =>
                EventImportance::Base,
            EventType::QuicEventType(QuicEventType::TimerUpdated) =>
                EventImportance::Extra,
            EventType::QuicEventType(QuicEventType::PacketLost) =>
                EventImportance::Core,
            EventType::QuicEventType(QuicEventType::MarkedForRetransmit) =>
                EventImportance::Extra,
            EventType::QuicEventType(QuicEventType::EcnStateUpdated) =>
                EventImportance::Extra,

            EventType::Http3EventType(Http3EventType::ParametersSet) =>
                EventImportance::Base,
            EventType::Http3EventType(Http3EventType::StreamTypeSet) =>
                EventImportance::Base,
            EventType::Http3EventType(Http3EventType::PriorityUpdated) =>
                EventImportance::Base,
            EventType::Http3EventType(Http3EventType::FrameCreated) =>
                EventImportance::Core,
            EventType::Http3EventType(Http3EventType::FrameParsed) =>
                EventImportance::Core,
            EventType::Http3EventType(Http3EventType::DatagramCreated) =>
                EventImportance::Base,
            EventType::Http3EventType(Http3EventType::DatagramParsed) =>
                EventImportance::Base,
            EventType::Http3EventType(Http3EventType::PushResolved) =>
                EventImportance::Extra,

            _ => unimplemented!(),
        }
    }
}

pub trait Eventable {
    fn importance(&self) -> EventImportance;

    fn set_time(&mut self, time: f64);
}

impl From<&EventData> for EventType {
    fn from(event_data: &EventData) -> Self {
        match event_data {
            EventData::QuicServerListening { .. } =>
                EventType::QuicEventType(QuicEventType::ServerListening),
            EventData::QuicConnectionStarted { .. } =>
                EventType::QuicEventType(QuicEventType::ConnectionStarted),
            EventData::QuicConnectionClosed { .. } =>
                EventType::QuicEventType(QuicEventType::ConnectionClosed),
            EventData::QuicConnectionIdUpdated { .. } =>
                EventType::QuicEventType(QuicEventType::ConnectionIdUpdated),
            EventData::QuicSpinBitUpdated { .. } =>
                EventType::QuicEventType(QuicEventType::SpinBitUpdated),
            EventData::QuicConnectionStateUpdated { .. } =>
                EventType::QuicEventType(QuicEventType::ConnectionStateUpdated),
            EventData::QuicTupleAssigned { .. } =>
                EventType::QuicEventType(QuicEventType::TupleAssigned),
            EventData::QuicMtuUpdated { .. } =>
                EventType::QuicEventType(QuicEventType::MtuUpdated),

            EventData::QuicVersionInformation { .. } =>
                EventType::QuicEventType(QuicEventType::VersionInformation),
            EventData::QuicAlpnInformation { .. } =>
                EventType::QuicEventType(QuicEventType::AlpnInformation),
            EventData::QuicParametersSet { .. } =>
                EventType::QuicEventType(QuicEventType::ParametersSet),
            EventData::QuicParametersRestored { .. } =>
                EventType::QuicEventType(QuicEventType::ParametersRestored),
            EventData::QuicPacketSent { .. } =>
                EventType::QuicEventType(QuicEventType::PacketSent),
            EventData::QuicPacketReceived { .. } =>
                EventType::QuicEventType(QuicEventType::PacketReceived),
            EventData::QuicPacketDropped { .. } =>
                EventType::QuicEventType(QuicEventType::PacketDropped),
            EventData::QuicPacketBuffered { .. } =>
                EventType::QuicEventType(QuicEventType::PacketBuffered),
            EventData::QuicPacketsAcked { .. } =>
                EventType::QuicEventType(QuicEventType::PacketsAcked),
            EventData::QuicUdpDatagramsSent { .. } =>
                EventType::QuicEventType(QuicEventType::UdpDatagramsSent),
            EventData::QuicUdpDatagramsReceived { .. } =>
                EventType::QuicEventType(QuicEventType::UdpDatagramsReceived),
            EventData::QuicUdpDatagramDropped { .. } =>
                EventType::QuicEventType(QuicEventType::UdpDatagramDropped),
            EventData::QuicStreamStateUpdated { .. } =>
                EventType::QuicEventType(QuicEventType::StreamStateUpdated),
            EventData::QuicFramesProcessed { .. } =>
                EventType::QuicEventType(QuicEventType::FramesProcessed),
            EventData::QuicStreamDataMoved { .. } =>
                EventType::QuicEventType(QuicEventType::StreamDataMoved),
            EventData::QuicDatagramDataMoved { .. } =>
                EventType::QuicEventType(QuicEventType::DatagramDataMoved),
            EventData::QuicConnectionDataBlockedUpdated { .. } =>
                EventType::QuicEventType(
                    QuicEventType::ConnectionDataBlockedUpdated,
                ),
            EventData::QuicStreamDataBlockedUpdated { .. } =>
                EventType::QuicEventType(QuicEventType::StreamDataBlockedUpdated),
            EventData::QuicDatagramDataBlockedUpdated { .. } =>
                EventType::QuicEventType(
                    QuicEventType::DatagramDataBlockedUpdated,
                ),
            EventData::QuicMigrationStateUpdated { .. } =>
                EventType::QuicEventType(QuicEventType::MigrationStateUpdated),

            EventData::QuicKeyUpdated { .. } =>
                EventType::QuicEventType(QuicEventType::KeyUpdated),
            EventData::QuicKeyDiscarded { .. } =>
                EventType::QuicEventType(QuicEventType::KeyDiscarded),

            EventData::QuicRecoveryParametersSet { .. } =>
                EventType::QuicEventType(QuicEventType::RecoveryParametersSet),
            EventData::QuicMetricsUpdated { .. } =>
                EventType::QuicEventType(QuicEventType::RecoveryMetricsUpdated),
            EventData::QuicCongestionStateUpdated { .. } =>
                EventType::QuicEventType(QuicEventType::CongestionStateUpdated),
            EventData::QuicTimerUpdated { .. } =>
                EventType::QuicEventType(QuicEventType::TimerUpdated),
            EventData::QuicPacketLost { .. } =>
                EventType::QuicEventType(QuicEventType::PacketLost),
            EventData::QuicMarkedForRetransmit { .. } =>
                EventType::QuicEventType(QuicEventType::MarkedForRetransmit),
            EventData::QuicEcnStateUpdated { .. } =>
                EventType::QuicEventType(QuicEventType::EcnStateUpdated),

            EventData::Http3ParametersSet { .. } =>
                EventType::Http3EventType(Http3EventType::ParametersSet),
            EventData::Http3ParametersRestored { .. } =>
                EventType::Http3EventType(Http3EventType::ParametersRestored),
            EventData::Http3StreamTypeSet { .. } =>
                EventType::Http3EventType(Http3EventType::StreamTypeSet),
            EventData::Http3PriorityUpdated { .. } =>
                EventType::Http3EventType(Http3EventType::PriorityUpdated),
            EventData::Http3FrameCreated { .. } =>
                EventType::Http3EventType(Http3EventType::FrameCreated),
            EventData::Http3FrameParsed { .. } =>
                EventType::Http3EventType(Http3EventType::FrameParsed),
            EventData::Http3DatagramCreated { .. } =>
                EventType::Http3EventType(Http3EventType::DatagramCreated),
            EventData::Http3DatagramParsed { .. } =>
                EventType::Http3EventType(Http3EventType::DatagramParsed),
            EventData::Http3PushResolved { .. } =>
                EventType::Http3EventType(Http3EventType::PushResolved),

            EventData::LogLevelError { .. } =>
                EventType::LogLevelEventType(LogLevelEventType::Error),
            EventData::LogLevelWarning { .. } =>
                EventType::LogLevelEventType(LogLevelEventType::Warning),
            EventData::LogLevelInfo { .. } =>
                EventType::LogLevelEventType(LogLevelEventType::Info),
            EventData::LogLevelDebug { .. } =>
                EventType::LogLevelEventType(LogLevelEventType::Debug),
            EventData::LogLevelVerbose { .. } =>
                EventType::LogLevelEventType(LogLevelEventType::Verbose),
            //_ => EventType::None,
        }
    }
}

#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
#[serde(rename_all = "snake_case")]
pub enum DataRecipient {
    User,
    Application,
    Transport,
    Network,
    Dropped,
}

#[serde_with::skip_serializing_none]
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug, Default)]
pub struct RawInfo {
    pub length: Option<u64>,
    pub payload_length: Option<u64>,

    pub data: Option<Box<Bytes>>,
}

#[serde_with::skip_serializing_none]
#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
#[serde(tag = "name", content = "data")]
#[allow(clippy::large_enum_variant)]
pub enum EventData {
    // QUIC
    #[serde(rename = "quic:server_listening")]
    QuicServerListening(quic::ServerListening),

    #[serde(rename = "quic:connection_started")]
    QuicConnectionStarted(quic::ConnectionStarted),

    #[serde(rename = "quic:connection_closed")]
    QuicConnectionClosed(quic::ConnectionClosed),

    #[serde(rename = "quic:connection_id_updated")]
    QuicConnectionIdUpdated(quic::ConnectionIdUpdated),

    #[serde(rename = "quic:spin_bit_updated")]
    QuicSpinBitUpdated(quic::SpinBitUpdated),

    #[serde(rename = "quic:connection_state_updated")]
    QuicConnectionStateUpdated(quic::ConnectionStateUpdated),

    #[serde(rename = "quic:tuple_assigned")]
    QuicTupleAssigned(quic::TupleAssigned),

    #[serde(rename = "quic:mtu_updated")]
    QuicMtuUpdated(quic::MtuUpdated),

    #[serde(rename = "quic:version_information")]
    QuicVersionInformation(quic::QuicVersionInformation),

    #[serde(rename = "quic:alpn_information")]
    QuicAlpnInformation(quic::AlpnInformation),

    #[serde(rename = "quic:parameters_set")]
    QuicParametersSet(Box<quic::ParametersSet>),

    #[serde(rename = "quic:parameters_restored")]
    QuicParametersRestored(quic::ParametersRestored),

    #[serde(rename = "quic:packet_sent")]
    QuicPacketSent(quic::PacketSent),

    #[serde(rename = "quic:packet_received")]
    QuicPacketReceived(quic::PacketReceived),

    #[serde(rename = "quic:packet_dropped")]
    QuicPacketDropped(quic::PacketDropped),

    #[serde(rename = "quic:packet_buffered")]
    QuicPacketBuffered(quic::PacketBuffered),

    #[serde(rename = "quic:packets_acked")]
    QuicPacketsAcked(quic::PacketsAcked),

    #[serde(rename = "quic:datagrams_sent")]
    QuicUdpDatagramsSent(quic::UdpDatagramsSent),

    #[serde(rename = "quic:datagrams_received")]
    QuicUdpDatagramsReceived(quic::UdpDatagramsReceived),

    #[serde(rename = "quic:datagram_dropped")]
    QuicUdpDatagramDropped(quic::UdpDatagramDropped),

    #[serde(rename = "quic:stream_state_updated")]
    QuicStreamStateUpdated(quic::StreamStateUpdated),

    #[serde(rename = "quic:frames_processed")]
    QuicFramesProcessed(quic::FramesProcessed),

    #[serde(rename = "quic:stream_data_moved")]
    QuicStreamDataMoved(quic::StreamDataMoved),

    #[serde(rename = "quic:datagram_data_moved")]
    QuicDatagramDataMoved(quic::DatagramDataMoved),

    #[serde(rename = "quic:connection_data_blocked_updated")]
    QuicConnectionDataBlockedUpdated(quic::ConnectionDataBlockedUpdated),

    #[serde(rename = "quic:stream_data_blocked_updated")]
    QuicStreamDataBlockedUpdated(quic::StreamDataBlockedUpdated),

    #[serde(rename = "quic:datagram_data_blocked_updated")]
    QuicDatagramDataBlockedUpdated(quic::DatagramDataBlockedUpdated),

    #[serde(rename = "quic:migration_state_updated")]
    QuicMigrationStateUpdated(quic::MigrationStateUpdated),

    #[serde(rename = "quic:key_updated")]
    QuicKeyUpdated(quic::KeyUpdated),

    #[serde(rename = "quic:key_retired")]
    QuicKeyDiscarded(quic::KeyDiscarded),

    #[serde(rename = "quic:recovery_parameters_set")]
    QuicRecoveryParametersSet(quic::RecoveryParametersSet),

    #[serde(rename = "quic:recovery_metrics_updated")]
    QuicMetricsUpdated(quic::RecoveryMetricsUpdated),

    #[serde(rename = "quic:congestion_state_updated")]
    QuicCongestionStateUpdated(quic::CongestionStateUpdated),

    #[serde(rename = "quic:timer_updated")]
    QuicTimerUpdated(quic::TimerUpdated),

    #[serde(rename = "quic:packet_lost")]
    QuicPacketLost(quic::PacketLost),

    #[serde(rename = "quic:marked_for_retransmit")]
    QuicMarkedForRetransmit(quic::MarkedForRetransmit),

    #[serde(rename = "quic:ecn_state_updated")]
    QuicEcnStateUpdated(quic::EcnStateUpdated),

    // HTTP/3
    #[serde(rename = "http3:parameters_set")]
    Http3ParametersSet(http3::ParametersSet),

    #[serde(rename = "http3:parameters_restored")]
    Http3ParametersRestored(http3::ParametersRestored),

    #[serde(rename = "http3:stream_type_set")]
    Http3StreamTypeSet(http3::StreamTypeSet),

    #[serde(rename = "http3:priority_updated")]
    Http3PriorityUpdated(http3::PriorityUpdated),

    #[serde(rename = "http3:frame_created")]
    Http3FrameCreated(http3::FrameCreated),

    #[serde(rename = "http3:frame_parsed")]
    Http3FrameParsed(http3::FrameParsed),

    #[serde(rename = "http3:datagram_created")]
    Http3DatagramCreated(http3::DatagramCreated),

    #[serde(rename = "http3:datagram_parsed")]
    Http3DatagramParsed(http3::DatagramParsed),

    #[serde(rename = "http3:push_resolved")]
    Http3PushResolved(http3::PushResolved),

    // LogLevel
    #[serde(rename = "loglevel:error")]
    LogLevelError {
        code: Option<u64>,
        message: Option<String>,
    },

    #[serde(rename = "loglevel:warning")]
    LogLevelWarning {
        code: Option<u64>,
        message: Option<String>,
    },

    #[serde(rename = "loglevel:info")]
    LogLevelInfo {
        code: Option<u64>,
        message: Option<String>,
    },

    #[serde(rename = "loglevel:debug")]
    LogLevelDebug {
        code: Option<u64>,
        message: Option<String>,
    },

    #[serde(rename = "loglevel:verbose")]
    LogLevelVerbose {
        code: Option<u64>,
        message: Option<String>,
    },
}

impl EventData {
    /// Returns size of `EventData` array of `QuicFrame`s if it exists.
    pub fn contains_quic_frames(&self) -> Option<usize> {
        // For some EventData variants, the frame array is optional
        // but for others it is mandatory.
        match self {
            EventData::QuicPacketSent(pkt) =>
                pkt.frames.as_ref().map(|f| f.len()),

            EventData::QuicPacketReceived(pkt) =>
                pkt.frames.as_ref().map(|f| f.len()),

            EventData::QuicPacketLost(pkt) =>
                pkt.frames.as_ref().map(|f| f.len()),

            EventData::QuicMarkedForRetransmit(ev) => Some(ev.frames.len()),
            EventData::QuicFramesProcessed(ev) => Some(ev.frames.len()),

            _ => None,
        }
    }
}

#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Debug)]
#[serde(rename_all = "snake_case")]
pub enum LogLevelEventType {
    Error,
    Warning,
    Info,
    Debug,
    Verbose,
}

#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
#[serde(untagged)]
pub enum ConnectionClosedEventError {
    TransportError(TransportError),
    CryptoError(CryptoError),
}

#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
#[serde(untagged)]
pub enum ConnectionClosedFrameError {
    TransportError(TransportError),
    ApplicationError(ApplicationError),
    CryptoError(CryptoError),
}

#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
#[serde(rename_all = "snake_case")]
pub enum ApplicationError {
    HttpNoError,
    HttpGeneralProtocolError,
    HttpInternalError,
    HttpRequestCancelled,
    HttpIncompleteRequest,
    HttpConnectError,
    HttpFrameError,
    HttpExcessiveLoad,
    HttpVersionFallback,
    HttpIdError,
    HttpStreamCreationError,
    HttpClosedCriticalStream,
    HttpEarlyResponse,
    HttpMissingSettings,
    HttpUnexpectedFrame,
    HttpRequestRejection,
    HttpSettingsError,
    Unknown,
}

// TODO
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
#[serde(rename_all = "snake_case")]
pub enum CryptoError {
    Prefix,
}

#[serde_with::skip_serializing_none]
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
pub struct TupleEndpointInfo {
    pub ip_v4: Option<String>,
    pub port_v4: Option<u16>,
    pub ip_v6: Option<String>,
    pub port_v6: Option<u16>,

    pub connection_ids: Option<Vec<Bytes>>,
}

pub mod http3;
pub mod quic;
