//! Peer-to-peer connections
//!
//! This module implements the `RTCPeerConnection` interface as defined in the
//! [W3C WebRTC specification](https://w3c.github.io/webrtc-pc/). It provides
//! the core functionality for establishing peer-to-peer connections, negotiating
//! media capabilities, and managing data channels.
//!
//! # Overview
//!
//! `RTCPeerConnection` is the central interface in WebRTC. It handles:
//!
//! - **Signaling**: Creating and exchanging SDP offers/answers
//! - **ICE**: Gathering candidates and establishing connectivity
//! - **Media**: Managing audio/video tracks and transceivers
//! - **Data**: Creating and managing data channels
//! - **Security**: DTLS encryption for all communication
//!
//! # Architecture
//!
//! This is a **sans-I/O** implementation, meaning it separates protocol logic
//! from I/O operations. The application is responsible for:
//!
//! - Transmitting/receiving network packets
//! - Managing the event loop
//! - Handling signaling channel communication
//!
//! ## Sans-I/O Benefits
//!
//! - **Flexibility**: Works with any I/O runtime (tokio, async-std, blocking, etc.)
//! - **Testability**: Protocol logic can be tested without network I/O
//! - **Control**: Application has full control over threading and scheduling
//!
//! # Connection Establishment
//!
//! The typical WebRTC connection flow:
//!
//! ```text
//! Peer A (Offerer)              Signaling Server              Peer B (Answerer)
//! ════════════════              ════════════════              ═══════════════════
//!      │                               │                               │
//!      │ 1. create_offer()             │                               │
//!      │─────────────────┐             │                               │
//!      │                 │             │                               │
//!      │<────────────────┘             │                               │
//!      │                               │                               │
//!      │ 2. set_local_description()    │                               │
//!      │─────────────────┐             │                               │
//!      │                 │             │                               │
//!      │<────────────────┘             │                               │
//!      │                               │                               │
//!      │ 3. send offer (via signaling) │                               │
//!      │──────────────────────────────>│──────────────────────────────>│
//!      │                               │                               │
//!      │                               │  4. set_remote_description()  │
//!      │                               │                  ┌────────────┤
//!      │                               │                  │            │
//!      │                               │                  └───────────>│
//!      │                               │                               │
//!      │                               │       5. create_answer()      │
//!      │                               │                  ┌────────────┤
//!      │                               │                  │            │
//!      │                               │                  └───────────>│
//!      │                               │                               │
//!      │                               │  6. set_local_description()   │
//!      │                               │                  ┌────────────┤
//!      │                               │                  │            │
//!      │                               │                  └───────────>│
//!      │                               │                               │
//!      │ 7. receive answer             │<──────────────────────────────│
//!      │<──────────────────────────────┤                               │
//!      │                               │                               │
//!      │ 8. set_remote_description()   │                               │
//!      │─────────────────┐             │                               │
//!      │                 │             │                               │
//!      │<────────────────┘             │                               │
//!      │                               │                               │
//!      │ 9. ICE candidates exchanged   │                               │
//!      │<─────────────────────────────────────────────────────────────>│
//!      │                               │                               │
//!      │ 10. Media/data flows directly │                               │
//!      │<═════════════════════════════════════════════════════════════>│
//! ```
//!
//! # Examples
//!
//! ## Creating a Peer Connection
//!
//! ```
//! use rtc::peer_connection::RTCPeerConnectionBuilder;
//!
//! # fn example() -> Result<(), Box<dyn std::error::Error>> {
//! // Create with default configuration
//! let mut pc = RTCPeerConnectionBuilder::new().build()?;
//! # Ok(())
//! # }
//! ```
//!
//! ## Creating an Offer (Initiating Peer)
//!
//! ```no_run
//! use rtc::peer_connection::RTCPeerConnectionBuilder;
//!
//! # fn example() -> Result<(), Box<dyn std::error::Error>> {
//! let mut pc = RTCPeerConnectionBuilder::new().build()?;
//!
//! // Add media track or data channel first
//! // pc.add_track(audio_track)?;
//!
//! // Create offer
//! let offer = pc.create_offer(None)?;
//!
//! // Set as local description
//! pc.set_local_description(offer.clone())?;
//!
//! // Send offer.sdp to remote peer via signaling channel
//! // signaling_channel.send(offer.sdp)?;
//! # Ok(())
//! # }
//! ```
//!
//! ## Answering an Offer (Responding Peer)
//!
//! ```no_run
//! use rtc::peer_connection::RTCPeerConnectionBuilder;
//! use rtc::peer_connection::sdp::RTCSessionDescription;
//!
//! # fn example(remote_offer_sdp: String) -> Result<(), Box<dyn std::error::Error>> {
//! let mut pc = RTCPeerConnectionBuilder::new().build()?;
//!
//! // Receive offer from remote peer
//! let offer = RTCSessionDescription::offer(remote_offer_sdp)?;
//!
//! // Set as remote description
//! pc.set_remote_description(offer)?;
//!
//! // Create answer
//! let answer = pc.create_answer(None)?;
//!
//! // Set as local description
//! pc.set_local_description(answer.clone())?;
//!
//! // Send answer.sdp to remote peer via signaling channel
//! // signaling_channel.send(answer.sdp)?;
//! # Ok(())
//! # }
//! ```
//!
//! ## Adding Media Tracks
//!
//! ```no_run
//! use rtc::peer_connection::RTCPeerConnectionBuilder;
//! use rtc::media_stream::MediaStreamTrack;
//! use rtc::rtp_transceiver::rtp_sender::RtpCodecKind;
//!
//! # fn example(audio_track: MediaStreamTrack) -> Result<(), Box<dyn std::error::Error>> {
//! let mut pc = RTCPeerConnectionBuilder::new().build()?;
//!
//! // Add an audio track
//! let sender_id = pc.add_track(audio_track)?;
//!
//! // Or add a transceiver for receiving
//! let transceiver_id = pc.add_transceiver_from_kind(RtpCodecKind::Video, None)?;
//! # Ok(())
//! # }
//! ```
//!
//! ## Creating Data Channels
//!
//! ```no_run
//! use rtc::peer_connection::RTCPeerConnectionBuilder;
//! use rtc::data_channel::RTCDataChannelInit;
//!
//! # fn example() -> Result<(), Box<dyn std::error::Error>> {
//! let mut pc = RTCPeerConnectionBuilder::new().build()?;
//!
//! // Create a reliable, ordered data channel
//! let init = RTCDataChannelInit {
//!     ordered: true,
//!     max_retransmits: None,
//!     ..Default::default()
//! };
//!
//! let channel_id = pc.create_data_channel("my-channel", Some(init))?;
//! # Ok(())
//! # }
//! ```
//!
//! ## ICE Candidate Exchange
//!
//! ```no_run
//! use rtc::peer_connection::{RTCPeerConnection, RTCPeerConnectionBuilder};
//! use rtc::peer_connection::transport::RTCIceCandidateInit;
//!
//! # fn example(mut pc: RTCPeerConnection) -> Result<(), Box<dyn std::error::Error>> {
//! // When local candidates are gathered, send to remote peer
//! // (In sans-I/O, you'd poll for events to get candidates)
//!
//! // When receiving remote candidate from signaling channel
//! let remote_candidate = RTCIceCandidateInit {
//!     candidate: "candidate:1 1 UDP 2130706431 192.168.1.100 54321 typ host".to_string(),
//!     ..Default::default()
//! };
//!
//! pc.add_remote_candidate(remote_candidate)?;
//! # Ok(())
//! # }
//! ```
//!
//! # State Management
//!
//! The peer connection maintains several state machines:
//!
//! - **Signaling State**: SDP negotiation progress (stable, have-local-offer, etc.)
//! - **ICE Connection State**: Network connectivity status
//! - **ICE Gathering State**: Candidate gathering progress
//! - **Connection State**: Overall connection health
//!
//! Monitor these states through the event system (sans-I/O polling).
//!
//! # Thread Safety
//!
//! `RTCPeerConnection` is **not** thread-safe. The application must ensure
//! exclusive access or use appropriate synchronization primitives.
//!
//! # Specifications
//!
//! - [W3C WebRTC 1.0] - Main specification
//! - [RFC 8829] - JSEP: JavaScript Session Establishment Protocol
//! - [RFC 8866] - SDP: Session Description Protocol
//! - [RFC 8445] - ICE: Interactive Connectivity Establishment
//! - [RFC 8831] - WebRTC Data Channels
//!
//! [W3C WebRTC 1.0]: https://w3c.github.io/webrtc-pc/
//! [RFC 8829]: https://datatracker.ietf.org/doc/html/rfc8829
//! [RFC 8866]: https://datatracker.ietf.org/doc/html/rfc8866
//! [RFC 8445]: https://datatracker.ietf.org/doc/html/rfc8445
//! [RFC 8831]: https://datatracker.ietf.org/doc/html/rfc8831

pub mod certificate;
pub mod configuration;
pub mod event;
pub(crate) mod handler;
mod internal;
pub mod message;
pub mod sdp;
pub mod state;
pub mod transport;

use crate::data_channel::init::RTCDataChannelInit;
use crate::data_channel::parameters::DataChannelParameters;
use crate::data_channel::{RTCDataChannel, RTCDataChannelId, internal::RTCDataChannelInternal};
use crate::media_stream::track::MediaStreamTrack;
use crate::peer_connection::configuration::media_engine::MediaEngine;
use crate::peer_connection::configuration::setting_engine::{SctpMaxMessageSize, SettingEngine};
use crate::peer_connection::configuration::{
    RTCConfiguration, RTCIceTransportPolicy,
    offer_answer_options::{RTCAnswerOptions, RTCOfferOptions},
};
use crate::peer_connection::event::RTCPeerConnectionEvent;
use crate::peer_connection::handler::PipelineContext;
use crate::peer_connection::handler::dtls::DtlsHandlerContext;
use crate::peer_connection::handler::ice::IceHandlerContext;
use crate::peer_connection::handler::sctp::SctpHandlerContext;
use crate::peer_connection::sdp::session_description::RTCSessionDescription;
use crate::peer_connection::sdp::{
    extract_fingerprint, extract_ice_details, get_application_media_section_max_message_size,
    get_application_media_section_sctp_port, get_mid_value, get_peer_direction,
    has_ice_trickle_option, is_lite_set, sdp_type::RTCSdpType, update_sdp_origin,
};
use crate::peer_connection::state::RTCIceGatheringState;
use crate::peer_connection::state::ice_connection_state::RTCIceConnectionState;
use crate::peer_connection::state::peer_connection_state::{
    NegotiationNeededState, RTCPeerConnectionState,
};
use crate::peer_connection::state::signaling_state::{RTCSignalingState, StateChangeOp};
use crate::peer_connection::transport::dtls::RTCDtlsTransport;
use crate::peer_connection::transport::dtls::fingerprint::RTCDtlsFingerprint;
use crate::peer_connection::transport::dtls::parameters::DTLSParameters;
use crate::peer_connection::transport::dtls::role::{
    DEFAULT_DTLS_ROLE_ANSWER, DEFAULT_DTLS_ROLE_OFFER, RTCDtlsRole,
};
use crate::peer_connection::transport::ice::RTCIceTransport;
use crate::peer_connection::transport::ice::candidate::RTCIceCandidateInit;
use crate::peer_connection::transport::ice::parameters::RTCIceParameters;
use crate::peer_connection::transport::ice::role::RTCIceRole;
use crate::peer_connection::transport::sctp::RTCSctpTransport;
use crate::peer_connection::transport::sctp::capabilities::SCTPTransportCapabilities;
use crate::rtp_transceiver::direction::RTCRtpTransceiverDirection;
use crate::rtp_transceiver::rtp_receiver::RTCRtpReceiver;
use crate::rtp_transceiver::rtp_sender::RTCRtpCodecParameters;
use crate::rtp_transceiver::rtp_sender::RTCRtpSender;
use crate::rtp_transceiver::rtp_sender::internal::RTCRtpSenderInternal;
use crate::rtp_transceiver::rtp_sender::rtp_codec::{
    CodecMatch, RtpCodecKind, codec_parameters_fuzzy_search,
};
use crate::rtp_transceiver::{
    RTCRtpReceiverId, RTCRtpSenderId, RTCRtpTransceiver, RTCRtpTransceiverId,
    RTCRtpTransceiverInit, internal::RTCRtpTransceiverInternal,
};
use crate::statistics::StatsSelector;
use crate::statistics::accumulator::RTCStatsAccumulator;
use crate::statistics::report::RTCStatsReport;
use ::sdp::description::session::Origin;
use ::sdp::util::ConnectionRole;
use ice::AgentConfig;
use ice::candidate::{Candidate, unmarshal_candidate};
use interceptor::{Interceptor, NoopInterceptor, Registry};
use sdp::MEDIA_SECTION_APPLICATION;
use shared::error::{Error, Result};
use shared::util::math_rand_alpha;
use std::collections::HashMap;
use std::time::Instant;

/// Builder for creating RTCPeerConnection instances.
///
/// This builder provides a fluent API for configuring peer connections with:
/// - ICE servers (STUN/TURN) via [`RTCConfiguration`]
/// - Media codecs and RTP extensions via [`MediaEngine`]
/// - Low-level transport settings via [`SettingEngine`]
/// - RTP/RTCP interceptors for NACK, TWCC, and RTCP reports
///
/// # Examples
///
/// ## Basic peer connection
///
/// ```
/// use rtc::peer_connection::RTCPeerConnectionBuilder;
///
/// # fn example() -> Result<(), Box<dyn std::error::Error>> {
/// let pc = RTCPeerConnectionBuilder::new().build()?;
/// # Ok(())
/// # }
/// ```
///
/// ## With ICE servers
///
/// ```
/// use rtc::peer_connection::RTCPeerConnectionBuilder;
/// use rtc::peer_connection::configuration::{RTCConfigurationBuilder, RTCIceServer};
///
/// # fn example() -> Result<(), Box<dyn std::error::Error>> {
/// let pc = RTCPeerConnectionBuilder::new()
///     .with_configuration(
///         RTCConfigurationBuilder::new()
///             .with_ice_servers(vec![RTCIceServer {
///                 urls: vec!["stun:stun.l.google.com:19302".to_string()],
///                 ..Default::default()
///             }])
///             .build()
///     )
///     .build()?;
/// # Ok(())
/// # }
/// ```
///
/// ## With custom media engine
///
/// ```
/// use rtc::peer_connection::RTCPeerConnectionBuilder;
/// use rtc::peer_connection::configuration::media_engine::MediaEngine;
///
/// # fn example() -> Result<(), Box<dyn std::error::Error>> {
/// let mut media_engine = MediaEngine::default();
/// media_engine.register_default_codecs()?;
///
/// let pc = RTCPeerConnectionBuilder::new()
///     .with_media_engine(media_engine)
///     .build()?;
/// # Ok(())
/// # }
/// ```
///
/// ## With interceptors
///
/// ```
/// use rtc::peer_connection::RTCPeerConnectionBuilder;
/// use rtc::peer_connection::configuration::media_engine::MediaEngine;
/// use rtc::peer_connection::configuration::interceptor_registry::register_default_interceptors;
/// use rtc::interceptor::Registry;
///
/// # fn example() -> Result<(), Box<dyn std::error::Error>> {
/// let mut media_engine = MediaEngine::default();
/// let registry = Registry::new();
/// let registry = register_default_interceptors(registry, &mut media_engine)?;
///
/// let pc = RTCPeerConnectionBuilder::new()
///     .with_media_engine(media_engine)
///     .with_interceptor_registry(registry)
///     .build()?;
/// # Ok(())
/// # }
/// ```
pub struct RTCPeerConnectionBuilder<I = NoopInterceptor>
where
    I: Interceptor,
{
    configuration: RTCConfiguration,
    media_engine: MediaEngine,
    setting_engine: SettingEngine,
    interceptor: I,
}

impl Default for RTCPeerConnectionBuilder<NoopInterceptor> {
    fn default() -> Self {
        Self {
            configuration: RTCConfiguration::default(),
            media_engine: MediaEngine::default(),
            setting_engine: SettingEngine::default(),
            interceptor: NoopInterceptor::new(),
        }
    }
}

// Non-generic impl block for associated functions that don't depend on I
impl RTCPeerConnectionBuilder<NoopInterceptor> {
    /// Creates a new RTCPeerConnectionBuilder with default configuration.
    ///
    /// The default builder includes:
    /// - Empty ICE server list
    /// - Default MediaEngine (no codecs registered)
    /// - Default SettingEngine (standard timeouts and limits)
    /// - NoopInterceptor (no RTP/RTCP processing)
    ///
    /// Use `with_*` methods to customize configuration before calling `build()`.
    ///
    /// # Examples
    ///
    /// ```
    /// use rtc::peer_connection::RTCPeerConnectionBuilder;
    ///
    /// # fn example() -> Result<(), Box<dyn std::error::Error>> {
    /// let pc = RTCPeerConnectionBuilder::new().build()?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn new() -> Self {
        Self::default()
    }
}

impl<I> RTCPeerConnectionBuilder<I>
where
    I: Interceptor,
{
    /// Sets the RTCConfiguration for the peer connection.
    ///
    /// The configuration includes ICE servers, transport policies, bundle policies,
    /// RTCP mux policies, and certificates.
    ///
    /// # Examples
    ///
    /// ```
    /// use rtc::peer_connection::RTCPeerConnectionBuilder;
    /// use rtc::peer_connection::configuration::{RTCConfigurationBuilder, RTCIceServer};
    ///
    /// # fn example() -> Result<(), Box<dyn std::error::Error>> {
    /// let config = RTCConfigurationBuilder::new()
    ///     .with_ice_servers(vec![RTCIceServer {
    ///         urls: vec!["stun:stun.l.google.com:19302".to_string()],
    ///         ..Default::default()
    ///     }])
    ///     .build();
    ///
    /// let pc = RTCPeerConnectionBuilder::new()
    ///     .with_configuration(config)
    ///     .build()?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn with_configuration(mut self, configuration: RTCConfiguration) -> Self {
        self.configuration = configuration;
        self
    }

    /// Sets the MediaEngine for the peer connection.
    ///
    /// The media engine configures codecs and RTP header extensions.
    ///
    /// # Examples
    ///
    /// ```
    /// use rtc::peer_connection::RTCPeerConnectionBuilder;
    /// use rtc::peer_connection::configuration::media_engine::MediaEngine;
    ///
    /// # fn example() -> Result<(), Box<dyn std::error::Error>> {
    /// let mut media_engine = MediaEngine::default();
    /// media_engine.register_default_codecs()?;
    ///
    /// let pc = RTCPeerConnectionBuilder::new()
    ///     .with_media_engine(media_engine)
    ///     .build()?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn with_media_engine(mut self, media_engine: MediaEngine) -> Self {
        self.media_engine = media_engine;
        self
    }

    /// Sets the SettingEngine for the peer connection.
    ///
    /// The setting engine configures low-level transport parameters including
    /// timeouts, buffer sizes, ICE settings, and SCTP parameters.
    ///
    /// # Examples
    ///
    /// ```
    /// use rtc::peer_connection::RTCPeerConnectionBuilder;
    /// use rtc::peer_connection::configuration::setting_engine::SettingEngine;
    /// use std::time::Duration;
    ///
    /// # fn example() -> Result<(), Box<dyn std::error::Error>> {
    /// let mut setting_engine = SettingEngine::default();
    /// setting_engine.set_ice_timeouts(
    ///     Some(Duration::from_secs(30)),
    ///     Some(Duration::from_secs(60)),
    ///     Some(Duration::from_millis(100)),
    /// );
    ///
    /// let pc = RTCPeerConnectionBuilder::new()
    ///     .with_setting_engine(setting_engine)
    ///     .build()?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn with_setting_engine(mut self, setting_engine: SettingEngine) -> Self {
        self.setting_engine = setting_engine;
        self
    }

    /// Configures the peer connection with an interceptor registry.
    ///
    /// Interceptors process RTP/RTCP packets as they flow through the pipeline,
    /// enabling features like:
    /// - NACK (Negative Acknowledgment) for packet loss recovery
    /// - TWCC (Transport-Wide Congestion Control) for bandwidth estimation
    /// - RTCP Reports for quality statistics
    ///
    /// This method changes the interceptor type from `NoopInterceptor` to the
    /// registry's interceptor type. It must be the last builder method called
    /// before `build()` when used.
    ///
    /// # Type Parameters
    ///
    /// * `P` - The interceptor type produced by the registry
    ///
    /// # Examples
    ///
    /// ```
    /// use rtc::peer_connection::RTCPeerConnectionBuilder;
    /// use rtc::peer_connection::configuration::media_engine::MediaEngine;
    /// use rtc::peer_connection::configuration::interceptor_registry::register_default_interceptors;
    /// use rtc::interceptor::Registry;
    ///
    /// # fn example() -> Result<(), Box<dyn std::error::Error>> {
    /// let mut media_engine = MediaEngine::default();
    /// let registry = Registry::new();
    /// let registry = register_default_interceptors(registry, &mut media_engine)?;
    ///
    /// let pc = RTCPeerConnectionBuilder::new()
    ///     .with_media_engine(media_engine)
    ///     .with_interceptor_registry(registry)
    ///     .build()?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn with_interceptor_registry<P>(
        self,
        interceptor_registry: Registry<P>,
    ) -> RTCPeerConnectionBuilder<P>
    where
        P: Interceptor,
    {
        RTCPeerConnectionBuilder {
            configuration: self.configuration,
            media_engine: self.media_engine,
            setting_engine: self.setting_engine,
            interceptor: interceptor_registry.build(),
        }
    }

    /// Builds the RTCPeerConnection with the configured settings.
    ///
    /// This method validates the configuration and creates a new peer connection.
    /// If validation fails (e.g., expired certificates, invalid ICE servers),
    /// an error is returned.
    ///
    /// # Errors
    ///
    /// Returns an error if:
    /// - Certificates have expired
    /// - ICE server URLs are invalid
    /// - Other validation checks fail
    ///
    /// # Examples
    ///
    /// ```
    /// use rtc::peer_connection::RTCPeerConnectionBuilder;
    ///
    /// # fn example() -> Result<(), Box<dyn std::error::Error>> {
    /// let pc = RTCPeerConnectionBuilder::new().build()?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn build(self) -> Result<RTCPeerConnection<I>> {
        RTCPeerConnection::new(
            self.configuration,
            self.media_engine,
            self.setting_engine,
            self.interceptor,
        )
    }
}

/// The `RTCPeerConnection` interface represents a WebRTC connection between the local computer
/// and a remote peer. It provides methods to connect to a remote peer, maintain and monitor
/// the connection, and close the connection once it's no longer needed.
///
/// This is a sans-I/O implementation following the [W3C WebRTC specification](https://www.w3.org/TR/webrtc/).
///
/// # Examples
///
/// ```
/// use rtc::peer_connection::RTCPeerConnectionBuilder;
///
/// # fn example() -> Result<(), Box<dyn std::error::Error>> {
/// let mut pc = RTCPeerConnectionBuilder::new().build()?;
/// # Ok(())
/// # }
/// ```
pub struct RTCPeerConnection<I = NoopInterceptor>
where
    I: Interceptor,
{
    //////////////////////////////////////////////////
    // PeerConnection WebRTC Spec Interface Definition
    //////////////////////////////////////////////////
    pub(crate) configuration: RTCConfiguration,
    pub(crate) media_engine: MediaEngine,
    pub(crate) setting_engine: SettingEngine,
    pub(crate) interceptor: I,

    local_description: Option<RTCSessionDescription>,
    current_local_description: Option<RTCSessionDescription>,
    pending_local_description: Option<RTCSessionDescription>,
    remote_description: Option<RTCSessionDescription>,
    current_remote_description: Option<RTCSessionDescription>,
    pending_remote_description: Option<RTCSessionDescription>,

    pub(crate) signaling_state: RTCSignalingState,
    pub(crate) peer_connection_state: RTCPeerConnectionState,
    can_trickle_ice_candidates: Option<bool>,

    //////////////////////////////////////////////////
    // PeerConnection Internal State Machine
    //////////////////////////////////////////////////
    pub(crate) pipeline_context: PipelineContext,
    pub(crate) data_channels: HashMap<RTCDataChannelId, RTCDataChannelInternal>,
    pub(super) rtp_transceivers: Vec<RTCRtpTransceiverInternal<I>>,

    greater_mid: isize,
    sdp_origin: Origin,
    last_offer: String,
    last_answer: String,

    ice_restart_requested: Option<RTCOfferOptions>,
    negotiation_needed_state: NegotiationNeededState,
    is_negotiation_ongoing: bool,
}

impl<I> RTCPeerConnection<I>
where
    I: Interceptor,
{
    /// Creates an SDP offer to start a new WebRTC connection to a remote peer.
    ///
    /// The offer includes information about the attached media tracks, codecs and options supported
    /// by the browser, and ICE candidates gathered by the ICE agent. This offer can be sent to a
    /// remote peer over a signaling channel to establish a connection.
    ///
    /// # Arguments
    ///
    /// * `options` - Optional configuration for the offer, such as whether to restart ICE.
    ///
    /// # Returns
    ///
    /// Returns an `RTCSessionDescription` containing the SDP offer.
    ///
    /// # Errors
    ///
    /// Returns an error if:
    /// - The peer connection is closed
    /// - There's an error generating the SDP
    ///
    /// # Specification
    ///
    /// See [createOffer](https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-createoffer)
    pub fn create_offer(
        &mut self,
        mut options: Option<RTCOfferOptions>,
    ) -> Result<RTCSessionDescription> {
        if self.peer_connection_state == RTCPeerConnectionState::Closed {
            return Err(Error::ErrConnectionClosed);
        }

        let is_ice_restart_requested = self
            .ice_restart_requested
            .take()
            .is_some_and(|options| options.ice_restart)
            || options.take().is_some_and(|options| options.ice_restart);

        if is_ice_restart_requested {
            self.ice_restart()?;
        }

        // include unmatched local transceivers
        // update the greater mid if the remote description provides a greater one
        if let Some(d) = self.current_remote_description.as_ref()
            && let Some(parsed) = &d.parsed
        {
            for media in &parsed.media_descriptions {
                if let Some(mid) = get_mid_value(media) {
                    if mid.is_empty() {
                        continue;
                    }
                    let numeric_mid = match mid.parse::<isize>() {
                        Ok(n) => n,
                        Err(_) => continue,
                    };
                    if numeric_mid > self.greater_mid {
                        self.greater_mid = numeric_mid;
                    }
                }
            }
        }
        for transceiver in &mut self.rtp_transceivers {
            if let Some(mid) = transceiver.mid()
                && !mid.is_empty()
            {
                if let Ok(numeric_mid) = mid.parse::<isize>()
                    && numeric_mid > self.greater_mid
                {
                    self.greater_mid = numeric_mid;
                }
            } else {
                self.greater_mid += 1;
                transceiver.set_mid(format!("{}", self.greater_mid))?;
            }
        }

        let mut d = if self.current_remote_description.is_none() {
            self.generate_unmatched_sdp()?
        } else {
            self.generate_matched_sdp(
                true, /*includeUnmatched */
                DEFAULT_DTLS_ROLE_OFFER.to_connection_role(),
                false,
            )?
        };

        update_sdp_origin(&mut self.sdp_origin, &mut d);

        let sdp = d.marshal();

        let offer = RTCSessionDescription {
            sdp_type: RTCSdpType::Offer,
            sdp,
            parsed: Some(d),
        };

        self.last_offer.clone_from(&offer.sdp);

        Ok(offer)
    }

    /// Creates an SDP answer in response to an offer received from a remote peer.
    ///
    /// The answer includes information about any media already attached to the session,
    /// codecs and options supported by the browser, and ICE candidates gathered by the ICE agent.
    ///
    /// # Arguments
    ///
    /// * `options` - Optional configuration for the answer (currently unused).
    ///
    /// # Returns
    ///
    /// Returns an `RTCSessionDescription` containing the SDP answer.
    ///
    /// # Errors
    ///
    /// Returns an error if:
    /// - No remote description has been set
    /// - The peer connection is closed
    /// - The signaling state is not `have-remote-offer` or `have-local-pranswer`
    ///
    /// # Specification
    ///
    /// See [createAnswer](https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-createanswer)
    /// Creates an SDP answer in response to an offer from a remote peer.
    ///
    /// This method must be called after `set_remote_description()` has been called
    /// with an offer. The answer describes which media formats and codecs this peer
    /// will accept and how the connection will be established.
    ///
    /// # Parameters
    ///
    /// - `options`: Optional answer configuration. Currently not used but reserved
    ///   for future extensions.
    ///
    /// # Returns
    ///
    /// Returns an `RTCSessionDescription` containing the SDP answer that should be
    /// set as the local description and sent to the remote peer.
    ///
    /// # Errors
    ///
    /// Returns an error if:
    /// - No remote description has been set (`ErrNoRemoteDescription`)
    /// - The peer connection is closed (`ErrConnectionClosed`)
    /// - The signaling state is incorrect (`ErrIncorrectSignalingState`)
    /// - SDP generation fails
    ///
    /// # Signaling State Requirements
    ///
    /// This method can only be called when the signaling state is:
    /// - `HaveRemoteOffer` - After receiving an initial offer
    /// - `HaveLocalPranswer` - After sending a provisional answer
    ///
    /// # Examples
    ///
    /// ## Basic Answer Flow
    ///
    /// ```no_run
    /// use rtc::peer_connection::RTCPeerConnectionBuilder;
    /// use rtc::peer_connection::sdp::RTCSessionDescription;
    ///
    /// # fn example(remote_offer_sdp: String) -> Result<(), Box<dyn std::error::Error>> {
    /// let mut pc = RTCPeerConnectionBuilder::new().build()?;
    ///
    /// // 1. Receive and set remote offer
    /// let offer = RTCSessionDescription::offer(remote_offer_sdp)?;
    /// pc.set_remote_description(offer)?;
    ///
    /// // 2. Create answer
    /// let answer = pc.create_answer(None)?;
    ///
    /// // 3. Set as local description
    /// pc.set_local_description(answer.clone())?;
    ///
    /// // 4. Send answer to remote peer
    /// // signaling_channel.send(answer.sdp)?;
    /// # Ok(())
    /// # }
    /// ```
    ///
    /// ## With Media Tracks
    ///
    /// ```no_run
    /// use rtc::peer_connection::RTCPeerConnectionBuilder;
    /// use rtc::peer_connection::sdp::RTCSessionDescription;
    /// use rtc::media_stream::MediaStreamTrack;
    ///
    /// # fn example(
    /// #     remote_offer_sdp: String,
    /// #     audio_track: MediaStreamTrack,
    /// # ) -> Result<(), Box<dyn std::error::Error>> {
    /// let mut pc = RTCPeerConnectionBuilder::new().build()?;
    ///
    /// // Set remote offer
    /// let offer = RTCSessionDescription::offer(remote_offer_sdp)?;
    /// pc.set_remote_description(offer)?;
    ///
    /// // Add local track before creating answer
    /// pc.add_track(audio_track)?;
    ///
    /// // Create answer (will include the track)
    /// let answer = pc.create_answer(None)?;
    /// pc.set_local_description(answer)?;
    /// # Ok(())
    /// # }
    /// ```
    ///
    /// # DTLS Role Selection
    ///
    /// The answer automatically determines the appropriate DTLS role:
    /// - Uses `answering_dtls_role` from settings if configured
    /// - Defaults to `Client` (active) for lower latency
    /// - Uses `Server` (passive) if remote is ICE-Lite
    ///
    /// # Specifications
    ///
    /// - [W3C RTCPeerConnection.createAnswer]
    /// - [RFC 8829 Section 5.3] - Generating an Answer
    ///
    /// [W3C RTCPeerConnection.createAnswer]: https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-createanswer
    /// [RFC 8829 Section 5.3]: https://datatracker.ietf.org/doc/html/rfc8829#section-5.3
    pub fn create_answer(
        &mut self,
        _options: Option<RTCAnswerOptions>,
    ) -> Result<RTCSessionDescription> {
        if self.remote_description().is_none() {
            return Err(Error::ErrNoRemoteDescription);
        }

        if self.peer_connection_state == RTCPeerConnectionState::Closed {
            return Err(Error::ErrConnectionClosed);
        }

        if self.signaling_state != RTCSignalingState::HaveRemoteOffer
            && self.signaling_state != RTCSignalingState::HaveLocalPranswer
        {
            return Err(Error::ErrIncorrectSignalingState);
        }

        let mut connection_role = self.setting_engine.answering_dtls_role.to_connection_role();
        if connection_role == ConnectionRole::Unspecified {
            connection_role = DEFAULT_DTLS_ROLE_ANSWER.to_connection_role();

            if let Some(remote_description) = self.remote_description()
                && let Some(parsed) = remote_description.parsed.as_ref()
                && is_lite_set(parsed)
                && !self.setting_engine.candidates.ice_lite
            {
                connection_role = RTCDtlsRole::Server.to_connection_role();
            }
        }

        let mut d = self.generate_matched_sdp(
            false, /*includeUnmatched */
            connection_role,
            self.setting_engine.ignore_rid_pause_for_recv,
        )?;

        update_sdp_origin(&mut self.sdp_origin, &mut d);

        let sdp = d.marshal();

        let answer = RTCSessionDescription {
            sdp_type: RTCSdpType::Answer,
            sdp,
            parsed: Some(d),
        };

        self.last_answer.clone_from(&answer.sdp);

        Ok(answer)
    }

    /// Sets the local description as part of the offer/answer negotiation.
    ///
    /// This changes the local description associated with the connection. If the `sdp` field
    /// is empty, an implicit description will be created based on the type.
    ///
    /// # Arguments
    ///
    /// * `local_description` - The local session description to set.
    ///
    /// # Errors
    ///
    /// Returns an error if:
    /// - The peer connection is closed
    /// - The SDP type is invalid
    /// - The SDP cannot be parsed
    ///
    /// # Specification
    ///
    /// See [setLocalDescription](https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-setlocaldescription)
    /// Sets the local description for this peer connection.
    ///
    /// This method applies a local SDP description (offer or answer) to the peer
    /// connection, updating the local media and transport configuration. It must be
    /// called after creating an offer or answer.
    ///
    /// # Parameters
    ///
    /// - `local_description`: The session description to set as the local description.
    ///   This should be an offer or answer created by `create_offer()` or `create_answer()`.
    ///
    /// # Returns
    ///
    /// Returns `Ok(())` on success.
    ///
    /// # Errors
    ///
    /// Returns an error if:
    /// - The peer connection is closed (`ErrConnectionClosed`)
    /// - The SDP type is invalid for the current signaling state
    /// - SDP parsing fails
    /// - Transport configuration fails
    ///
    /// # Signaling State Transitions
    ///
    /// Setting the local description causes signaling state transitions:
    ///
    /// - **Offer**: `Stable` → `HaveLocalOffer`
    /// - **Answer**: `HaveRemoteOffer` → `Stable`
    /// - **Pranswer**: `HaveRemoteOffer` → `HaveLocalPranswer`
    ///
    /// # Examples
    ///
    /// ## Setting Local Offer
    ///
    /// ```no_run
    /// use rtc::peer_connection::RTCPeerConnectionBuilder;
    ///
    /// # fn example() -> Result<(), Box<dyn std::error::Error>> {
    /// let mut pc = RTCPeerConnectionBuilder::new().build()?;
    ///
    /// // Create offer
    /// let offer = pc.create_offer(None)?;
    ///
    /// // Set as local description
    /// pc.set_local_description(offer.clone())?;
    ///
    /// // Now send offer.sdp to remote peer via signaling
    /// // signaling_channel.send(offer.sdp)?;
    /// # Ok(())
    /// # }
    /// ```
    ///
    /// ## Setting Local Answer
    ///
    /// ```no_run
    /// use rtc::peer_connection::RTCPeerConnectionBuilder;
    /// use rtc::peer_connection::sdp::RTCSessionDescription;
    ///
    /// # fn example(remote_offer_sdp: String) -> Result<(), Box<dyn std::error::Error>> {
    /// let mut pc = RTCPeerConnectionBuilder::new().build()?;
    ///
    /// // Set remote offer first
    /// let offer = RTCSessionDescription::offer(remote_offer_sdp)?;
    /// pc.set_remote_description(offer)?;
    ///
    /// // Create and set local answer
    /// let answer = pc.create_answer(None)?;
    /// pc.set_local_description(answer.clone())?;
    ///
    /// // Send answer to remote peer
    /// // signaling_channel.send(answer.sdp)?;
    /// # Ok(())
    /// # }
    /// ```
    ///
    /// # Empty SDP Handling (JSEP 5.4)
    ///
    /// If the SDP string is empty, the last offer or answer is reused:
    /// - For offers: Uses the last generated offer
    /// - For answers: Uses the last generated answer
    ///
    /// This allows re-applying descriptions without regenerating SDP.
    ///
    /// # Media and Transport Activation
    ///
    /// When setting a local answer:
    /// - RTP transceivers are activated
    /// - SCTP transport is started for data channels
    /// - Media can begin flowing
    ///
    /// # Specifications
    ///
    /// - [W3C RTCPeerConnection.setLocalDescription]
    /// - [RFC 8829 Section 5.4] - Setting the Session Description
    ///
    /// [W3C RTCPeerConnection.setLocalDescription]: https://w3c.github.io/webrtc-pc/#dom-peerconnection-setlocaldescription
    /// [RFC 8829 Section 5.4]: https://datatracker.ietf.org/doc/html/rfc8829#section-5.4
    pub fn set_local_description(
        &mut self,
        mut local_description: RTCSessionDescription,
    ) -> Result<()> {
        if self.peer_connection_state == RTCPeerConnectionState::Closed {
            return Err(Error::ErrConnectionClosed);
        }

        // JSEP 5.4
        if local_description.sdp.is_empty() {
            match local_description.sdp_type {
                RTCSdpType::Answer | RTCSdpType::Pranswer => {
                    local_description.sdp.clone_from(&self.last_answer);
                }
                RTCSdpType::Offer => {
                    local_description.sdp.clone_from(&self.last_offer);
                }
                RTCSdpType::Rollback => {
                    // WebRTC spec: rollback SDP is ignored, empty is allowed
                }
                _ => return Err(Error::ErrPeerConnSDPTypeInvalidValueSetLocalDescription),
            }
        }

        // Parse SDP (skip for rollback as content is ignored per spec)
        if local_description.sdp_type != RTCSdpType::Rollback {
            local_description.parsed = Some(local_description.unmarshal()?);
        }
        self.set_description(&local_description, StateChangeOp::SetLocal)?;

        let we_answer = local_description.sdp_type == RTCSdpType::Answer;
        if we_answer && let Some(parsed_local_description) = &local_description.parsed {
            // WebRTC Spec 1.0 https://www.w3.org/TR/webrtc/
            // Section 4.4.1.5
            for media in &parsed_local_description.media_descriptions {
                let mid_value = match get_mid_value(media) {
                    Some(mid) if !mid.is_empty() => mid,
                    _ => return Err(Error::ErrPeerConnLocalDescriptionWithoutMidValue),
                };

                if media.media_name.media == MEDIA_SECTION_APPLICATION {
                    continue;
                }

                let i = match RTCPeerConnection::find_by_mid(mid_value, &self.rtp_transceivers) {
                    Some(i) => i,
                    None => return Err(Error::ErrPeerConnTransceiverMidNil),
                };

                let kind = RtpCodecKind::from(media.media_name.media.as_str());
                let mut direction = get_peer_direction(media);
                if kind == RtpCodecKind::Unspecified
                    || direction == RTCRtpTransceiverDirection::Unspecified
                {
                    continue;
                }

                // If a transceiver is created by applying a remote description that has recvonly transceiver,
                // it will have no sender. In this case, the transceiver's current direction is set to inactive so
                // that the transceiver can be reused by next AddTrack.
                if direction == RTCRtpTransceiverDirection::Sendonly
                    && self.rtp_transceivers[i].sender().is_none()
                {
                    direction = RTCRtpTransceiverDirection::Inactive;
                }

                self.rtp_transceivers[i].set_current_direction(direction);
            }

            if let Some(remote_description) = self.remote_description().cloned()
                && let Some(parsed_remote_description) = remote_description.parsed.as_ref()
            {
                let (dtls_role, remote_caps, local_sctp_port, remote_sctp_port) = (
                    self.dtls_transport().role(),
                    SCTPTransportCapabilities {
                        max_message_size: get_application_media_section_max_message_size(
                            parsed_remote_description,
                        )
                        .unwrap_or(SctpMaxMessageSize::DEFAULT_MESSAGE_SIZE),
                    },
                    get_application_media_section_sctp_port(parsed_local_description)
                        .unwrap_or(5000),
                    get_application_media_section_sctp_port(parsed_remote_description)
                        .unwrap_or(5000),
                );

                // we_answer: we first call set_remote_description,
                // then, we create_answer() and set_local_description() here
                // Now we should have done SDP negotiation.
                // Therefore, it is ready to start sctp and rtp.
                self.sctp_transport_mut().start(
                    dtls_role,
                    remote_caps,
                    local_sctp_port,
                    remote_sctp_port,
                )?;
                self.start_rtp(remote_description)?;
            }
        }

        self.ice_transport_mut().ice_gathering_state = RTCIceGatheringState::Gathering;

        Ok(())
    }

    /// Returns the local session description.
    ///
    /// Returns `pending_local_description` if it is not null, otherwise returns
    /// `current_local_description`. This property is used to determine if
    /// `set_local_description` has already been called.
    ///
    /// # Specification
    ///
    /// See [localDescription](https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-localdescription)
    pub fn local_description(&self) -> Option<RTCSessionDescription> {
        if let Some(pending_local_description) = self.pending_local_description() {
            return Some(pending_local_description);
        }
        self.current_local_description()
    }

    /// Returns the current local description as last successfully negotiated since
    /// the last negotiation completed.
    ///
    /// This represents the local description from the last offer/answer exchange that was
    /// successfully applied, not including any offers currently being negotiated.
    ///
    /// Returns `None` if there is no current local description (e.g., before initial negotiation).
    ///
    /// # Specification
    ///
    /// See [currentLocalDescription](https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-currentlocaldescription)
    pub fn current_local_description(&self) -> Option<RTCSessionDescription> {
        self.populate_local_candidates(self.current_local_description.as_ref())
    }

    /// Returns the pending local description if it exists.
    ///
    /// This represents the local description from a call to `set_local_description()` whose
    /// corresponding remote description has not yet been applied. This is `None` if negotiation
    /// is not in progress or if a rollback has been performed.
    ///
    /// Returns `None` if there is no pending local description.
    ///
    /// # Specification
    ///
    /// See [pendingLocalDescription](https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-pendinglocaldescription)
    pub fn pending_local_description(&self) -> Option<RTCSessionDescription> {
        self.populate_local_candidates(self.pending_local_description.as_ref())
    }

    /// Returns whether the remote peer supports trickle ICE.
    ///
    /// This value is determined from the remote SDP description after `set_remote_description()`
    /// is called. It checks for "trickle" in the "ice-options" attribute per
    /// RFC 8838 and RFC 9429 section 4.1.17.
    ///
    /// Returns:
    /// - `None` if no remote description has been set yet (unknown)
    /// - `Some(true)` if the remote peer indicated trickle ICE support
    /// - `Some(false)` if the remote peer did not indicate support
    ///
    /// # Specification
    ///
    /// See [canTrickleIceCandidates](https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-cantrickleicecandidates)
    pub fn can_trickle_ice_candidates(&self) -> Option<bool> {
        self.can_trickle_ice_candidates
    }

    /// Sets the remote description as part of the offer/answer negotiation.
    ///
    /// This changes the remote description associated with the connection. This description
    /// specifies the properties of the remote end of the connection, including the media format.
    ///
    /// # Arguments
    ///
    /// * `remote_description` - The remote session description to set.
    ///
    /// # Errors
    ///
    /// Returns an error if:
    /// - The peer connection is closed
    /// - The SDP cannot be parsed
    /// - The media engine fails to update from the remote description
    ///
    /// # Specification
    ///
    /// See [setRemoteDescription](https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-setremotedescription)
    pub fn set_remote_description(
        &mut self,
        mut remote_description: RTCSessionDescription,
    ) -> Result<()> {
        if self.peer_connection_state == RTCPeerConnectionState::Closed {
            return Err(Error::ErrConnectionClosed);
        }

        let is_renegotiation = self.current_remote_description.is_some();

        // Parse SDP (skip for rollback as content is ignored per spec)
        if remote_description.sdp_type != RTCSdpType::Rollback {
            remote_description.parsed = Some(remote_description.unmarshal()?);
        }
        self.set_description(&remote_description, StateChangeOp::SetRemote)?;

        if let Some(parsed_remote_description) = &remote_description.parsed {
            self.media_engine
                .update_from_remote_description(parsed_remote_description)?;

            // Detect trickle ICE support from remote SDP (RFC 8838/RFC 9429 section 4.1.17)
            // Check for "trickle" in space-separated "ice-options" attribute values
            let has_trickle_ice = has_ice_trickle_option(parsed_remote_description);

            match remote_description.sdp_type {
                RTCSdpType::Offer | RTCSdpType::Answer | RTCSdpType::Pranswer => {
                    self.can_trickle_ice_candidates = Some(has_trickle_ice);
                }
                _ => {
                    // Rollback or other types: reset to unknown
                    self.can_trickle_ice_candidates = None;
                }
            }

            // Disable RTX/FEC on RTPSenders if the remote didn't support it
            for transceiver in &mut self.rtp_transceivers {
                if let Some(sender) = transceiver.sender_mut() {
                    let (is_rtx_enabled, is_fec_enabled) = (
                        self.media_engine
                            .is_rtx_enabled(sender.kind(), RTCRtpTransceiverDirection::Sendonly),
                        self.media_engine
                            .is_fec_enabled(sender.kind(), RTCRtpTransceiverDirection::Sendonly),
                    );
                    sender.configure_rtx_and_fec(is_rtx_enabled, is_fec_enabled);
                }
            }

            let we_offer = remote_description.sdp_type == RTCSdpType::Answer;

            // Extract media descriptions to avoid borrowing conflicts
            let media_descriptions = self
                .remote_description()
                .as_ref()
                .and_then(|r| r.parsed.as_ref())
                .map(|parsed| parsed.media_descriptions.clone());

            if let Some(media_descriptions) = media_descriptions {
                if !we_offer {
                    for media in &media_descriptions {
                        let mid_value = match get_mid_value(media) {
                            Some(mid) if !mid.is_empty() => mid,
                            _ => return Err(Error::ErrPeerConnRemoteDescriptionWithoutMidValue),
                        };

                        if media.media_name.media == MEDIA_SECTION_APPLICATION {
                            continue;
                        }

                        let kind = RtpCodecKind::from(media.media_name.media.as_str());
                        let direction = get_peer_direction(media);
                        if kind == RtpCodecKind::Unspecified
                            || direction == RTCRtpTransceiverDirection::Unspecified
                        {
                            continue;
                        }

                        let transceiver = if let Some(i) =
                            RTCPeerConnection::find_by_mid(mid_value, &self.rtp_transceivers)
                        {
                            if direction == RTCRtpTransceiverDirection::Inactive {
                                self.rtp_transceivers[i]
                                    .stop(&self.media_engine, &mut self.interceptor)?;
                            }
                            Some(&mut self.rtp_transceivers[i])
                        } else {
                            RTCPeerConnection::satisfy_type_and_direction(
                                kind,
                                direction,
                                &mut self.rtp_transceivers,
                            )
                        };

                        if let Some(transceiver) = transceiver {
                            if direction == RTCRtpTransceiverDirection::Recvonly {
                                if transceiver.direction() == RTCRtpTransceiverDirection::Sendrecv {
                                    transceiver.set_direction(RTCRtpTransceiverDirection::Sendonly);
                                } else if transceiver.direction()
                                    == RTCRtpTransceiverDirection::Recvonly
                                {
                                    transceiver.set_direction(RTCRtpTransceiverDirection::Inactive);
                                }
                            } else if direction == RTCRtpTransceiverDirection::Sendrecv {
                                if transceiver.direction() == RTCRtpTransceiverDirection::Sendonly {
                                    transceiver.set_direction(RTCRtpTransceiverDirection::Sendrecv);
                                } else if transceiver.direction()
                                    == RTCRtpTransceiverDirection::Inactive
                                {
                                    transceiver.set_direction(RTCRtpTransceiverDirection::Recvonly);
                                }
                            } else if direction == RTCRtpTransceiverDirection::Sendonly
                                && transceiver.direction() == RTCRtpTransceiverDirection::Inactive
                            {
                                transceiver.set_direction(RTCRtpTransceiverDirection::Recvonly);
                            }

                            transceiver.set_codec_preferences_from_remote_description(
                                media,
                                &self.media_engine,
                            )?;

                            if transceiver.mid().is_none() {
                                transceiver.set_mid(mid_value.to_string())?;
                            }
                        } else {
                            let local_direction =
                                if direction == RTCRtpTransceiverDirection::Recvonly {
                                    RTCRtpTransceiverDirection::Sendonly
                                } else {
                                    RTCRtpTransceiverDirection::Recvonly
                                };

                            let mut transceiver = RTCRtpTransceiverInternal::new(
                                kind,
                                None,
                                RTCRtpTransceiverInit {
                                    direction: local_direction,
                                    streams: vec![],
                                    send_encodings: vec![],
                                },
                            );

                            transceiver.set_codec_preferences_from_remote_description(
                                media,
                                &self.media_engine,
                            )?;

                            if transceiver.mid().is_none() {
                                transceiver.set_mid(mid_value.to_string())?;
                            }

                            self.add_rtp_transceiver(transceiver);
                        }
                    }
                } else {
                    // we_offer
                    // WebRTC Spec 1.0 https://www.w3.org/TR/webrtc/
                    // 4.5.9.2
                    // This is an answer from the remote.
                    for media in &media_descriptions {
                        let mid_value = match get_mid_value(media) {
                            Some(mid) if !mid.is_empty() => mid,
                            _ => return Err(Error::ErrPeerConnRemoteDescriptionWithoutMidValue),
                        };

                        if media.media_name.media == MEDIA_SECTION_APPLICATION {
                            continue;
                        }

                        let kind = RtpCodecKind::from(media.media_name.media.as_str());
                        let mut direction = get_peer_direction(media);
                        if kind == RtpCodecKind::Unspecified
                            || direction == RTCRtpTransceiverDirection::Unspecified
                        {
                            continue;
                        }

                        let transceiver = if let Some(i) =
                            RTCPeerConnection::find_by_mid(mid_value, &self.rtp_transceivers)
                        {
                            &mut self.rtp_transceivers[i]
                        } else {
                            return Err(Error::ErrPeerConnTransceiverMidNil);
                        };

                        // reverse direction if it was a remote answer
                        if direction == RTCRtpTransceiverDirection::Sendonly {
                            direction = RTCRtpTransceiverDirection::Recvonly;
                        } else if direction == RTCRtpTransceiverDirection::Recvonly {
                            direction = RTCRtpTransceiverDirection::Sendonly;
                        }

                        transceiver.set_current_direction(direction);

                        transceiver.set_codec_preferences_from_remote_description(
                            media,
                            &self.media_engine,
                        )?;
                    }
                }
            }

            let (remote_ufrag, remote_pwd, candidates) =
                extract_ice_details(parsed_remote_description)?;

            if is_renegotiation
                && self
                    .ice_transport()
                    .have_remote_credentials_change(&remote_ufrag, &remote_pwd)
            {
                // An ICE Restart only happens implicitly for a set_remote_description of type offer

                if !we_offer {
                    // Update stats with new ICE credentials after restart (may have been generated)
                    self.ice_restart()?;
                }

                self.ice_transport_mut()
                    .set_remote_credentials(remote_ufrag.clone(), remote_pwd.clone())?;
            }

            for candidate in candidates {
                self.ice_transport_mut().add_remote_candidate(candidate)?;
            }

            if !is_renegotiation {
                let remote_is_lite = is_lite_set(parsed_remote_description);

                let (remote_fingerprint, remote_fingerprint_hash) =
                    extract_fingerprint(parsed_remote_description)?;

                // If one of the agents is lite and the other one is not, the lite agent must be the controlling agent.
                // If both or neither agents are lite the offering agent is controlling.
                // RFC 8445 S6.1.1
                let local_ice_role = if (we_offer
                    && remote_is_lite == self.setting_engine.candidates.ice_lite)
                    || (remote_is_lite && !self.setting_engine.candidates.ice_lite)
                {
                    RTCIceRole::Controlling
                } else {
                    RTCIceRole::Controlled
                };

                let remote_dtls_role = RTCDtlsRole::from(parsed_remote_description);
                log::trace!(
                    "start_transports: local_ice_role={local_ice_role}, remote_dtls_role={remote_dtls_role}"
                );

                self.start_transports(
                    local_ice_role,
                    RTCIceParameters {
                        username_fragment: remote_ufrag,
                        password: remote_pwd,
                        ice_lite: remote_is_lite,
                    },
                    DTLSParameters {
                        role: remote_dtls_role,
                        fingerprints: vec![RTCDtlsFingerprint {
                            algorithm: remote_fingerprint_hash,
                            value: remote_fingerprint,
                        }],
                    },
                )?;
            }

            if we_offer
                && let Some(parsed_local_description) = self
                    .current_local_description
                    .as_ref()
                    .and_then(|desc| desc.parsed.as_ref())
            {
                let (dtls_role, remote_caps, local_sctp_port, remote_sctp_port) = (
                    self.dtls_transport().role(),
                    SCTPTransportCapabilities {
                        max_message_size: get_application_media_section_max_message_size(
                            parsed_remote_description,
                        )
                        .unwrap_or(SctpMaxMessageSize::DEFAULT_MESSAGE_SIZE),
                    },
                    get_application_media_section_sctp_port(parsed_local_description)
                        .unwrap_or(5000),
                    get_application_media_section_sctp_port(parsed_remote_description)
                        .unwrap_or(5000),
                );

                // we_offer: we create_offer() and set_local_description() first
                // then, after call set_remote_description here,
                // Now we should have done SDP negotiation.
                // Therefore, it is ready to start sctp and rtp.
                self.sctp_transport_mut().start(
                    dtls_role,
                    remote_caps,
                    local_sctp_port,
                    remote_sctp_port,
                )?;
                self.start_rtp(remote_description)?;
            }
        }

        Ok(())
    }

    /// Returns the remote session description.
    ///
    /// Returns `pending_remote_description` if it is not null, otherwise returns
    /// `current_remote_description`. This property is used to determine if
    /// `set_remote_description` has already been called.
    ///
    /// # Specification
    ///
    /// See [remoteDescription](https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-remotedescription)
    pub fn remote_description(&self) -> Option<&RTCSessionDescription> {
        if self.pending_remote_description.is_some() {
            self.pending_remote_description.as_ref()
        } else {
            self.current_remote_description.as_ref()
        }
    }

    /// Returns the current remote description as last successfully negotiated since
    /// the last negotiation completed.
    ///
    /// This represents the remote description from the last offer/answer exchange that was
    /// successfully applied, not including any offers currently being negotiated.
    ///
    /// Returns `None` if there is no current remote description (e.g., before initial negotiation).
    ///
    /// # Specification
    ///
    /// See [currentRemoteDescription](https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-currentremotedescription)
    pub fn current_remote_description(&self) -> Option<&RTCSessionDescription> {
        self.current_remote_description.as_ref()
    }

    /// Returns the pending remote description if it exists.
    ///
    /// This represents the remote description from a call to `set_remote_description()` whose
    /// corresponding local description has not yet been applied. This is `None` if negotiation
    /// is not in progress or if a rollback has been performed.
    ///
    /// Returns `None` if there is no pending remote description.
    ///
    /// # Specification
    ///
    /// See [pendingRemoteDescription](https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-pendingremotedescription)
    pub fn pending_remote_description(&self) -> Option<&RTCSessionDescription> {
        self.pending_remote_description.as_ref()
    }

    /// Adds a remote ICE candidate to the peer connection.
    ///
    /// This method provides a remote candidate to the ICE agent. When the remote peer
    /// gathers ICE candidates and sends them over the signaling channel, this method
    /// should be called to add each candidate.
    ///
    /// # Arguments
    ///
    /// * `remote_candidate` - The ICE candidate initialization data.
    ///
    /// # Errors
    ///
    /// Returns an error if:
    /// - No remote description has been set
    /// - The candidate string is invalid
    ///
    /// # Specification
    ///
    /// See [addIceCandidate](https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-addicecandidate)
    pub fn add_remote_candidate(&mut self, remote_candidate: RTCIceCandidateInit) -> Result<()> {
        if self.remote_description().is_none() {
            return Err(Error::ErrNoRemoteDescription);
        }

        let candidate_value = match remote_candidate.candidate.strip_prefix("candidate:") {
            Some(s) => s,
            None => remote_candidate.candidate.as_str(),
        };

        if !candidate_value.is_empty() {
            self.add_ice_remote_candidate(candidate_value)?;
        }

        Ok(())
    }

    /// Adds a local ICE candidate to the peer connection.
    ///
    /// This method adds a locally gathered ICE candidate. In a typical implementation,
    /// local candidates are generated by the ICE agent and passed to this method.
    ///
    /// # Arguments
    ///
    /// * `local_candidate` - The ICE candidate initialization data. For candidates of
    ///   type "srflx" (server reflexive) or "relay", the `url` field should contain
    ///   the STUN/TURN server URL used to gather the candidate.
    ///
    /// # Errors
    ///
    /// Returns an error if the candidate string is invalid.
    pub fn add_local_candidate(&mut self, local_candidate: RTCIceCandidateInit) -> Result<()> {
        let candidate_value = match local_candidate.candidate.strip_prefix("candidate:") {
            Some(s) => s,
            None => local_candidate.candidate.as_str(),
        };

        if !candidate_value.is_empty() {
            self.add_ice_local_candidate(candidate_value, local_candidate.url.as_deref())?;
        } else {
            self.ice_transport_mut().ice_gathering_state = RTCIceGatheringState::Complete;
            // Emit OnIceGatheringStateChangeEvent
            self.pipeline_context.event_outs.push_back(
                RTCPeerConnectionEvent::OnIceGatheringStateChangeEvent(
                    RTCIceGatheringState::Complete,
                ),
            );
        }

        Ok(())
    }

    /// Tells the peer connection that ICE should be restarted.
    ///
    /// This method causes the next call to `create_offer` to generate an offer that
    /// will restart ICE. This is useful when network conditions change or the connection
    /// fails.
    ///
    /// # Specification
    ///
    /// See [restartIce](https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-restartice)
    pub fn restart_ice(&mut self) {
        self.ice_restart_requested = Some(RTCOfferOptions { ice_restart: true });
    }

    /// Returns the current configuration of this peer connection.
    ///
    /// The returned reference is to the current configuration. To modify the configuration,
    /// use `set_configuration`.
    ///
    /// # Specification
    ///
    /// See [getConfiguration](https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-getconfiguration)
    pub fn get_configuration(&self) -> &RTCConfiguration {
        &self.configuration
    }

    /// set_configuration updates the configuration of this PeerConnection object.
    pub fn set_configuration(&mut self, configuration: RTCConfiguration) -> Result<()> {
        // https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-setconfiguration (step #2)
        if self.peer_connection_state == RTCPeerConnectionState::Closed {
            return Err(Error::ErrConnectionClosed);
        }

        // https://www.w3.org/TR/webrtc/#set-the-configuration (step #3)
        if !configuration.peer_identity.is_empty() {
            if configuration.peer_identity != self.configuration.peer_identity {
                return Err(Error::ErrModifyingPeerIdentity);
            }
            self.configuration.peer_identity = configuration.peer_identity;
        }

        // https://www.w3.org/TR/webrtc/#set-the-configuration (step #4)
        if !configuration.certificates.is_empty() {
            if configuration.certificates.len() != self.configuration.certificates.len() {
                return Err(Error::ErrModifyingCertificates);
            }

            self.configuration.certificates = configuration.certificates;
        }

        // https://www.w3.org/TR/webrtc/#set-the-configuration (step #5)

        if configuration.bundle_policy != self.configuration.bundle_policy {
            return Err(Error::ErrModifyingBundlePolicy);
        }
        self.configuration.bundle_policy = configuration.bundle_policy;

        // https://www.w3.org/TR/webrtc/#set-the-configuration (step #6)
        if configuration.rtcp_mux_policy != self.configuration.rtcp_mux_policy {
            return Err(Error::ErrModifyingRTCPMuxPolicy);
        }
        self.configuration.rtcp_mux_policy = configuration.rtcp_mux_policy;

        // https://www.w3.org/TR/webrtc/#set-the-configuration (step #7)
        if configuration.ice_candidate_pool_size != 0 {
            if self.configuration.ice_candidate_pool_size != configuration.ice_candidate_pool_size
                && self.local_description().is_some()
            {
                return Err(Error::ErrModifyingICECandidatePoolSize);
            }
            self.configuration.ice_candidate_pool_size = configuration.ice_candidate_pool_size;
        }

        // https://www.w3.org/TR/webrtc/#set-the-configuration (step #8)

        self.configuration.ice_transport_policy = configuration.ice_transport_policy;

        // https://www.w3.org/TR/webrtc/#set-the-configuration (step #11)
        if !configuration.ice_servers.is_empty() {
            // https://www.w3.org/TR/webrtc/#set-the-configuration (step #11.3)
            for server in &configuration.ice_servers {
                server.validate()?;
            }
            self.configuration.ice_servers = configuration.ice_servers
        }

        Ok(())
    }

    /// create_data_channel creates a new DataChannel object with the given label
    /// and optional DataChannelInit used to configure properties of the
    /// underlying channel such as data reliability.
    pub fn create_data_channel(
        &mut self,
        label: &str,
        options: Option<RTCDataChannelInit>,
    ) -> Result<RTCDataChannel<'_, I>>
    where
        I: Interceptor,
    {
        // https://w3c.github.io/webrtc-pc/#peer-to-peer-data-api (Step #2)
        if self.peer_connection_state == RTCPeerConnectionState::Closed {
            return Err(Error::ErrConnectionClosed);
        }

        let mut params = DataChannelParameters {
            label: label.to_owned(),
            ..Default::default()
        };

        let mut id = self.generate_data_channel_id()?;

        // https://w3c.github.io/webrtc-pc/#peer-to-peer-data-api (Step #19)
        if let Some(options) = options {
            // https://w3c.github.io/webrtc-pc/#peer-to-peer-data-api (Step #16)
            if options.max_packet_life_time.is_some() && options.max_retransmits.is_some() {
                return Err(Error::ErrRetransmitsOrPacketLifeTime);
            }

            // Ordered indicates if data is allowed to be delivered out of order. The
            // default value of true, guarantees that data will be delivered in order.
            // https://w3c.github.io/webrtc-pc/#peer-to-peer-data-api (Step #9)
            params.ordered = options.ordered;

            // https://w3c.github.io/webrtc-pc/#peer-to-peer-data-api (Step #7)
            params.max_packet_life_time = options.max_packet_life_time;

            // https://w3c.github.io/webrtc-pc/#peer-to-peer-data-api (Step #8)
            params.max_retransmits = options.max_retransmits;

            // https://w3c.github.io/webrtc-pc/#peer-to-peer-data-api (Step #10)
            params.protocol = options.protocol;

            // https://w3c.github.io/webrtc-pc/#peer-to-peer-data-api (Step #11)
            if params.protocol.len() > 65535 {
                return Err(Error::ErrProtocolTooLarge);
            }

            // https://w3c.github.io/webrtc-pc/#peer-to-peer-data-api (Step #12)
            params.negotiated = options.negotiated;

            if let Some(negotiated_id) = &params.negotiated {
                id = *negotiated_id;
            }
        }

        let data_channel = RTCDataChannelInternal::new(id, params);

        self.data_channels.insert(id, data_channel);

        self.trigger_negotiation_needed();

        Ok(RTCDataChannel {
            id,
            peer_connection: self,
        })
    }

    /// Returns an iterator over the `RTCRtpSender` objects.
    ///
    /// The `RTCRtpSender` objects represent the media streams that are being sent
    /// to the remote peer.
    ///
    /// # Specification
    ///
    /// See [getSenders](https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-getsenders)
    pub fn get_senders(&self) -> impl Iterator<Item = RTCRtpSenderId> + use<'_, I> {
        self.rtp_transceivers
            .iter()
            .enumerate()
            .filter(|(_, transceiver)| transceiver.direction().has_send())
            .map(|(id, _)| RTCRtpSenderId(id))
    }

    /// Returns an iterator over the `RTCRtpReceiver` objects.
    ///
    /// The `RTCRtpReceiver` objects represent the media streams that are being received
    /// from the remote peer.
    ///
    /// # Specification
    ///
    /// See [getReceivers](https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-getreceivers)
    pub fn get_receivers(&self) -> impl Iterator<Item = RTCRtpReceiverId> + use<'_, I> {
        self.rtp_transceivers
            .iter()
            .enumerate()
            .filter(|(_, transceiver)| transceiver.direction().has_recv())
            .map(|(id, _)| RTCRtpReceiverId(id))
    }

    /// Returns an iterator over the `RTCRtpTransceiver` objects.
    ///
    /// The `RTCRtpTransceiver` objects represent the combination of an `RTCRtpSender`
    /// and an `RTCRtpReceiver` that share a common mid.
    ///
    /// # Specification
    ///
    /// See [getTransceivers](https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-gettransceivers)
    pub fn get_transceivers(&self) -> impl Iterator<Item = RTCRtpTransceiverId> {
        0..self.rtp_transceivers.len()
    }

    /// Adds a media track to the peer connection.
    ///
    /// This method adds a track to the connection, either by finding an existing transceiver
    /// that can be reused, or by creating a new transceiver. The track represents media
    /// (audio or video) that will be sent to the remote peer.
    ///
    /// # Arguments
    ///
    /// * `track` - The media stream track to add.
    ///
    /// # Returns
    ///
    /// Returns the ID of the `RTCRtpSender` that will send this track.
    ///
    /// # Errors
    ///
    /// Returns an error if the peer connection is closed.
    ///
    /// # Specification
    ///
    /// See [addTrack](https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-addtrack)
    pub fn add_track(&mut self, track: MediaStreamTrack) -> Result<RTCRtpSenderId> {
        if self.peer_connection_state == RTCPeerConnectionState::Closed {
            return Err(Error::ErrConnectionClosed);
        }

        let send_encodings = self.send_encodings_from_track(&track);
        let (track, send_encodings, codec_preferences) =
            self.normalize_sender_track(track, send_encodings)?;
        for (id, transceiver) in self.rtp_transceivers.iter_mut().enumerate() {
            if !transceiver.stopped()
                && transceiver.kind() == track.kind()
                && transceiver.sender().is_none()
            {
                let mut sender =
                    RTCRtpSenderInternal::new(track.kind(), track, vec![], send_encodings);

                if transceiver.get_codec_preferences().is_empty() && !codec_preferences.is_empty() {
                    transceiver.set_codec_preferences(codec_preferences, &self.media_engine)?;
                }

                sender.set_codec_preferences(transceiver.get_codec_preferences().to_vec());

                transceiver.sender_mut().replace(sender);

                transceiver.set_direction(RTCRtpTransceiverDirection::from_send_recv(
                    true,
                    transceiver.direction().has_recv(),
                ));

                self.trigger_negotiation_needed();
                return Ok(RTCRtpSenderId(id));
            }
        }

        let mut transceiver = self.new_transceiver_from_track(
            track,
            RTCRtpTransceiverInit {
                direction: RTCRtpTransceiverDirection::Sendrecv,
                streams: vec![],
                send_encodings,
            },
        )?;
        if !codec_preferences.is_empty() {
            transceiver.set_codec_preferences(codec_preferences, &self.media_engine)?;
        }
        Ok(RTCRtpSenderId(self.add_rtp_transceiver(transceiver)))
    }

    /// Removes a track from the peer connection.
    ///
    /// This method stops an `RTCRtpSender` from sending media and marks its transceiver
    /// as no longer sending. This will trigger renegotiation.
    ///
    /// # Arguments
    ///
    /// * `sender_id` - The ID of the `RTCRtpSender` to remove.
    ///
    /// # Errors
    ///
    /// Returns an error if:
    /// - The peer connection is closed
    /// - The sender ID is invalid
    ///
    /// # Specification
    ///
    /// See [removeTrack](https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-removetrack)
    pub fn remove_track(&mut self, sender_id: RTCRtpSenderId) -> Result<()> {
        if self.peer_connection_state == RTCPeerConnectionState::Closed {
            return Err(Error::ErrConnectionClosed);
        }

        if sender_id.0 >= self.rtp_transceivers.len() {
            return Err(Error::ErrRTPSenderNotExisted);
        }

        // This also happens in `set_sending_track` but we need to make sure we do this
        // before we call sender.stop to avoid a race condition when removing tracks and
        // generating offers.
        let has_recv = self.rtp_transceivers[sender_id.0].direction().has_recv();
        self.rtp_transceivers[sender_id.0]
            .set_direction(RTCRtpTransceiverDirection::from_send_recv(false, has_recv));

        if let Some(sender) = self.rtp_transceivers[sender_id.0].sender_mut()
            && sender
                .stop(&self.media_engine, &mut self.interceptor)
                .is_ok()
        {
            self.trigger_negotiation_needed();
        }

        self.rtp_transceivers[sender_id.0].sender_mut().take();

        Ok(())
    }

    /// Creates a new `RTCRtpTransceiver` and adds it to the set of transceivers.
    ///
    /// This method creates a transceiver associated with the given track, which can be
    /// configured to send, receive, or both.
    ///
    /// # Arguments
    ///
    /// * `track` - The media stream track to associate with the transceiver.
    /// * `init` - Optional initialization parameters for the transceiver.
    ///
    /// # Returns
    ///
    /// Returns the ID of the created transceiver.
    ///
    /// # Errors
    ///
    /// Returns an error if the peer connection is closed.
    ///
    /// # Specification
    ///
    /// See [addTransceiver](https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-addtransceiver)
    pub fn add_transceiver_from_track(
        &mut self,
        track: MediaStreamTrack,
        init: Option<RTCRtpTransceiverInit>,
    ) -> Result<RTCRtpTransceiverId> {
        if self.peer_connection_state == RTCPeerConnectionState::Closed {
            return Err(Error::ErrConnectionClosed);
        }

        if let Some(init) = init.as_ref()
            && !init.direction.has_send()
        {
            return Err(Error::ErrInvalidDirection);
        }

        let mut init = if let Some(init) = init {
            init
        } else {
            RTCRtpTransceiverInit {
                direction: RTCRtpTransceiverDirection::Sendrecv,
                streams: vec![],
                send_encodings: vec![],
            }
        };

        let send_encodings = if init.send_encodings.is_empty() {
            self.send_encodings_from_track(&track)
        } else {
            init.send_encodings.clone()
        };
        let (track, send_encodings, codec_preferences) =
            self.normalize_sender_track(track, send_encodings)?;
        init.send_encodings = send_encodings;

        let mut transceiver = self.new_transceiver_from_track(track, init)?;
        if !codec_preferences.is_empty() {
            transceiver.set_codec_preferences(codec_preferences, &self.media_engine)?;
        }

        Ok(self.add_rtp_transceiver(transceiver))
    }

    /// add_transceiver_from_kind Create a new RtpTransceiver and adds it to the set of transceivers.
    pub fn add_transceiver_from_kind(
        &mut self,
        kind: RtpCodecKind,
        init: Option<RTCRtpTransceiverInit>,
    ) -> Result<RTCRtpTransceiverId> {
        if self.peer_connection_state == RTCPeerConnectionState::Closed {
            return Err(Error::ErrConnectionClosed);
        }

        let init = if let Some(init) = init {
            if init.direction.has_send() && init.send_encodings.is_empty() {
                return Err(Error::ErrInvalidDirection);
            }

            init
        } else {
            RTCRtpTransceiverInit {
                direction: RTCRtpTransceiverDirection::Recvonly,
                streams: vec![],
                send_encodings: vec![],
            }
        };

        let transceiver = match init.direction {
            RTCRtpTransceiverDirection::Sendonly | RTCRtpTransceiverDirection::Sendrecv => {
                let mut init = init;
                let track = MediaStreamTrack::new(
                    math_rand_alpha(16), // MediaStreamId
                    math_rand_alpha(16), // MediaStreamTrackId
                    math_rand_alpha(16), // Label
                    kind,
                    init.send_encodings.clone(),
                );
                let (track, send_encodings, codec_preferences) =
                    self.normalize_sender_track(track, init.send_encodings)?;
                init.send_encodings = send_encodings;

                let mut transceiver = self.new_transceiver_from_track(track, init)?;
                if !codec_preferences.is_empty() {
                    transceiver.set_codec_preferences(codec_preferences, &self.media_engine)?;
                }
                transceiver
            }
            RTCRtpTransceiverDirection::Recvonly => {
                RTCRtpTransceiverInternal::new(kind, None, init)
            }
            _ => return Err(Error::ErrPeerConnAddTransceiverFromKindSupport),
        };

        Ok(self.add_rtp_transceiver(transceiver))
    }

    /// data_channel provides the access to RTCDataChannel object with the given id
    pub fn data_channel(&mut self, id: RTCDataChannelId) -> Option<RTCDataChannel<'_, I>>
    where
        I: Interceptor,
    {
        if self.data_channels.contains_key(&id) {
            Some(RTCDataChannel {
                id,
                peer_connection: self,
            })
        } else {
            None
        }
    }

    /// rtp_sender provides the access to RTCRtpSender object with the given id
    pub fn rtp_sender(&mut self, id: RTCRtpSenderId) -> Option<RTCRtpSender<'_, I>>
    where
        I: Interceptor,
    {
        if id.0 < self.rtp_transceivers.len()
            && self.rtp_transceivers[id.0].direction().has_send()
            && self.rtp_transceivers[id.0].sender().is_some()
        {
            Some(RTCRtpSender {
                id,
                peer_connection: self,
            })
        } else {
            None
        }
    }

    /// rtp_receiver provides the access to RTCRtpReceiver object with the given id
    pub fn rtp_receiver(&mut self, id: RTCRtpReceiverId) -> Option<RTCRtpReceiver<'_, I>>
    where
        I: Interceptor,
    {
        if id.0 < self.rtp_transceivers.len()
            && self.rtp_transceivers[id.0].direction().has_recv()
            && self.rtp_transceivers[id.0].receiver().is_some()
        {
            Some(RTCRtpReceiver {
                id,
                peer_connection: self,
            })
        } else {
            None
        }
    }

    /// rtp_transceiver provides the access to RTCRtpTransceiver object with the given id
    pub fn rtp_transceiver(&mut self, id: RTCRtpTransceiverId) -> Option<RTCRtpTransceiver<'_, I>>
    where
        I: Interceptor,
    {
        if id < self.rtp_transceivers.len() {
            Some(RTCRtpTransceiver {
                id,
                peer_connection: self,
            })
        } else {
            None
        }
    }

    /// Returns a snapshot of accumulated statistics.
    ///
    /// This method creates an immutable snapshot of WebRTC statistics
    /// at the given timestamp. When `selector` is `StatsSelector::None`,
    /// the returned `RTCStatsReport` contains statistics for all aspects
    /// of the peer connection. When a sender or receiver is specified,
    /// only statistics relevant to that sender/receiver are included.
    ///
    /// # Statistics included by selector
    ///
    /// - `StatsSelector::None` - All statistics for the entire connection
    /// - `StatsSelector::Sender(id)` - Outbound RTP streams for the sender
    ///   and all referenced stats (transport, codec, remote inbound, etc.)
    /// - `StatsSelector::Receiver(id)` - Inbound RTP streams for the receiver
    ///   and all referenced stats (transport, codec, remote outbound, etc.)
    ///
    /// # Arguments
    ///
    /// * `now` - The timestamp to use for all stats in the report. This is
    ///   passed explicitly to support deterministic testing.
    /// * `selector` - Controls which statistics are included in the report.
    ///
    /// # Returns
    ///
    /// An `RTCStatsReport` containing snapshots of the selected statistics.
    ///
    /// # Example
    ///
    /// ```no_run
    /// use std::time::Instant;
    /// use rtc::peer_connection::RTCPeerConnectionBuilder;
    /// use rtc::statistics::StatsSelector;
    ///
    /// # fn example() -> Result<(), Box<dyn std::error::Error>> {
    /// let mut pc = RTCPeerConnectionBuilder::new().build()?;
    ///
    /// // Get all stats
    /// let report = pc.get_stats(Instant::now(), StatsSelector::None);
    ///
    /// // Access peer connection stats
    /// if let Some(pc_stats) = report.peer_connection() {
    ///     println!("Data channels opened: {}", pc_stats.data_channels_opened);
    /// }
    ///
    /// // Iterate over inbound RTP streams
    /// for stream in report.inbound_rtp_streams() {
    ///     println!("SSRC {}: {} packets received", stream.received_rtp_stream_stats.rtp_stream_stats.ssrc, stream.received_rtp_stream_stats.packets_received);
    /// }
    /// # Ok(())
    /// # }
    /// ```
    ///
    /// # Specification
    ///
    /// See [getStats](https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-getstats) and
    /// [The stats selection algorithm](https://www.w3.org/TR/webrtc/#the-stats-selection-algorithm)
    pub fn get_stats(&mut self, now: Instant, selector: StatsSelector) -> RTCStatsReport {
        // Update ICE agent stats before taking snapshot
        self.update_ice_agent_stats();
        // Update codec stats from transceivers before taking snapshot
        self.update_codec_stats();
        self.pipeline_context
            .stats
            .snapshot_with_selector(now, selector)
    }
}
