use derive_builder::Builder;
use serde::{Deserialize, Serialize};

use super::KeyType;
use crate::HexString;

/// The key_updated event has Base importance level; see Section 9.2 of
/// [QLOG-MAIN]
///
/// [QLOG-MAIN]: https://datatracker.ietf.org/doc/html/draft-ietf-quic-qlog-main-schema-09
#[serde_with::skip_serializing_none]
#[derive(Builder, Debug, Clone, Serialize, Deserialize, PartialEq)]
#[builder(setter(into, strip_option), build_fn(private, name = "fallible_build"))]
pub struct KeyUpdated {
    key_type: KeyType,
    #[builder(default)]
    old: Option<HexString>,
    #[builder(default)]
    new: Option<HexString>,

    /// needed for 1RTT key updates
    #[builder(default)]
    key_phase: Option<u64>,
    #[builder(default)]
    trigger: Option<KeyUpdatedTrigger>,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum KeyUpdatedTrigger {
    /// (e.g., initial, handshake and 0-RTT keys
    /// are generated by TLS)
    Tls,
    RemoteUpdate,
    LocalUpdate,
}

/// The key_discarded event has Base importance level; see Section 9.2 of
/// [QLOG-MAIN].
///
/// [QLOG-MAIN]: https://datatracker.ietf.org/doc/html/draft-ietf-quic-qlog-main-schema-09
#[serde_with::skip_serializing_none]
#[derive(Builder, Debug, Clone, Serialize, Deserialize, PartialEq)]
#[builder(setter(into, strip_option), build_fn(private, name = "fallible_build"))]
pub struct KeyDiscarded {
    key_type: KeyType,
    #[builder(default)]
    key: Option<HexString>,

    /// needed for 1RTT key updates
    key_phase: Option<u64>,
    #[builder(default)]
    trigger: Option<KeyDiscardedTrigger>,
}

#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum KeyDiscardedTrigger {
    /// (e.g., initial, handshake and 0-RTT keys
    /// are generated by TLS)
    Tls,
    RemoteUpdate,
    LocalUpdate,
}

crate::gen_builder_method! {
    KeyUpdatedBuilder   => KeyUpdated;
    KeyDiscardedBuilder => KeyDiscarded;
}

mod rollback {
    use super::*;
    use crate::{build, legacy::quic as legacy};

    impl From<KeyType> for legacy::KeyType {
        fn from(value: KeyType) -> Self {
            match value {
                KeyType::ServerInitialSecret => legacy::KeyType::ServerInitialSecret,
                KeyType::ClientInitialSecret => legacy::KeyType::ClientInitialSecret,
                KeyType::ServerHandshakeSecret => legacy::KeyType::ServerHandshakeSecret,
                KeyType::ClientHandshakeSecret => legacy::KeyType::ClientHandshakeSecret,
                KeyType::Server0RttSecret => legacy::KeyType::Server0RTTSecret,
                KeyType::Client0RttSecret => legacy::KeyType::Client0RTTSecret,
                KeyType::Server1RttSecret => legacy::KeyType::Server1RTTSecret,
                KeyType::Client1RttSecret => legacy::KeyType::Client1RTTSecret,
            }
        }
    }

    impl From<KeyUpdatedTrigger> for legacy::SecurityKeyUpdatedTrigger {
        #[inline]
        fn from(value: KeyUpdatedTrigger) -> Self {
            match value {
                KeyUpdatedTrigger::Tls => legacy::SecurityKeyUpdatedTrigger::Tls,
                KeyUpdatedTrigger::RemoteUpdate => legacy::SecurityKeyUpdatedTrigger::RemoteUpdate,
                KeyUpdatedTrigger::LocalUpdate => legacy::SecurityKeyUpdatedTrigger::LocalUpdate,
            }
        }
    }

    impl From<KeyUpdated> for legacy::SecurityKeyUpdated {
        #[inline]
        fn from(value: KeyUpdated) -> Self {
            build!(legacy::SecurityKeyUpdated {
                key_type: value.key_type,
                ?old: value.old,
                // for legacy new is not optional
                ?new: value.new,
                // is this key_phase?
                ?generation: value.key_phase.map(|p| p as u32),
                ?trigger: value.trigger,
            })
        }
    }

    impl From<KeyDiscardedTrigger> for legacy::SecurityKeyRetiredTrigger {
        #[inline]
        fn from(value: KeyDiscardedTrigger) -> Self {
            match value {
                KeyDiscardedTrigger::Tls => legacy::SecurityKeyRetiredTrigger::Tls,
                KeyDiscardedTrigger::RemoteUpdate => {
                    legacy::SecurityKeyRetiredTrigger::RemoteUpdate
                }
                KeyDiscardedTrigger::LocalUpdate => legacy::SecurityKeyRetiredTrigger::LocalUpdate,
            }
        }
    }

    impl From<KeyDiscarded> for legacy::SecurityKeyRetired {
        #[inline]
        fn from(value: KeyDiscarded) -> Self {
            build!(legacy::SecurityKeyRetired {
                key_type: value.key_type,
                ?key: value.key,
                // is this key_phase?
                ?generation: value.key_phase .map(|p| p as u32),
                ?trigger: value.trigger,
            })
        }
    }
}
