rvoip_dialog_core/api/
common.rs

1//! Common API Types
2//!
3//! This module provides shared types and handles used across the dialog-core API,
4//! offering convenient access to dialog operations and events.
5//!
6//! ## Overview
7//!
8//! The common types provide high-level abstractions over SIP dialogs and calls:
9//!
10//! - **DialogHandle**: General-purpose dialog operations and state management
11//! - **CallHandle**: Call-specific operations built on top of DialogHandle
12//! - **CallInfo**: Information about active calls
13//! - **DialogEvent**: Events that can be monitored by applications
14//!
15//! ## Quick Start
16//!
17//! ### Using DialogHandle
18//!
19//! ```rust,no_run
20//! use rvoip_dialog_core::api::common::DialogHandle;
21//! use rvoip_sip_core::Method;
22//!
23//! # async fn example(dialog: DialogHandle) -> Result<(), Box<dyn std::error::Error>> {
24//! // Send a request within the dialog
25//! let tx_key = dialog.send_request(Method::Info, Some("Hello".to_string())).await?;
26//! println!("Sent INFO request: {}", tx_key);
27//!
28//! // Check dialog state
29//! let state = dialog.state().await?;
30//! println!("Dialog state: {:?}", state);
31//!
32//! // Send BYE to terminate
33//! dialog.send_bye().await?;
34//! # Ok(())
35//! # }
36//! ```
37//!
38//! ### Using CallHandle
39//!
40//! ```rust,no_run
41//! use rvoip_dialog_core::api::common::CallHandle;
42//!
43//! # async fn example(call: CallHandle) -> Result<(), Box<dyn std::error::Error>> {
44//! // Get call information
45//! let info = call.info().await?;
46//! println!("Call from {} to {}", info.local_uri, info.remote_uri);
47//!
48//! // Put call on hold
49//! call.hold(Some("SDP with hold attributes".to_string())).await?;
50//!
51//! // Transfer the call
52//! call.transfer("sip:voicemail@example.com".to_string()).await?;
53//!
54//! // Hang up
55//! call.hangup().await?;
56//! # Ok(())
57//! # }
58//! ```
59//!
60//! ## Handle Architecture
61//!
62//! ```text
63//! CallHandle
64//!     │
65//!     └── DialogHandle
66//!             │
67//!             └── DialogManager
68//!                     │
69//!                     └── Dialog (actual state)
70//! ```
71//!
72//! CallHandle provides call-specific operations while DialogHandle provides
73//! general dialog operations. Both are lightweight wrappers that delegate
74//! to the underlying DialogManager.
75//!
76//! ## Event Monitoring
77//!
78//! Applications can monitor dialog events for state changes:
79//!
80//! ```rust,no_run
81//! use rvoip_dialog_core::api::common::DialogEvent;
82//!
83//! fn handle_dialog_event(event: DialogEvent) {
84//!     match event {
85//!         DialogEvent::Created { dialog_id } => {
86//!             println!("New dialog: {}", dialog_id);
87//!         },
88//!         DialogEvent::StateChanged { dialog_id, old_state, new_state } => {
89//!             println!("Dialog {} changed from {:?} to {:?}", dialog_id, old_state, new_state);
90//!         },
91//!         DialogEvent::Terminated { dialog_id, reason } => {
92//!             println!("Dialog {} terminated: {}", dialog_id, reason);
93//!         },
94//!         _ => {}
95//!     }
96//! }
97//! ```
98
99use std::sync::Arc;
100use tracing::{debug, info, warn};
101
102use rvoip_sip_core::{Method, StatusCode, Response};
103use rvoip_transaction_core::TransactionKey;
104use crate::manager::DialogManager;
105use crate::dialog::{DialogId, Dialog, DialogState};
106use super::{ApiResult, ApiError};
107
108/// A handle to a SIP dialog for convenient operations
109/// 
110/// Provides a high-level interface to dialog operations without exposing
111/// the underlying DialogManager complexity. DialogHandle is the primary
112/// way applications interact with SIP dialogs, offering both basic and
113/// advanced dialog management capabilities.
114///
115/// ## Key Features
116///
117/// - **State Management**: Query and monitor dialog state changes
118/// - **Request Sending**: Send arbitrary SIP methods within the dialog
119/// - **Response Handling**: Send responses to incoming requests
120/// - **Lifecycle Control**: Terminate dialogs gracefully or immediately
121/// - **SIP Methods**: Convenient methods for common SIP operations
122///
123/// ## Examples
124///
125/// ### Basic Dialog Operations
126///
127/// ```rust,no_run
128/// use rvoip_dialog_core::api::common::DialogHandle;
129/// use rvoip_sip_core::Method;
130///
131/// # async fn example(dialog: DialogHandle) -> Result<(), Box<dyn std::error::Error>> {
132/// // Check if dialog is still active
133/// if dialog.is_active().await {
134///     println!("Dialog {} is active", dialog.id());
135///     
136///     // Get current state
137///     let state = dialog.state().await?;
138///     println!("State: {:?}", state);
139///     
140///     // Send a custom request
141///     let tx_key = dialog.send_request(Method::Update, Some("new parameters".to_string())).await?;
142///     println!("Sent UPDATE: {}", tx_key);
143/// }
144/// # Ok(())
145/// # }
146/// ```
147///
148/// ### Advanced Dialog Usage
149///
150/// ```rust,no_run
151/// use rvoip_dialog_core::api::common::DialogHandle;
152/// use rvoip_sip_core::Method;
153///
154/// # async fn example(dialog: DialogHandle) -> Result<(), Box<dyn std::error::Error>> {
155/// // Get full dialog information
156/// let info = dialog.info().await?;
157/// println!("Dialog: {} -> {}", info.local_uri, info.remote_uri);
158/// println!("Call-ID: {}, State: {:?}", info.call_id, info.state);
159///
160/// // Send application-specific information
161/// dialog.send_info("Custom application data".to_string()).await?;
162///
163/// // Send a notification about an event
164/// dialog.send_notify("presence".to_string(), Some("online".to_string())).await?;
165///
166/// // Gracefully terminate the dialog
167/// dialog.terminate().await?;
168/// # Ok(())
169/// # }
170/// ```
171///
172/// ### Dialog State Monitoring
173///
174/// ```rust,no_run
175/// use rvoip_dialog_core::api::common::DialogHandle;
176/// use rvoip_dialog_core::dialog::DialogState;
177///
178/// # async fn example(dialog: DialogHandle) -> Result<(), Box<dyn std::error::Error>> {
179/// // Monitor dialog state changes
180/// loop {
181///     let state = dialog.state().await?;
182///     match state {
183///         DialogState::Initial => println!("Dialog starting..."),
184///         DialogState::Early => println!("Dialog in early state"),
185///         DialogState::Confirmed => {
186///             println!("Dialog confirmed - ready for operations");
187///             break;
188///         },
189///         DialogState::Terminated => {
190///             println!("Dialog terminated");
191///             break;
192///         },
193///         DialogState::Recovering => {
194///             println!("Dialog recovering...");
195///         },
196///     }
197///     tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
198/// }
199/// # Ok(())
200/// # }
201/// ```
202#[derive(Debug, Clone)]
203pub struct DialogHandle {
204    dialog_id: DialogId,
205    dialog_manager: Arc<DialogManager>,
206}
207
208impl DialogHandle {
209    /// Create a new dialog handle
210    pub(crate) fn new(dialog_id: DialogId, dialog_manager: Arc<DialogManager>) -> Self {
211        Self {
212            dialog_id,
213            dialog_manager,
214        }
215    }
216    
217    /// Get the dialog ID
218    pub fn id(&self) -> &DialogId {
219        &self.dialog_id
220    }
221    
222    /// Get the current dialog information
223    pub async fn info(&self) -> ApiResult<Dialog> {
224        self.dialog_manager.get_dialog(&self.dialog_id)
225            .map_err(ApiError::from)
226    }
227    
228    /// Get the current dialog state
229    pub async fn state(&self) -> ApiResult<DialogState> {
230        self.dialog_manager.get_dialog_state(&self.dialog_id)
231            .map_err(ApiError::from)
232    }
233    
234    /// Send a request within this dialog
235    /// 
236    /// # Arguments
237    /// * `method` - SIP method to send
238    /// * `body` - Optional message body
239    /// 
240    /// # Returns
241    /// Transaction key for tracking the request
242    pub async fn send_request(&self, method: Method, body: Option<String>) -> ApiResult<String> {
243        debug!("Sending {} request in dialog {}", method, self.dialog_id);
244        
245        let body_bytes = body.map(|s| bytes::Bytes::from(s));
246        let transaction_key = self.dialog_manager.send_request(&self.dialog_id, method, body_bytes).await
247            .map_err(ApiError::from)?;
248        
249        Ok(transaction_key.to_string())
250    }
251    
252    /// **NEW**: Send a request within this dialog (returns TransactionKey)
253    /// 
254    /// Enhanced version that returns the actual TransactionKey for advanced usage.
255    /// 
256    /// # Arguments
257    /// * `method` - SIP method to send
258    /// * `body` - Optional message body
259    /// 
260    /// # Returns
261    /// Transaction key for tracking the request
262    pub async fn send_request_with_key(&self, method: Method, body: Option<bytes::Bytes>) -> ApiResult<TransactionKey> {
263        debug!("Sending {} request in dialog {}", method, self.dialog_id);
264        
265        self.dialog_manager.send_request(&self.dialog_id, method, body).await
266            .map_err(ApiError::from)
267    }
268    
269    /// **NEW**: Send a SIP response for a transaction
270    /// 
271    /// Allows sending responses directly through the dialog handle.
272    /// 
273    /// # Arguments
274    /// * `transaction_id` - Transaction to respond to
275    /// * `response` - Complete SIP response
276    /// 
277    /// # Returns
278    /// Success or error
279    pub async fn send_response(&self, transaction_id: &TransactionKey, response: Response) -> ApiResult<()> {
280        debug!("Sending response for transaction {} in dialog {}", transaction_id, self.dialog_id);
281        
282        self.dialog_manager.send_response(transaction_id, response).await
283            .map_err(ApiError::from)
284    }
285    
286    /// **NEW**: Send specific SIP methods with convenience
287    
288    /// Send a BYE request to terminate the dialog
289    pub async fn send_bye(&self) -> ApiResult<TransactionKey> {
290        info!("Sending BYE for dialog {}", self.dialog_id);
291        self.send_request_with_key(Method::Bye, None).await
292    }
293    
294    /// Send a REFER request for call transfer
295    pub async fn send_refer(&self, target_uri: String, refer_body: Option<String>) -> ApiResult<TransactionKey> {
296        info!("Sending REFER for dialog {} to {}", self.dialog_id, target_uri);
297        
298        let body = if let Some(custom_body) = refer_body {
299            custom_body
300        } else {
301            format!("Refer-To: {}\r\n", target_uri)
302        };
303        
304        self.send_request_with_key(Method::Refer, Some(bytes::Bytes::from(body))).await
305    }
306    
307    /// Send a NOTIFY request for event notifications
308    pub async fn send_notify(&self, event: String, body: Option<String>) -> ApiResult<TransactionKey> {
309        info!("Sending NOTIFY for dialog {} event {}", self.dialog_id, event);
310        
311        let notify_body = body.map(|b| bytes::Bytes::from(b));
312        self.send_request_with_key(Method::Notify, notify_body).await
313    }
314    
315    /// Send an UPDATE request for media modifications
316    pub async fn send_update(&self, sdp: Option<String>) -> ApiResult<TransactionKey> {
317        info!("Sending UPDATE for dialog {}", self.dialog_id);
318        
319        let update_body = sdp.map(|s| bytes::Bytes::from(s));
320        self.send_request_with_key(Method::Update, update_body).await
321    }
322    
323    /// Send an INFO request for application-specific information
324    pub async fn send_info(&self, info_body: String) -> ApiResult<TransactionKey> {
325        info!("Sending INFO for dialog {}", self.dialog_id);
326        
327        self.send_request_with_key(Method::Info, Some(bytes::Bytes::from(info_body))).await
328    }
329    
330    /// Send BYE to terminate the dialog
331    pub async fn terminate(&self) -> ApiResult<()> {
332        info!("Terminating dialog {}", self.dialog_id);
333        
334        // Send BYE request
335        self.send_request(Method::Bye, None).await?;
336        
337        // Terminate dialog
338        self.dialog_manager.terminate_dialog(&self.dialog_id).await
339            .map_err(ApiError::from)?;
340        
341        Ok(())
342    }
343    
344    /// **NEW**: Terminate dialog directly without sending BYE
345    /// 
346    /// For cases where you want to clean up the dialog state without
347    /// sending a BYE request (e.g., after receiving a BYE).
348    pub async fn terminate_immediately(&self) -> ApiResult<()> {
349        info!("Terminating dialog {} immediately", self.dialog_id);
350        
351        self.dialog_manager.terminate_dialog(&self.dialog_id).await
352            .map_err(ApiError::from)
353    }
354    
355    /// Check if the dialog is still active
356    pub async fn is_active(&self) -> bool {
357        self.dialog_manager.has_dialog(&self.dialog_id)
358    }
359}
360
361/// A handle to a SIP call (specific type of dialog) for call-related operations
362/// 
363/// Provides call-specific convenience methods on top of the basic dialog operations.
364/// CallHandle extends DialogHandle with operations that are specifically relevant
365/// to voice/video calls, such as hold/resume, transfer, and media management.
366///
367/// ## Key Features
368///
369/// - **Call Lifecycle**: Answer, reject, and hang up calls
370/// - **Call Control**: Hold, resume, transfer, and mute operations
371/// - **Media Management**: Update media parameters and handle media events
372/// - **Call Information**: Access to call-specific metadata and state
373/// - **Dialog Access**: Full access to underlying dialog operations
374///
375/// ## Examples
376///
377/// ### Basic Call Operations
378///
379/// ```rust,no_run
380/// use rvoip_dialog_core::api::common::CallHandle;
381/// use rvoip_sip_core::StatusCode;
382///
383/// # async fn example(call: CallHandle) -> Result<(), Box<dyn std::error::Error>> {
384/// // Get call information
385/// let info = call.info().await?;
386/// println!("Call {} from {} to {}", info.call_id, info.local_uri, info.remote_uri);
387/// println!("State: {:?}", info.state);
388///
389/// // Answer an incoming call
390/// call.answer(Some("SDP answer with media info".to_string())).await?;
391///
392/// // Or reject a call
393/// // call.reject(StatusCode::Busy, Some("Busy right now".to_string())).await?;
394/// # Ok(())
395/// # }
396/// ```
397///
398/// ### Call Control Operations
399///
400/// ```rust,no_run
401/// use rvoip_dialog_core::api::common::CallHandle;
402///
403/// # async fn example(call: CallHandle) -> Result<(), Box<dyn std::error::Error>> {
404/// // Put call on hold
405/// let hold_sdp = "v=0\r\no=- 123 456 IN IP4 0.0.0.0\r\n...";
406/// call.hold(Some(hold_sdp.to_string())).await?;
407/// println!("Call on hold");
408///
409/// // Resume from hold
410/// let resume_sdp = "v=0\r\no=- 123 457 IN IP4 192.168.1.100\r\n...";
411/// call.resume(Some(resume_sdp.to_string())).await?;
412/// println!("Call resumed");
413///
414/// // Transfer to another party
415/// call.transfer("sip:alice@example.com".to_string()).await?;
416/// println!("Call transferred");
417/// # Ok(())
418/// # }
419/// ```
420///
421/// ### Advanced Call Management
422///
423/// ```rust,no_run
424/// use rvoip_dialog_core::api::common::CallHandle;
425///
426/// # async fn example(call: CallHandle) -> Result<(), Box<dyn std::error::Error>> {
427/// // Update media parameters
428/// let new_sdp = "v=0\r\no=- 123 458 IN IP4 192.168.1.100\r\n...";
429/// call.update_media(Some(new_sdp.to_string())).await?;
430///
431/// // Send call-related information
432/// call.send_info("DTMF: 1".to_string()).await?;
433///
434/// // Send call event notification
435/// call.notify("call-status".to_string(), Some("ringing".to_string())).await?;
436///
437/// // Advanced transfer with custom REFER
438/// let refer_body = "Refer-To: sip:bob@example.com\r\nReplaces: abc123";
439/// call.transfer_with_body("sip:bob@example.com".to_string(), refer_body.to_string()).await?;
440/// # Ok(())
441/// # }
442/// ```
443///
444/// ### Call State Monitoring
445///
446/// ```rust,no_run
447/// use rvoip_dialog_core::api::common::CallHandle;
448/// use rvoip_dialog_core::dialog::DialogState;
449///
450/// # async fn example(call: CallHandle) -> Result<(), Box<dyn std::error::Error>> {
451/// // Monitor call state
452/// while call.is_active().await {
453///     let state = call.dialog_state().await?;
454///     match state {
455///         DialogState::Early => println!("Call ringing..."),
456///         DialogState::Confirmed => {
457///             println!("Call answered and active");
458///             break;
459///         },
460///         DialogState::Terminated => {
461///             println!("Call ended");
462///             break;
463///         },
464///         _ => {}
465///     }
466///     tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;
467/// }
468/// # Ok(())
469/// # }
470/// ```
471///
472/// ### Integration with Dialog Operations
473///
474/// ```rust,no_run
475/// use rvoip_dialog_core::api::common::CallHandle;
476/// use rvoip_sip_core::Method;
477///
478/// # async fn example(call: CallHandle) -> Result<(), Box<dyn std::error::Error>> {
479/// // Access underlying dialog handle
480/// let dialog = call.dialog();
481/// 
482/// // Use dialog operations directly
483/// let tx_key = dialog.send_request(Method::Options, None).await?;
484/// println!("Sent OPTIONS via dialog: {}", tx_key);
485///
486/// // Or use call-specific shortcuts
487/// let tx_key = call.send_request(Method::Info, Some("Call info".to_string())).await?;
488/// println!("Sent INFO via call: {}", tx_key);
489/// # Ok(())
490/// # }
491/// ```
492#[derive(Debug, Clone)]
493pub struct CallHandle {
494    dialog_handle: DialogHandle,
495}
496
497impl CallHandle {
498    /// Create a new call handle
499    pub(crate) fn new(dialog_id: DialogId, dialog_manager: Arc<DialogManager>) -> Self {
500        Self {
501            dialog_handle: DialogHandle::new(dialog_id, dialog_manager),
502        }
503    }
504    
505    /// Get the underlying dialog handle
506    pub fn dialog(&self) -> &DialogHandle {
507        &self.dialog_handle
508    }
509    
510    /// Get the call ID (same as dialog ID)
511    pub fn call_id(&self) -> &DialogId {
512        self.dialog_handle.id()
513    }
514    
515    /// Get call information
516    pub async fn info(&self) -> ApiResult<CallInfo> {
517        let dialog = self.dialog_handle.info().await?;
518        Ok(CallInfo {
519            call_id: dialog.id.clone(),
520            state: dialog.state,
521            local_uri: dialog.local_uri.to_string(),
522            remote_uri: dialog.remote_uri.to_string(),
523            call_id_header: dialog.call_id,
524            local_tag: dialog.local_tag,
525            remote_tag: dialog.remote_tag,
526        })
527    }
528    
529    /// Answer the call (send 200 OK)
530    /// 
531    /// # Arguments
532    /// * `sdp_answer` - Optional SDP answer for media negotiation
533    /// 
534    /// # Returns
535    /// Success or error
536    pub async fn answer(&self, sdp_answer: Option<String>) -> ApiResult<()> {
537        info!("Answering call {}", self.call_id());
538        
539        // Find the transaction associated with this dialog
540        // We need to look through the transaction-to-dialog mappings to find the INVITE transaction
541        let transaction_id = {
542            let dialog_manager = &self.dialog_handle.dialog_manager;
543            let mut found_tx_id = None;
544            
545            // Search through transaction mappings to find the INVITE transaction for this dialog
546            for entry in dialog_manager.transaction_to_dialog.iter() {
547                if entry.value() == self.call_id() {
548                    // Check if this is an INVITE transaction (server-side)
549                    let tx_key = entry.key();
550                    if tx_key.to_string().contains("INVITE") && tx_key.to_string().contains("server") {
551                        found_tx_id = Some(tx_key.clone());
552                        break;
553                    }
554                }
555            }
556            
557            found_tx_id.ok_or_else(|| ApiError::Internal {
558                message: "No INVITE transaction found for this call".to_string()
559            })?
560        };
561        
562        // Get the original INVITE request to build a proper response
563        let original_request = self.dialog_handle.dialog_manager
564            .transaction_manager()
565            .original_request(&transaction_id)
566            .await
567            .map_err(|e| ApiError::Internal {
568                message: format!("Failed to get original INVITE request: {}", e)
569            })?
570            .ok_or_else(|| ApiError::Internal {
571                message: "Original INVITE request not found".to_string()
572            })?;
573        
574        // Build 200 OK response with SDP and proper To tag for dialog establishment
575        let response = {
576            use rvoip_transaction_core::utils::response_builders;
577            
578            // Use the dialog-aware response builder that adds To tags
579            // Get the actual local address from the dialog handle
580            let local_addr = self.dialog_handle.dialog_manager.local_address();
581            let mut response = response_builders::create_ok_response_with_dialog_info(
582                &original_request,
583                "server",                    // contact_user
584                &local_addr.ip().to_string(), // contact_host - use actual local IP
585                Some(local_addr.port())      // contact_port - use actual local port
586            );
587            
588            // Add SDP body if provided
589            if let Some(sdp) = &sdp_answer {
590                response = response.with_body(sdp.as_bytes().to_vec());
591                // Add Content-Type header for SDP
592                use rvoip_sip_core::{TypedHeader, types::content_type::ContentType};
593                use rvoip_sip_core::parser::headers::content_type::ContentTypeValue;
594                response.headers.push(TypedHeader::ContentType(ContentType::new(
595                    ContentTypeValue {
596                        m_type: "application".to_string(),
597                        m_subtype: "sdp".to_string(),
598                        parameters: std::collections::HashMap::new(),
599                    }
600                )));
601            }
602            
603            response
604        };
605        
606        // CRITICAL FIX: Extract the tag from the response BEFORE sending it
607        let response_local_tag = response.to()
608            .and_then(|to_header| to_header.tag())
609            .map(|tag| tag.to_string());
610        
611        // Send the 200 OK response
612        self.dialog_handle.dialog_manager.send_response(&transaction_id, response).await
613            .map_err(|e| ApiError::Internal {
614                message: format!("Failed to send 200 OK response: {}", e)
615            })?;
616        
617        // Update dialog state to Confirmed and set local tag to match the response
618        {
619            let mut dialog = self.dialog_handle.dialog_manager.get_dialog_mut(self.call_id())
620                .map_err(|e| ApiError::Internal {
621                    message: format!("Failed to get dialog for state update: {}", e)
622                })?;
623            
624            // CRITICAL FIX: Use the tag from the response we just sent to ensure consistency
625            if dialog.local_tag.is_none() {
626                if let Some(response_tag) = response_local_tag {
627                    dialog.local_tag = Some(response_tag.clone());
628                    info!("Dialog {} local tag set to match response: {}", self.call_id(), response_tag);
629                } else {
630                    // Fallback: generate tag if response doesn't have one (shouldn't happen)
631                    let local_tag = dialog.generate_local_tag();
632                    dialog.local_tag = Some(local_tag);
633                    warn!("Dialog {} fallback tag generation used", self.call_id());
634                }
635            }
636            
637            // Transition from Early to Confirmed
638            if dialog.state == crate::dialog::DialogState::Early {
639                dialog.state = crate::dialog::DialogState::Confirmed;
640                info!("Dialog {} transitioned to Confirmed state", self.call_id());
641                
642                // CRITICAL FIX: Update dialog lookup now that we have both tags
643                if let Some(tuple) = dialog.dialog_id_tuple() {
644                    use crate::manager::utils::DialogUtils;
645                    let key = DialogUtils::create_lookup_key(&tuple.0, &tuple.1, &tuple.2);
646                    self.dialog_handle.dialog_manager.dialog_lookup.insert(key, dialog.id.clone());
647                    info!("Updated dialog lookup for confirmed dialog {}", dialog.id);
648                }
649            }
650        }
651        
652        // Emit session coordination event
653        if let Some(sdp) = sdp_answer {
654            let event = crate::events::SessionCoordinationEvent::CallAnswered {
655                dialog_id: self.call_id().clone(),
656                session_answer: sdp,
657            };
658            self.dialog_handle.dialog_manager.emit_session_coordination_event(event).await;
659        }
660        
661        info!("Successfully answered call {}", self.call_id());
662        Ok(())
663    }
664    
665    /// Reject the call
666    /// 
667    /// # Arguments
668    /// * `status_code` - SIP status code for rejection
669    /// * `reason` - Optional reason phrase
670    /// 
671    /// # Returns
672    /// Success or error
673    pub async fn reject(&self, status_code: StatusCode, reason: Option<String>) -> ApiResult<()> {
674        info!("Rejecting call {} with status {}", self.call_id(), status_code);
675        
676        // TODO: This should send an error response when response API is available
677        debug!("Call {} would be rejected with status {} reason: {:?}", 
678               self.call_id(), status_code, reason);
679        
680        Ok(())
681    }
682    
683    /// Hang up the call (send BYE)
684    pub async fn hangup(&self) -> ApiResult<()> {
685        info!("Hanging up call {}", self.call_id());
686        self.dialog_handle.terminate().await
687    }
688    
689    /// Put the call on hold
690    /// 
691    /// # Arguments
692    /// * `hold_sdp` - SDP with hold attributes
693    /// 
694    /// # Returns
695    /// Success or error
696    pub async fn hold(&self, hold_sdp: Option<String>) -> ApiResult<()> {
697        info!("Putting call {} on hold", self.call_id());
698        
699        // Send re-INVITE with hold SDP
700        self.dialog_handle.send_request(Method::Invite, hold_sdp).await?;
701        
702        Ok(())
703    }
704    
705    /// Resume the call from hold
706    /// 
707    /// # Arguments
708    /// * `resume_sdp` - SDP with active media attributes
709    /// 
710    /// # Returns
711    /// Success or error
712    pub async fn resume(&self, resume_sdp: Option<String>) -> ApiResult<()> {
713        info!("Resuming call {} from hold", self.call_id());
714        
715        // Send re-INVITE with active SDP
716        self.dialog_handle.send_request(Method::Invite, resume_sdp).await?;
717        
718        Ok(())
719    }
720    
721    /// Transfer the call
722    /// 
723    /// # Arguments
724    /// * `transfer_target` - URI to transfer the call to
725    /// 
726    /// # Returns
727    /// Success or error
728    pub async fn transfer(&self, transfer_target: String) -> ApiResult<()> {
729        info!("Transferring call {} to {}", self.call_id(), transfer_target);
730        
731        // Use the enhanced dialog handle method
732        self.dialog_handle.send_refer(transfer_target, None).await?;
733        
734        Ok(())
735    }
736    
737    /// **NEW**: Advanced transfer with custom REFER body
738    /// 
739    /// Allows sending custom REFER bodies for advanced transfer scenarios.
740    /// 
741    /// # Arguments
742    /// * `transfer_target` - URI to transfer the call to
743    /// * `refer_body` - Custom REFER body with additional headers
744    /// 
745    /// # Returns
746    /// Transaction key for the REFER request
747    pub async fn transfer_with_body(&self, transfer_target: String, refer_body: String) -> ApiResult<TransactionKey> {
748        info!("Transferring call {} to {} with custom body", self.call_id(), transfer_target);
749        
750        self.dialog_handle.send_refer(transfer_target, Some(refer_body)).await
751    }
752    
753    /// **NEW**: Send call-related notifications
754    /// 
755    /// Send NOTIFY requests for call-related events.
756    /// 
757    /// # Arguments
758    /// * `event` - Event type being notified
759    /// * `body` - Optional notification body
760    /// 
761    /// # Returns
762    /// Transaction key for the NOTIFY request
763    pub async fn notify(&self, event: String, body: Option<String>) -> ApiResult<TransactionKey> {
764        info!("Sending call notification for {} event {}", self.call_id(), event);
765        
766        self.dialog_handle.send_notify(event, body).await
767    }
768    
769    /// **NEW**: Update call media parameters
770    /// 
771    /// Send UPDATE request to modify media parameters without re-INVITE.
772    /// 
773    /// # Arguments
774    /// * `sdp` - Optional SDP body with new media parameters
775    /// 
776    /// # Returns
777    /// Transaction key for the UPDATE request
778    pub async fn update_media(&self, sdp: Option<String>) -> ApiResult<TransactionKey> {
779        info!("Updating media for call {}", self.call_id());
780        
781        self.dialog_handle.send_update(sdp).await
782    }
783    
784    /// **NEW**: Send call information
785    /// 
786    /// Send INFO request with call-related information.
787    /// 
788    /// # Arguments
789    /// * `info_body` - Information to send
790    /// 
791    /// # Returns
792    /// Transaction key for the INFO request
793    pub async fn send_info(&self, info_body: String) -> ApiResult<TransactionKey> {
794        info!("Sending call info for {}", self.call_id());
795        
796        self.dialog_handle.send_info(info_body).await
797    }
798    
799    /// **NEW**: Direct dialog operations for advanced use cases
800    
801    /// Get dialog state
802    pub async fn dialog_state(&self) -> ApiResult<DialogState> {
803        self.dialog_handle.state().await
804    }
805    
806    /// Send custom request in dialog
807    pub async fn send_request(&self, method: Method, body: Option<String>) -> ApiResult<TransactionKey> {
808        self.dialog_handle.send_request_with_key(method, body.map(|s| bytes::Bytes::from(s))).await
809    }
810    
811    /// Send response for transaction
812    pub async fn send_response(&self, transaction_id: &TransactionKey, response: Response) -> ApiResult<()> {
813        self.dialog_handle.send_response(transaction_id, response).await
814    }
815    
816    /// Check if the call is still active
817    pub async fn is_active(&self) -> bool {
818        self.dialog_handle.is_active().await
819    }
820}
821
822/// Information about a call
823///
824/// Provides comprehensive metadata about an active call including identifiers,
825/// state information, and participant details. This is typically obtained from
826/// CallHandle.info() and used for monitoring and debugging purposes.
827///
828/// ## Examples
829///
830/// ### Displaying Call Information
831///
832/// ```rust,no_run
833/// use rvoip_dialog_core::api::common::{CallHandle, CallInfo};
834/// use rvoip_dialog_core::dialog::DialogState;
835///
836/// # async fn example(call: CallHandle) -> Result<(), Box<dyn std::error::Error>> {
837/// let info = call.info().await?;
838///
839/// println!("=== Call Information ===");
840/// println!("Call ID: {}", info.call_id);
841/// println!("SIP Call-ID: {}", info.call_id_header);
842/// println!("From: {} (tag: {:?})", info.local_uri, info.local_tag);
843/// println!("To: {} (tag: {:?})", info.remote_uri, info.remote_tag);
844/// println!("State: {:?}", info.state);
845///
846/// match info.state {
847///     DialogState::Early => println!("Call is ringing"),
848///     DialogState::Confirmed => println!("Call is active"),
849///     DialogState::Terminated => println!("Call has ended"),
850///     _ => println!("Call in transition"),
851/// }
852/// # Ok(())
853/// # }
854/// ```
855///
856/// ### Conditional Operations Based on Call Info
857///
858/// ```rust,no_run
859/// use rvoip_dialog_core::api::common::{CallHandle, CallInfo};
860/// use rvoip_dialog_core::dialog::DialogState;
861///
862/// # async fn example(call: CallHandle) -> Result<(), Box<dyn std::error::Error>> {
863/// let info = call.info().await?;
864///
865/// // Only allow transfer if call is confirmed
866/// if info.state == DialogState::Confirmed {
867///     call.transfer("sip:voicemail@example.com".to_string()).await?;
868///     println!("Call transferred to voicemail");
869/// } else {
870///     println!("Cannot transfer call in state: {:?}", info.state);
871/// }
872///
873/// // Check if this is an outbound call (local tag exists)
874/// if info.local_tag.is_some() {
875///     println!("This is an outbound call");
876/// } else {
877///     println!("This is an inbound call");
878/// }
879/// # Ok(())
880/// # }
881/// ```
882#[derive(Debug, Clone)]
883pub struct CallInfo {
884    /// Call ID (dialog ID)
885    pub call_id: DialogId,
886    
887    /// Current call state
888    pub state: DialogState,
889    
890    /// Local URI
891    pub local_uri: String,
892    
893    /// Remote URI
894    pub remote_uri: String,
895    
896    /// SIP Call-ID header value
897    pub call_id_header: String,
898    
899    /// Local tag
900    pub local_tag: Option<String>,
901    
902    /// Remote tag
903    pub remote_tag: Option<String>,
904}
905
906/// Dialog events that applications can listen for
907///
908/// Represents various events that occur during the dialog lifecycle,
909/// allowing applications to monitor and react to dialog state changes,
910/// incoming requests, and responses.
911///
912/// ## Event Categories
913///
914/// - **Lifecycle Events**: Created, StateChanged, Terminated
915/// - **Message Events**: RequestReceived, ResponseReceived
916///
917/// ## Examples
918///
919/// ### Basic Event Handling
920///
921/// ```rust,no_run
922/// use rvoip_dialog_core::api::common::DialogEvent;
923/// use rvoip_dialog_core::dialog::DialogState;
924/// use rvoip_sip_core::Method;
925///
926/// fn handle_dialog_event(event: DialogEvent) {
927///     match event {
928///         DialogEvent::Created { dialog_id } => {
929///             println!("New dialog created: {}", dialog_id);
930///         },
931///         DialogEvent::StateChanged { dialog_id, old_state, new_state } => {
932///             println!("Dialog {} transitioned from {:?} to {:?}", 
933///                     dialog_id, old_state, new_state);
934///             
935///             if new_state == DialogState::Confirmed {
936///                 println!("Dialog is now ready for operations");
937///             }
938///         },
939///         DialogEvent::Terminated { dialog_id, reason } => {
940///             println!("Dialog {} ended: {}", dialog_id, reason);
941///         },
942///         DialogEvent::RequestReceived { dialog_id, method, body } => {
943///             println!("Dialog {} received {} request", dialog_id, method);
944///             if let Some(body) = body {
945///                 println!("Request body: {}", body);
946///             }
947///         },
948///         DialogEvent::ResponseReceived { dialog_id, status_code, body } => {
949///             println!("Dialog {} received {} response", dialog_id, status_code);
950///             if let Some(body) = body {
951///                 println!("Response body: {}", body);
952///             }
953///         },
954///     }
955/// }
956/// ```
957///
958/// ### Advanced Event Processing
959///
960/// ```rust,no_run
961/// use rvoip_dialog_core::api::common::DialogEvent;
962/// use rvoip_dialog_core::dialog::DialogState;
963/// use rvoip_sip_core::{Method, StatusCode};
964/// use std::collections::HashMap;
965///
966/// struct CallManager {
967///     active_calls: HashMap<String, CallInfo>,
968/// }
969///
970/// impl CallManager {
971///     fn handle_event(&mut self, event: DialogEvent) {
972///         match event {
973///             DialogEvent::Created { dialog_id } => {
974///                 println!("Tracking new dialog: {}", dialog_id);
975///                 // Initialize call tracking
976///             },
977///             DialogEvent::StateChanged { dialog_id, new_state, .. } => {
978///                 match new_state {
979///                     DialogState::Confirmed => {
980///                         println!("Call {} is now active", dialog_id);
981///                         // Start call timer, enable features, etc.
982///                     },
983///                     DialogState::Terminated => {
984///                         println!("Call {} ended", dialog_id);
985///                         // Cleanup call resources
986///                     },
987///                     _ => {}
988///                 }
989///             },
990///             DialogEvent::RequestReceived { dialog_id, method, .. } => {
991///                 match method {
992///                     Method::Invite => println!("Re-INVITE received on {}", dialog_id),
993///                     Method::Bye => println!("BYE received on {}", dialog_id),
994///                     Method::Info => println!("INFO received on {}", dialog_id),
995///                     _ => println!("Other request: {} on {}", method, dialog_id),
996///                 }
997///             },
998///             DialogEvent::ResponseReceived { dialog_id, status_code, .. } => {
999///                 // Pattern match on specific status codes
1000///                 match status_code {
1001///                     StatusCode::Ok | StatusCode::Accepted => {
1002///                         println!("Success response {} on {}", status_code, dialog_id);
1003///                     },
1004///                     StatusCode::BadRequest | StatusCode::NotFound | StatusCode::ServerInternalError => {
1005///                         println!("Error response {} on {}", status_code, dialog_id);
1006///                     },
1007///                     _ => {
1008///                         println!("Other response {} on {}", status_code, dialog_id);
1009///                     }
1010///                 }
1011///             },
1012///             _ => {}
1013///         }
1014///     }
1015/// }
1016/// # struct CallInfo;
1017/// ```
1018///
1019/// ### Event Filtering and Routing
1020///
1021/// ```rust,no_run
1022/// use rvoip_dialog_core::api::common::DialogEvent;
1023/// use rvoip_dialog_core::dialog::{DialogId, DialogState};
1024///
1025/// fn route_dialog_event(event: DialogEvent, call_id: &DialogId) {
1026///     // Only process events for the dialog we care about
1027///     let event_dialog_id = match &event {
1028///         DialogEvent::Created { dialog_id } => dialog_id,
1029///         DialogEvent::StateChanged { dialog_id, .. } => dialog_id,
1030///         DialogEvent::Terminated { dialog_id, .. } => dialog_id,
1031///         DialogEvent::RequestReceived { dialog_id, .. } => dialog_id,
1032///         DialogEvent::ResponseReceived { dialog_id, .. } => dialog_id,
1033///     };
1034///
1035///     if event_dialog_id == call_id {
1036///         println!("Processing event for our dialog: {:?}", event);
1037///         
1038///         // Handle the specific event
1039///         match event {
1040///             DialogEvent::StateChanged { new_state: DialogState::Confirmed, .. } => {
1041///                 println!("Our call is now active!");
1042///             },
1043///             DialogEvent::Terminated { reason, .. } => {
1044///                 println!("Our call ended: {}", reason);
1045///             },
1046///             _ => {}
1047///         }
1048///     }
1049/// }
1050/// ```
1051#[derive(Debug, Clone)]
1052pub enum DialogEvent {
1053    /// Dialog was created
1054    Created {
1055        dialog_id: DialogId,
1056    },
1057    
1058    /// Dialog state changed
1059    StateChanged {
1060        dialog_id: DialogId,
1061        old_state: DialogState,
1062        new_state: DialogState,
1063    },
1064    
1065    /// Dialog was terminated
1066    Terminated {
1067        dialog_id: DialogId,
1068        reason: String,
1069    },
1070    
1071    /// Request received in dialog
1072    RequestReceived {
1073        dialog_id: DialogId,
1074        method: Method,
1075        body: Option<String>,
1076    },
1077    
1078    /// Response received in dialog
1079    ResponseReceived {
1080        dialog_id: DialogId,
1081        status_code: StatusCode,
1082        body: Option<String>,
1083    },
1084}