rvoip_dialog_core/api/
client.rs

1//! Dialog Client API
2//!
3//! This module provides a high-level client interface for SIP dialog management,
4//! abstracting the complexity of the underlying DialogManager for client use cases.
5//!
6//! ## Overview
7//!
8//! The DialogClient is the primary interface for client-side SIP operations, providing
9//! a clean, intuitive API that handles the complexities of SIP dialog management while
10//! offering powerful features for call control, media negotiation, and session coordination.
11//!
12//! ### Key Features
13//!
14//! - **Call Management**: Make outgoing calls, handle responses, manage call lifecycle
15//! - **Dialog Operations**: Create, manage, and terminate SIP dialogs
16//! - **Session Integration**: Built-in coordination with session-core for media management
17//! - **Request/Response Handling**: Send arbitrary SIP methods and handle responses
18//! - **Statistics & Monitoring**: Track call metrics and dialog states
19//! - **Dependency Injection**: Clean architecture with proper separation of concerns
20//!
21//! ## Quick Start
22//!
23//! ### Basic Client Setup
24//!
25//! ```rust,no_run
26//! use rvoip_dialog_core::api::{DialogClient, DialogApi, ClientConfig};
27//! use rvoip_transaction_core::TransactionManager;
28//! use std::sync::Arc;
29//!
30//! #[tokio::main]
31//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
32//!     // Set up dependencies (transport setup omitted for brevity)
33//!     # let transport = unimplemented!(); // Mock transport
34//!     let tx_mgr = Arc::new(TransactionManager::new_sync(transport));
35//!     let config = ClientConfig::new("127.0.0.1:0".parse()?)
36//!         .with_from_uri("sip:alice@example.com");
37//!     
38//!     // Create and start the client
39//!     let client = DialogClient::with_dependencies(tx_mgr, config).await?;
40//!     client.start().await?;
41//!     
42//!     // Make a call
43//!     let call = client.make_call(
44//!         "sip:alice@example.com",
45//!         "sip:bob@example.com",
46//!         Some("v=0\r\no=- 123 456 IN IP4 192.168.1.100\r\n...".to_string())
47//!     ).await?;
48//!     
49//!     println!("Call created: {}", call.call_id());
50//!     
51//!     // Clean up
52//!     call.hangup().await?;
53//!     client.stop().await?;
54//!     
55//!     Ok(())
56//! }
57//! ```
58//!
59//! ## Architecture & Design Patterns
60//!
61//! ### Dependency Injection Pattern (Recommended)
62//!
63//! The DialogClient follows a clean dependency injection pattern to maintain proper
64//! separation of concerns. The client does not manage transport concerns directly:
65//!
66//! ```rust,no_run
67//! use rvoip_dialog_core::api::{DialogClient, ClientConfig};
68//! use rvoip_transaction_core::TransactionManager;
69//! use std::sync::Arc;
70//!
71//! # async fn setup_example() -> Result<(), Box<dyn std::error::Error>> {
72//! // 1. Set up transport layer (your responsibility)
73//! # let transport = unimplemented!(); // Your transport implementation
74//! 
75//! // 2. Create transaction manager with transport
76//! let tx_mgr = Arc::new(TransactionManager::new_sync(transport));
77//! 
78//! // 3. Configure client behavior
79//! let config = ClientConfig::new("192.168.1.100:5060".parse()?)
80//!     .with_from_uri("sip:user@domain.com")
81//!     .with_auth("username", "password");
82//! 
83//! // 4. Create client with dependencies
84//! let client = DialogClient::with_dependencies(tx_mgr, config).await?;
85//! # Ok(())
86//! # }
87//! ```
88//!
89//! ### Session Coordination Pattern
90//!
91//! For applications that need media management, use the session coordination pattern:
92//!
93//! ```rust,no_run
94//! use rvoip_dialog_core::api::{DialogClient, DialogApi};
95//! use rvoip_dialog_core::events::SessionCoordinationEvent;
96//! use tokio::sync::mpsc;
97//!
98//! # async fn session_example() -> Result<(), Box<dyn std::error::Error>> {
99//! # let (tx_mgr, config) = setup_dependencies().await?;
100//! let client = DialogClient::with_dependencies(tx_mgr, config).await?;
101//! 
102//! // Set up session coordination
103//! let (session_tx, mut session_rx) = mpsc::channel(100);
104//! client.set_session_coordinator(session_tx).await?;
105//! client.start().await?;
106//! 
107//! // Handle session events
108//! tokio::spawn(async move {
109//!     while let Some(event) = session_rx.recv().await {
110//!         match event {
111//!             SessionCoordinationEvent::IncomingCall { dialog_id, .. } => {
112//!                 println!("Incoming call: {}", dialog_id);
113//!                 // Coordinate with media layer
114//!             },
115//!             SessionCoordinationEvent::CallAnswered { dialog_id, session_answer } => {
116//!                 println!("Call answered: {}", dialog_id);
117//!                 // Set up media based on SDP answer
118//!             },
119//!             _ => {}
120//!         }
121//!     }
122//! });
123//! # Ok(())
124//! # }
125//! # async fn setup_dependencies() -> Result<(std::sync::Arc<rvoip_transaction_core::TransactionManager>, rvoip_dialog_core::api::ClientConfig), Box<dyn std::error::Error>> { unimplemented!() }
126//! ```
127//!
128//! ## Usage Patterns
129//!
130//! ### Pattern 1: Simple Call Management
131//!
132//! For basic voice calls with minimal complexity:
133//!
134//! ```rust,no_run
135//! use rvoip_dialog_core::api::DialogClient;
136//!
137//! # async fn simple_calls(client: DialogClient) -> Result<(), Box<dyn std::error::Error>> {
138//! // Make a call
139//! let call = client.make_call(
140//!     "sip:caller@example.com",
141//!     "sip:callee@example.com",
142//!     Some("SDP offer".to_string())
143//! ).await?;
144//! 
145//! // Basic call operations
146//! call.hold(Some("SDP with hold".to_string())).await?;
147//! call.resume(Some("SDP with active media".to_string())).await?;
148//! call.transfer("sip:transfer@example.com".to_string()).await?;
149//! call.hangup().await?;
150//! # Ok(())
151//! # }
152//! ```
153//!
154//! ### Pattern 2: Advanced Dialog Management
155//!
156//! For applications requiring fine-grained control over SIP dialogs:
157//!
158//! ```rust,no_run
159//! use rvoip_dialog_core::api::DialogClient;
160//! use rvoip_sip_core::Method;
161//!
162//! # async fn advanced_dialogs(client: DialogClient) -> Result<(), Box<dyn std::error::Error>> {
163//! // Create dialog without initial request
164//! let dialog = client.create_dialog("sip:me@here.com", "sip:you@there.com").await?;
165//! 
166//! // Send custom SIP methods
167//! client.send_notify(&dialog.id(), "presence".to_string(), Some("online".to_string())).await?;
168//! client.send_update(&dialog.id(), Some("Updated SDP".to_string())).await?;
169//! client.send_info(&dialog.id(), "Application data".to_string()).await?;
170//! 
171//! // Monitor dialog state
172//! let state = client.get_dialog_state(&dialog.id()).await?;
173//! println!("Dialog state: {:?}", state);
174//! 
175//! // Clean up
176//! client.terminate_dialog(&dialog.id()).await?;
177//! # Ok(())
178//! # }
179//! ```
180//!
181//! ### Pattern 3: Response Handling & Server-like Behavior
182//!
183//! For applications that need to handle incoming requests and send responses:
184//!
185//! ```rust,no_run
186//! use rvoip_dialog_core::api::DialogClient;
187//! use rvoip_sip_core::StatusCode;
188//! use rvoip_transaction_core::TransactionKey;
189//!
190//! # async fn response_handling(client: DialogClient) -> Result<(), Box<dyn std::error::Error>> {
191//! # let transaction_id: TransactionKey = unimplemented!();
192//! # let dialog_id = unimplemented!();
193//! 
194//! // Build and send responses
195//! let response = client.build_response(&transaction_id, StatusCode::Ok, Some("Response body".to_string())).await?;
196//! client.send_response(&transaction_id, response).await?;
197//! 
198//! // Or use convenience method
199//! client.send_status_response(&transaction_id, StatusCode::Accepted, None).await?;
200//! 
201//! // Dialog-aware responses
202//! let dialog_response = client.build_dialog_response(
203//!     &transaction_id, 
204//!     &dialog_id, 
205//!     StatusCode::Ok, 
206//!     Some("Dialog response".to_string())
207//! ).await?;
208//! client.send_response(&transaction_id, dialog_response).await?;
209//! # Ok(())
210//! # }
211//! ```
212//!
213//! ## Error Handling
214//!
215//! The DialogClient provides comprehensive error handling with specific error types:
216//!
217//! ```rust,no_run
218//! use rvoip_dialog_core::api::{DialogClient, ApiError};
219//!
220//! # async fn error_handling(client: DialogClient) -> Result<(), Box<dyn std::error::Error>> {
221//! match client.make_call("invalid-uri", "another-invalid", None).await {
222//!     Ok(call) => {
223//!         println!("Call successful: {}", call.call_id());
224//!     },
225//!     Err(ApiError::Configuration { message }) => {
226//!         eprintln!("Configuration issue: {}", message);
227//!         // Fix configuration and retry
228//!     },
229//!     Err(ApiError::Network { message }) => {
230//!         eprintln!("Network problem: {}", message);
231//!         // Check connectivity, retry with backoff
232//!     },
233//!     Err(ApiError::Protocol { message }) => {
234//!         eprintln!("SIP protocol error: {}", message);
235//!         // Log for debugging, potentially report upstream
236//!     },
237//!     Err(ApiError::Dialog { message }) => {
238//!         eprintln!("Dialog state error: {}", message);
239//!         // Handle dialog lifecycle issues
240//!     },
241//!     Err(ApiError::Internal { message }) => {
242//!         eprintln!("Internal error: {}", message);
243//!         // Log for debugging, potentially restart client
244//!     },
245//! }
246//! # Ok(())
247//! # }
248//! ```
249//!
250//! ## Configuration Examples
251//!
252//! ### Production Client Configuration
253//!
254//! ```rust
255//! use rvoip_dialog_core::api::ClientConfig;
256//! use std::time::Duration;
257//!
258//! let config = ClientConfig::new("192.168.1.100:5060".parse().unwrap())
259//!     .with_from_uri("sip:service@company.com")
260//!     .with_auth("service_user", "secure_password");
261//!
262//! // Customize for production load
263//! let mut prod_config = config;
264//! prod_config.dialog = prod_config.dialog
265//!     .with_timeout(Duration::from_secs(300))
266//!     .with_max_dialogs(10000)
267//!     .with_user_agent("MyApp/2.0 (Production)");
268//! ```
269//!
270//! ### Development Configuration
271//!
272//! ```rust
273//! use rvoip_dialog_core::api::ClientConfig;
274//! use std::time::Duration;
275//!
276//! let dev_config = ClientConfig::new("127.0.0.1:0".parse().unwrap())
277//!     .with_from_uri("sip:dev@localhost");
278//!
279//! // Fast timeouts for testing
280//! let mut test_config = dev_config;
281//! test_config.dialog = test_config.dialog
282//!     .with_timeout(Duration::from_secs(30))
283//!     .with_user_agent("MyApp-Dev/1.0");
284//! ```
285//!
286//! ## Integration with Other Components
287//!
288//! ### Media Layer Integration
289//!
290//! ```rust,no_run
291//! use rvoip_dialog_core::api::{DialogClient, DialogApi};
292//! use rvoip_dialog_core::events::SessionCoordinationEvent;
293//!
294//! # async fn media_integration(client: DialogClient) -> Result<(), Box<dyn std::error::Error>> {
295//! // Set up session coordination for media management
296//! let (session_tx, mut session_rx) = tokio::sync::mpsc::channel(100);
297//! client.set_session_coordinator(session_tx).await?;
298//!
299//! // Handle media-related events
300//! tokio::spawn(async move {
301//!     while let Some(event) = session_rx.recv().await {
302//!         match event {
303//!             SessionCoordinationEvent::IncomingCall { dialog_id, request, .. } => {
304//!                 // Extract SDP from request, set up media
305//!                 println!("Setting up media for call: {}", dialog_id);
306//!             },
307//!             SessionCoordinationEvent::CallAnswered { dialog_id, session_answer } => {
308//!                 // Use SDP answer to configure media streams
309//!                 println!("Configuring media with answer: {}", session_answer);
310//!             },
311//!             _ => {}
312//!         }
313//!     }
314//! });
315//! # Ok(())
316//! # }
317//! ```
318//!
319//! ### Monitoring & Statistics
320//!
321//! ```rust,no_run
322//! use rvoip_dialog_core::api::{DialogClient, DialogApi};
323//!
324//! # async fn monitoring(client: DialogClient) -> Result<(), Box<dyn std::error::Error>> {
325//! // Get client statistics
326//! let stats = client.get_stats().await;
327//! println!("=== Client Statistics ===");
328//! println!("Active dialogs: {}", stats.active_dialogs);
329//! println!("Total dialogs: {}", stats.total_dialogs);
330//! println!("Success rate: {:.1}%", 
331//!     100.0 * stats.successful_calls as f64 / (stats.successful_calls + stats.failed_calls) as f64);
332//! println!("Average call duration: {:.1}s", stats.avg_call_duration);
333//!
334//! // List active dialogs
335//! let active = client.active_dialogs().await;
336//! for dialog in active {
337//!     let info = dialog.info().await?;
338//!     println!("Dialog {}: {} -> {} ({})", 
339//!         dialog.id(), info.local_uri, info.remote_uri, info.state);
340//! }
341//! # Ok(())
342//! # }
343//! ```
344//!
345//! ## Best Practices
346//!
347//! 1. **Always use dependency injection**: Use `with_dependencies()` or `with_global_events()` in production
348//! 2. **Handle session coordination**: Set up proper event handling for media integration
349//! 3. **Monitor client health**: Regularly check statistics and active dialog counts
350//! 4. **Implement proper error handling**: Match on specific error types for appropriate responses
351//! 5. **Clean shutdown**: Always call `stop()` before terminating the application
352//! 6. **Use configuration validation**: Call `validate()` on configurations before use
353//! 7. **Leverage handles**: Use DialogHandle and CallHandle for dialog-specific operations
354//!
355//! ## Thread Safety
356//!
357//! DialogClient is designed to be thread-safe and can be safely shared across async tasks:
358//!
359//! ```rust,no_run
360//! use rvoip_dialog_core::api::DialogClient;
361//! use std::sync::Arc;
362//!
363//! # async fn thread_safety(client: DialogClient) -> Result<(), Box<dyn std::error::Error>> {
364//! let client = Arc::new(client);
365//!
366//! // Spawn multiple tasks using the same client
367//! let client1 = client.clone();
368//! let task1 = tokio::spawn(async move {
369//!     client1.make_call("sip:a@test.com", "sip:b@test.com", None).await
370//! });
371//!
372//! let client2 = client.clone();
373//! let task2 = tokio::spawn(async move {
374//!     client2.make_call("sip:c@test.com", "sip:d@test.com", None).await
375//! });
376//!
377//! // Both tasks can safely use the client concurrently
378//! let (call1, call2) = tokio::try_join!(task1, task2)?;
379//! # Ok(())
380//! # }
381//! ```
382
383use std::sync::Arc;
384use std::net::SocketAddr;
385use tokio::sync::mpsc;
386use tracing::{info, debug, warn};
387
388use rvoip_transaction_core::{TransactionManager, TransactionKey, TransactionEvent};
389use rvoip_transaction_core::builders::dialog_quick;
390use rvoip_sip_core::{Uri, Method, Response, StatusCode};
391
392use crate::manager::DialogManager;
393use crate::events::SessionCoordinationEvent;
394use crate::dialog::{DialogId, Dialog, DialogState};
395use super::{
396    ApiResult, ApiError, DialogApi, DialogStats,
397    config::ClientConfig,
398    common::{DialogHandle, CallHandle},
399};
400
401/// High-level client interface for SIP dialog management
402/// 
403/// The DialogClient provides a comprehensive, easy-to-use interface for client-side
404/// SIP operations. It handles the complexities of SIP dialog management while offering
405/// powerful features for call control, media negotiation, and session coordination.
406///
407/// ## Key Capabilities
408///
409/// - **Call Management**: Create, control, and terminate voice/video calls
410/// - **Dialog Operations**: Full SIP dialog lifecycle management
411/// - **Media Coordination**: Integration with media layers through session events
412/// - **Request/Response Handling**: Send arbitrary SIP methods and build responses
413/// - **Authentication**: Built-in support for SIP authentication challenges
414/// - **Statistics & Monitoring**: Real-time metrics and dialog state tracking
415///
416/// ## Constructor Patterns
417///
418/// The DialogClient supports multiple construction patterns to fit different use cases:
419///
420/// ### Pattern 1: Dependency Injection (Recommended)
421///
422/// ```rust,no_run
423/// use rvoip_dialog_core::api::{DialogClient, ClientConfig};
424/// use rvoip_transaction_core::TransactionManager;
425/// use std::sync::Arc;
426///
427/// # async fn dependency_injection() -> Result<(), Box<dyn std::error::Error>> {
428/// // Your application manages transport
429/// # let transport = unimplemented!(); // Your transport implementation
430/// let tx_mgr = Arc::new(TransactionManager::new_sync(transport));
431/// let config = ClientConfig::new("127.0.0.1:0".parse()?);
432///
433/// let client = DialogClient::with_dependencies(tx_mgr, config).await?;
434/// # Ok(())
435/// # }
436/// ```
437///
438/// ### Pattern 2: Global Events (Best for Event Processing)
439///
440/// ```rust,no_run
441/// use rvoip_dialog_core::api::{DialogClient, ClientConfig};
442/// use rvoip_transaction_core::{TransactionManager, TransactionEvent};
443/// use tokio::sync::mpsc;
444/// use std::sync::Arc;
445///
446/// # async fn global_events() -> Result<(), Box<dyn std::error::Error>> {
447/// # let transport = unimplemented!(); // Your transport
448/// let tx_mgr = Arc::new(TransactionManager::new_sync(transport));
449/// let (events_tx, events_rx) = mpsc::channel(1000);
450/// let config = ClientConfig::new("127.0.0.1:0".parse()?);
451///
452/// let client = DialogClient::with_global_events(tx_mgr, events_rx, config).await?;
453/// # Ok(())
454/// # }
455/// ```
456///
457/// ## Complete Usage Example
458///
459/// ```rust,no_run
460/// use rvoip_dialog_core::api::{DialogClient, DialogApi, ClientConfig};
461/// use rvoip_transaction_core::TransactionManager;
462/// use std::sync::Arc;
463///
464/// #[tokio::main]
465/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
466///     // Set up dependencies
467///     # let transport = unimplemented!(); // Your transport setup
468///     let tx_mgr = Arc::new(TransactionManager::new_sync(transport));
469///     let config = ClientConfig::new("192.168.1.100:5060".parse()?)
470///         .with_from_uri("sip:alice@company.com")
471///         .with_auth("alice", "secret123");
472///     
473///     // Create and start client
474///     let client = DialogClient::with_dependencies(tx_mgr, config).await?;
475///     client.start().await?;
476///     
477///     // Make an outgoing call
478///     let call = client.make_call(
479///         "sip:alice@company.com",
480///         "sip:bob@partner.com",
481///         Some("v=0\r\no=alice 123 456 IN IP4 192.168.1.100\r\n...".to_string())
482///     ).await?;
483///     
484///     println!("Call initiated: {}", call.call_id());
485///     
486///     // Call operations
487///     tokio::time::sleep(tokio::time::Duration::from_secs(5)).await;
488///     call.hold(Some("SDP with hold attributes".to_string())).await?;
489///     
490///     tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
491///     call.resume(Some("SDP with active media".to_string())).await?;
492///     
493///     // Transfer the call
494///     call.transfer("sip:voicemail@company.com".to_string()).await?;
495///     
496///     // Get statistics
497///     let stats = client.get_stats().await;
498///     println!("Client stats: {} active dialogs, {} total", 
499///         stats.active_dialogs, stats.total_dialogs);
500///     
501///     // Clean shutdown
502///     client.stop().await?;
503///     
504///     Ok(())
505/// }
506/// ```
507///
508/// ## Advanced Dialog Operations
509///
510/// ```rust,no_run
511/// use rvoip_dialog_core::api::DialogClient;
512/// use rvoip_sip_core::{Method, StatusCode};
513///
514/// # async fn advanced_operations(client: DialogClient) -> Result<(), Box<dyn std::error::Error>> {
515/// // Create a dialog without sending initial request
516/// let dialog = client.create_dialog("sip:me@here.com", "sip:you@there.com").await?;
517/// 
518/// // Send custom SIP methods within the dialog
519/// let info_tx = client.send_info(&dialog.id(), "Application-specific data".to_string()).await?;
520/// let notify_tx = client.send_notify(&dialog.id(), "presence".to_string(), Some("online".to_string())).await?;
521/// let update_tx = client.send_update(&dialog.id(), Some("Updated media SDP".to_string())).await?;
522/// 
523/// // Monitor dialog state
524/// let state = client.get_dialog_state(&dialog.id()).await?;
525/// println!("Dialog state: {:?}", state);
526/// 
527/// // Build and send a response (if acting as UAS)
528/// # let transaction_id = unimplemented!();
529/// let response = client.build_response(&transaction_id, StatusCode::Ok, Some("Success".to_string())).await?;
530/// client.send_response(&transaction_id, response).await?;
531/// 
532/// // Terminate the dialog
533/// client.terminate_dialog(&dialog.id()).await?;
534/// # Ok(())
535/// # }
536/// ```
537///
538/// ## Session Coordination Integration
539///
540/// ```rust,no_run
541/// use rvoip_dialog_core::api::{DialogClient, DialogApi};
542/// use rvoip_dialog_core::events::SessionCoordinationEvent;
543/// use tokio::sync::mpsc;
544///
545/// # async fn session_integration(client: DialogClient) -> Result<(), Box<dyn std::error::Error>> {
546/// // Set up session coordination for media management
547/// let (session_tx, mut session_rx) = mpsc::channel(100);
548/// client.set_session_coordinator(session_tx).await?;
549/// client.start().await?;
550///
551/// // Handle session events from the client
552/// let session_handler = tokio::spawn(async move {
553///     while let Some(event) = session_rx.recv().await {
554///         match event {
555///             SessionCoordinationEvent::IncomingCall { dialog_id, request, .. } => {
556///                 println!("Incoming call on dialog {}", dialog_id);
557///                 // Extract SDP, set up media, send response
558///             },
559///             SessionCoordinationEvent::CallAnswered { dialog_id, session_answer } => {
560///                 println!("Call {} answered", dialog_id);
561///                 // Configure media streams based on SDP answer
562///             },
563///             SessionCoordinationEvent::CallTerminated { dialog_id, reason } => {
564///                 println!("Call {} terminated: {}", dialog_id, reason);
565///                 // Clean up media resources
566///             },
567///             _ => {}
568///         }
569///     }
570/// });
571///
572/// // Make calls while session handler processes events
573/// let call = client.make_call("sip:me@here.com", "sip:you@there.com", None).await?;
574/// 
575/// // Session events will be automatically generated and handled
576/// tokio::time::sleep(tokio::time::Duration::from_secs(10)).await;
577/// call.hangup().await?;
578/// 
579/// # Ok(())
580/// # }
581/// ```
582///
583/// ## Error Handling Strategies
584///
585/// ```rust,no_run
586/// use rvoip_dialog_core::api::{DialogClient, ApiError};
587/// use tokio::time::{sleep, Duration};
588///
589/// # async fn error_handling_strategies(client: DialogClient) -> Result<(), Box<dyn std::error::Error>> {
590/// // Retry logic for network errors
591/// let mut attempts = 0;
592/// let max_attempts = 3;
593///
594/// loop {
595///     match client.make_call("sip:me@here.com", "sip:you@there.com", None).await {
596///         Ok(call) => {
597///             println!("Call successful: {}", call.call_id());
598///             break;
599///         },
600///         Err(ApiError::Network { message }) if attempts < max_attempts => {
601///             attempts += 1;
602///             println!("Network error (attempt {}): {}", attempts, message);
603///             sleep(Duration::from_secs(2u64.pow(attempts))).await; // Exponential backoff
604///             continue;
605///         },
606///         Err(ApiError::Configuration { message }) => {
607///             eprintln!("Configuration error: {}", message);
608///             // Fix configuration and restart
609///             break;
610///         },
611///         Err(ApiError::Protocol { message }) => {
612///             eprintln!("SIP protocol error: {}", message);
613///             // Log for debugging, continue with other operations
614///             break;
615///         },
616///         Err(e) => {
617///             eprintln!("Unrecoverable error: {}", e);
618///             break;
619///         }
620///     }
621/// }
622/// # Ok(())
623/// # }
624/// ```
625pub struct DialogClient {
626    /// Underlying dialog manager
627    dialog_manager: Arc<DialogManager>,
628    
629    /// Client configuration
630    config: ClientConfig,
631    
632    /// Statistics tracking
633    stats: Arc<tokio::sync::RwLock<ClientStats>>,
634}
635
636/// Internal statistics tracking
637#[derive(Debug, Default)]
638struct ClientStats {
639    active_dialogs: usize,
640    total_dialogs: u64,
641    successful_calls: u64,
642    failed_calls: u64,
643    total_call_duration: f64,
644}
645
646impl DialogClient {
647    /// Create a new dialog client with simple configuration
648    /// 
649    /// This is the easiest way to create a client - just provide a local address
650    /// and the client will be configured with sensible defaults.
651    /// 
652    /// # Arguments
653    /// * `local_address` - Local address to use (e.g., "127.0.0.1:0")
654    /// 
655    /// # Returns
656    /// A configured DialogClient ready to start
657    pub async fn new(local_address: &str) -> ApiResult<Self> {
658        let addr: SocketAddr = local_address.parse()
659            .map_err(|e| ApiError::Configuration { 
660                message: format!("Invalid local address '{}': {}", local_address, e) 
661            })?;
662        
663        let config = ClientConfig::new(addr);
664        Self::with_config(config).await
665    }
666    
667    /// Create a dialog client with custom configuration
668    /// 
669    /// **ARCHITECTURAL NOTE**: This method requires dependency injection to maintain
670    /// proper separation of concerns. dialog-core should not directly manage transport
671    /// concerns - that's the responsibility of transaction-core.
672    /// 
673    /// Use `with_global_events()` or `with_dependencies()` instead, where you provide
674    /// a pre-configured TransactionManager that handles all transport setup.
675    /// 
676    /// # Arguments
677    /// * `config` - Client configuration (for validation and future use)
678    /// 
679    /// # Returns
680    /// An error directing users to the proper dependency injection constructors
681    pub async fn with_config(config: ClientConfig) -> ApiResult<Self> {
682        // Validate configuration for future use
683        config.validate()
684            .map_err(|e| ApiError::Configuration { message: e })?;
685            
686        // Return architectural guidance error
687        Err(ApiError::Configuration { 
688            message: format!(
689                "Simple construction violates architectural separation of concerns. \
690                 dialog-core should not manage transport directly. \
691                 \nUse dependency injection instead:\
692                 \n\n1. with_global_events(transaction_manager, events, config) - RECOMMENDED\
693                 \n2. with_dependencies(transaction_manager, config)\
694                 \n\nExample setup in your application:\
695                 \n  // Set up transport and transaction manager in your app\
696                 \n  let (tx_mgr, events) = TransactionManager::with_transport(transport).await?;\
697                 \n  let client = DialogClient::with_global_events(tx_mgr, events, config).await?;\
698                 \n\nSee examples/ directory for complete setup patterns.",
699            )
700        })
701    }
702    
703    /// Create a dialog client with dependency injection and global events (RECOMMENDED)
704    /// 
705    /// This is the **recommended** constructor for production applications. It uses global 
706    /// transaction event subscription which provides better event handling reliability and 
707    /// prevents event loss that can occur with per-transaction subscriptions.
708    ///
709    /// ## Why Global Events?
710    ///
711    /// - **Reliable Event Processing**: All transaction events flow through a single channel
712    /// - **No Event Loss**: Events are queued and processed in order
713    /// - **Better Resource Management**: Single event loop handles all transactions
714    /// - **Cleaner Architecture**: Separation between event generation and consumption
715    ///
716    /// ## Usage Pattern
717    ///
718    /// ```rust,no_run
719    /// use rvoip_dialog_core::api::{DialogClient, DialogApi, ClientConfig};
720    /// use rvoip_transaction_core::{TransactionManager, TransactionEvent};
721    /// use tokio::sync::mpsc;
722    /// use std::sync::Arc;
723    ///
724    /// #[tokio::main]
725    /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
726    ///     // Set up transport layer (your responsibility)
727    ///     # let transport = unimplemented!(); // Your transport implementation
728    ///     
729    ///     // Create transaction manager
730    ///     let tx_mgr = Arc::new(TransactionManager::new_sync(transport));
731    ///     
732    ///     // Set up global event channel
733    ///     let (event_tx, event_rx) = mpsc::channel(1000);
734    ///     
735    ///     // Configure client
736    ///     let config = ClientConfig::new("192.168.1.100:5060".parse()?)
737    ///         .with_from_uri("sip:client@company.com")
738    ///         .with_auth("client_user", "password123");
739    ///     
740    ///     // Create client with global events
741    ///     let client = DialogClient::with_global_events(tx_mgr, event_rx, config).await?;
742    ///     
743    ///     // Start client and begin processing
744    ///     client.start().await?;
745    ///     
746    ///     // Client is now ready for operations
747    ///     let call = client.make_call(
748    ///         "sip:client@company.com",
749    ///         "sip:target@partner.com",
750    ///         None
751    ///     ).await?;
752    ///     
753    ///     // Clean shutdown
754    ///     call.hangup().await?;
755    ///     client.stop().await?;
756    ///     
757    ///     Ok(())
758    /// }
759    /// ```
760    ///
761    /// ## Advanced Event Processing
762    ///
763    /// ```rust,no_run
764    /// use rvoip_dialog_core::api::{DialogClient, ClientConfig, DialogApi};
765    /// use rvoip_transaction_core::{TransactionManager, TransactionEvent};
766    /// use tokio::sync::mpsc;
767    /// use std::sync::Arc;
768    ///
769    /// # async fn advanced_event_processing() -> Result<(), Box<dyn std::error::Error>> {
770    /// # let transport = unimplemented!();
771    /// let tx_mgr = Arc::new(TransactionManager::new_sync(transport));
772    /// let (event_tx, event_rx) = mpsc::channel(1000);
773    /// let config = ClientConfig::new("127.0.0.1:0".parse()?);
774    ///
775    /// // Create client
776    /// let client = DialogClient::with_global_events(tx_mgr.clone(), event_rx, config).await?;
777    ///
778    /// // You can also handle transaction events directly if needed
779    /// tokio::spawn(async move {
780    ///     // This would be your custom transaction event processor
781    ///     // The client gets its own copy of events through the channel
782    /// });
783    ///
784    /// client.start().await?;
785    /// # Ok(())
786    /// # }
787    /// ```
788    /// 
789    /// # Arguments
790    /// * `transaction_manager` - Pre-configured transaction manager with transport
791    /// * `transaction_events` - Global transaction event receiver channel
792    /// * `config` - Client configuration with network and authentication settings
793    /// 
794    /// # Returns
795    /// A configured DialogClient ready to start processing
796    ///
797    /// # Errors
798    /// - `ApiError::Configuration` - Invalid configuration parameters
799    /// - `ApiError::Internal` - Failed to create dialog manager with global events
800    pub async fn with_global_events(
801        transaction_manager: Arc<TransactionManager>,
802        transaction_events: mpsc::Receiver<TransactionEvent>,
803        config: ClientConfig,
804    ) -> ApiResult<Self> {
805        // Validate configuration
806        config.validate()
807            .map_err(|e| ApiError::Configuration { message: e })?;
808        
809        info!("Creating DialogClient with global transaction events (RECOMMENDED PATTERN)");
810        
811        // Create dialog manager with global event subscription (ROOT CAUSE FIX)
812        let dialog_manager = Arc::new(
813            DialogManager::with_global_events(transaction_manager, transaction_events, config.dialog.local_address).await
814                .map_err(|e| ApiError::Internal { 
815                    message: format!("Failed to create dialog manager with global events: {}", e) 
816                })?
817        );
818        
819        Ok(Self {
820            dialog_manager,
821            config,
822            stats: Arc::new(tokio::sync::RwLock::new(ClientStats::default())),
823        })
824    }
825    
826    /// Create a dialog client with dependency injection
827    /// 
828    /// This constructor provides direct dependency injection for scenarios where you need
829    /// full control over the dependencies. It's particularly useful for testing, legacy
830    /// integration, or when you have existing infrastructure to integrate with.
831    ///
832    /// ## When to Use This Pattern
833    ///
834    /// - **Testing**: When you need to inject mock dependencies
835    /// - **Legacy Integration**: When working with existing transaction managers
836    /// - **Simple Use Cases**: When you don't need complex event processing
837    /// - **Custom Event Handling**: When you want to handle transaction events differently
838    ///
839    /// ## Production Considerations
840    ///
841    /// **⚠️ Warning**: This method uses individual transaction event subscriptions which
842    /// may not be as reliable as global event subscriptions. For production applications,
843    /// consider using `with_global_events()` instead.
844    ///
845    /// ## Usage Examples
846    ///
847    /// ### Basic Usage
848    ///
849    /// ```rust,no_run
850    /// use rvoip_dialog_core::api::{DialogClient, DialogApi, ClientConfig};
851    /// use rvoip_transaction_core::TransactionManager;
852    /// use std::sync::Arc;
853    ///
854    /// # async fn basic_usage() -> Result<(), Box<dyn std::error::Error>> {
855    /// // Set up transport (your responsibility)
856    /// # let transport = unimplemented!(); // Your transport implementation
857    /// let tx_mgr = Arc::new(TransactionManager::new_sync(transport));
858    /// let config = ClientConfig::new("127.0.0.1:0".parse()?);
859    ///
860    /// // Create client with dependency injection
861    /// let client = DialogClient::with_dependencies(tx_mgr, config).await?;
862    /// client.start().await?;
863    ///
864    /// // Client is ready for use
865    /// let stats = client.get_stats().await;
866    /// println!("Client initialized: {} active dialogs", stats.active_dialogs);
867    /// # Ok(())
868    /// # }
869    /// ```
870    ///
871    /// ### Testing Pattern
872    ///
873    /// ```rust,no_run
874    /// use rvoip_dialog_core::api::{DialogClient, ClientConfig};
875    /// use rvoip_transaction_core::TransactionManager;
876    /// use std::sync::Arc;
877    ///
878    /// # async fn testing_pattern() -> Result<(), Box<dyn std::error::Error>> {
879    /// // Create mock transport for testing
880    /// # let mock_transport = unimplemented!(); // Your mock transport
881    /// let tx_mgr = Arc::new(TransactionManager::new_sync(mock_transport));
882    /// let config = ClientConfig::new("127.0.0.1:0".parse()?)
883    ///     .with_from_uri("sip:test@localhost");
884    ///
885    /// let client = DialogClient::with_dependencies(tx_mgr, config).await?;
886    ///
887    /// // Test client operations
888    /// assert_eq!(client.config().from_uri.as_ref().unwrap(), "sip:test@localhost");
889    /// # Ok(())
890    /// # }
891    /// ```
892    ///
893    /// ### Integration with Existing Infrastructure
894    ///
895    /// ```rust,no_run
896    /// use rvoip_dialog_core::api::{DialogClient, ClientConfig, DialogApi};
897    /// use rvoip_transaction_core::TransactionManager;
898    /// use std::sync::Arc;
899    ///
900    /// # async fn infrastructure_integration(existing_tx_mgr: Arc<TransactionManager>) -> Result<(), Box<dyn std::error::Error>> {
901    /// // Use existing transaction manager from your infrastructure
902    /// let config = ClientConfig::new("10.0.1.50:5060".parse()?)
903    ///     .with_from_uri("sip:service@internal.company.com")
904    ///     .with_auth("service", "internal_password");
905    ///
906    /// let client = DialogClient::with_dependencies(existing_tx_mgr, config).await?;
907    ///
908    /// // Integrate with existing systems
909    /// client.start().await?;
910    /// # Ok(())
911    /// # }
912    /// ```
913    /// 
914    /// # Arguments
915    /// * `transaction_manager` - Pre-configured transaction manager with transport
916    /// * `config` - Client configuration with network and authentication settings
917    /// 
918    /// # Returns
919    /// A configured DialogClient ready to start processing
920    ///
921    /// # Errors
922    /// - `ApiError::Configuration` - Invalid configuration parameters
923    /// - `ApiError::Internal` - Failed to create dialog manager
924    pub async fn with_dependencies(
925        transaction_manager: Arc<TransactionManager>,
926        config: ClientConfig,
927    ) -> ApiResult<Self> {
928        // Validate configuration
929        config.validate()
930            .map_err(|e| ApiError::Configuration { message: e })?;
931        
932        info!("Creating DialogClient with injected dependencies");
933        warn!("WARNING: Using old DialogManager::new() pattern - consider upgrading to with_global_events() for better reliability");
934        
935        // Create dialog manager with injected dependencies (OLD PATTERN - may have event issues)
936        let dialog_manager = Arc::new(
937            DialogManager::new(transaction_manager, config.dialog.local_address).await
938                .map_err(|e| ApiError::Internal { 
939                    message: format!("Failed to create dialog manager: {}", e) 
940                })?
941        );
942        
943        Ok(Self {
944            dialog_manager,
945            config,
946            stats: Arc::new(tokio::sync::RwLock::new(ClientStats::default())),
947        })
948    }
949    
950    /// Make an outgoing call
951    /// 
952    /// Creates a new SIP dialog and initiates an outgoing call by sending an INVITE request.
953    /// This is the primary method for establishing voice/video calls and handles all the
954    /// complexity of SIP dialog creation, request generation, and initial negotiation.
955    ///
956    /// ## Call Flow
957    ///
958    /// 1. **URI Validation**: Validates and parses the provided SIP URIs
959    /// 2. **Dialog Creation**: Creates a new outgoing dialog with unique identifiers
960    /// 3. **INVITE Generation**: Builds and sends an INVITE request with optional SDP
961    /// 4. **Handle Creation**: Returns a CallHandle for subsequent call operations
962    /// 5. **Statistics Update**: Updates internal call tracking metrics
963    ///
964    /// ## Usage Examples
965    ///
966    /// ### Basic Call
967    ///
968    /// ```rust,no_run
969    /// use rvoip_dialog_core::api::DialogClient;
970    ///
971    /// # async fn basic_call(client: DialogClient) -> Result<(), Box<dyn std::error::Error>> {
972    /// let call = client.make_call(
973    ///     "sip:alice@company.com",      // Who the call is from
974    ///     "sip:bob@partner.com",        // Who to call
975    ///     None                          // No SDP offer (late negotiation)
976    /// ).await?;
977    ///
978    /// println!("Call initiated: {}", call.call_id());
979    /// // Call is now ringing, wait for response...
980    /// # Ok(())
981    /// # }
982    /// ```
983    ///
984    /// ### Call with SDP Offer (Early Media Negotiation)
985    ///
986    /// ```rust,no_run
987    /// use rvoip_dialog_core::api::DialogClient;
988    ///
989    /// # async fn call_with_sdp(client: DialogClient) -> Result<(), Box<dyn std::error::Error>> {
990    /// let sdp_offer = r#"v=0
991    /// o=alice 2890844526 2890844527 IN IP4 192.168.1.100
992    /// s=Call
993    /// c=IN IP4 192.168.1.100
994    /// t=0 0
995    /// m=audio 5004 RTP/AVP 0
996    /// a=rtpmap:0 PCMU/8000"#;
997    ///
998    /// let call = client.make_call(
999    ///     "sip:alice@company.com",
1000    ///     "sip:bob@partner.com",
1001    ///     Some(sdp_offer.to_string())
1002    /// ).await?;
1003    ///
1004    /// println!("Call with media offer sent: {}", call.call_id());
1005    /// # Ok(())
1006    /// # }
1007    /// ```
1008    ///
1009    /// ### Production Call with Error Handling
1010    ///
1011    /// ```rust,no_run
1012    /// use rvoip_dialog_core::api::{DialogClient, ApiError};
1013    /// use tokio::time::{timeout, Duration};
1014    ///
1015    /// # async fn production_call(client: DialogClient) -> Result<(), Box<dyn std::error::Error>> {
1016    /// // Set a timeout for the call setup
1017    /// let call_result = timeout(Duration::from_secs(30), async {
1018    ///     client.make_call(
1019    ///         "sip:service@company.com",
1020    ///         "sip:customer@external.com",
1021    ///         None
1022    ///     ).await
1023    /// }).await;
1024    ///
1025    /// match call_result {
1026    ///     Ok(Ok(call)) => {
1027    ///         println!("Call successful: {}", call.call_id());
1028    ///         
1029    ///         // Set up call monitoring
1030    ///         tokio::spawn(async move {
1031    ///             if call.is_active().await {
1032    ///                 println!("Call is active and can be managed");
1033    ///             }
1034    ///         });
1035    ///     },
1036    ///     Ok(Err(ApiError::Configuration { message })) => {
1037    ///         eprintln!("Configuration error: {}", message);
1038    ///         // Fix URIs and retry
1039    ///     },
1040    ///     Ok(Err(ApiError::Network { message })) => {
1041    ///         eprintln!("Network error: {}", message);
1042    ///         // Check connectivity and retry
1043    ///     },
1044    ///     Err(_) => {
1045    ///         eprintln!("Call setup timed out");
1046    ///         // Handle timeout scenario
1047    ///     },
1048    ///     Ok(Err(e)) => {
1049    ///         eprintln!("Call failed: {}", e);
1050    ///     }
1051    /// }
1052    /// # Ok(())
1053    /// # }
1054    /// ```
1055    ///
1056    /// ### Batch Call Operations
1057    ///
1058    /// ```rust,no_run
1059    /// use rvoip_dialog_core::api::DialogClient;
1060    ///
1061    /// # async fn batch_calls(client: DialogClient) -> Result<(), Box<dyn std::error::Error>> {
1062    /// let call_targets = vec![
1063    ///     ("sip:alice@company.com", "sip:customer1@external.com"),
1064    ///     ("sip:alice@company.com", "sip:customer2@external.com"),
1065    ///     ("sip:alice@company.com", "sip:customer3@external.com"),
1066    /// ];
1067    ///
1068    /// // Make multiple calls sequentially for simplicity
1069    /// let mut calls = Vec::new();
1070    /// for (from, to) in call_targets {
1071    ///     let call = client.make_call(from, to, None).await?;
1072    ///     calls.push(call);
1073    /// }
1074    /// println!("Successfully initiated {} calls", calls.len());
1075    ///
1076    /// // Monitor all calls
1077    /// for call in calls {
1078    ///     let info = call.info().await?;
1079    ///     println!("Call {}: {} -> {} ({})", 
1080    ///         call.call_id(), info.local_uri, info.remote_uri, info.state);
1081    /// }
1082    /// # Ok(())
1083    /// # }
1084    /// ```
1085    /// 
1086    /// # Arguments
1087    /// * `from_uri` - Local SIP URI (From header) - who the call is from
1088    /// * `to_uri` - Remote SIP URI (To header) - who to call  
1089    /// * `sdp_offer` - Optional SDP offer for media negotiation
1090    /// 
1091    /// # Returns
1092    /// A CallHandle for managing the call lifecycle and operations
1093    ///
1094    /// # Errors
1095    /// - `ApiError::Configuration` - Invalid SIP URIs provided
1096    /// - `ApiError::Dialog` - Failed to create dialog or send INVITE
1097    /// - `ApiError::Internal` - Internal dialog manager error
1098    pub async fn make_call(
1099        &self,
1100        from_uri: &str,
1101        to_uri: &str,
1102        sdp_offer: Option<String>,
1103    ) -> ApiResult<CallHandle> {
1104        info!("Making call from {} to {}", from_uri, to_uri);
1105        
1106        // Parse URIs
1107        let local_uri: Uri = from_uri.parse()
1108            .map_err(|e| ApiError::Configuration { 
1109                message: format!("Invalid from URI '{}': {}", from_uri, e) 
1110            })?;
1111        
1112        let remote_uri: Uri = to_uri.parse()
1113            .map_err(|e| ApiError::Configuration { 
1114                message: format!("Invalid to URI '{}': {}", to_uri, e) 
1115            })?;
1116        
1117        // Create outgoing dialog
1118        let dialog_id = self.dialog_manager.create_outgoing_dialog(
1119            local_uri,
1120            remote_uri,
1121            None, // Let dialog manager generate call-id
1122        ).await.map_err(ApiError::from)?;
1123        
1124        // Send INVITE request
1125        let body_bytes = sdp_offer.map(|s| bytes::Bytes::from(s));
1126        let _transaction_key = self.dialog_manager.send_request(&dialog_id, Method::Invite, body_bytes).await
1127            .map_err(ApiError::from)?;
1128        
1129        // Update statistics
1130        {
1131            let mut stats = self.stats.write().await;
1132            stats.active_dialogs += 1;
1133            stats.total_dialogs += 1;
1134        }
1135        
1136        debug!("Call created with dialog ID: {}", dialog_id);
1137        Ok(CallHandle::new(dialog_id, self.dialog_manager.clone()))
1138    }
1139    
1140    /// Create a new dialog without sending a request
1141    /// 
1142    /// Creates a SIP dialog without immediately sending an INVITE or other initial request.
1143    /// This is useful for advanced scenarios where you need fine-grained control over the
1144    /// dialog lifecycle, want to send custom requests, or need to prepare dialogs for
1145    /// specific protocol sequences.
1146    ///
1147    /// ## When to Use This Method
1148    ///
1149    /// - **Custom Protocol Sequences**: When you need to send non-INVITE initial requests
1150    /// - **Conditional Call Setup**: When call initiation depends on external factors
1151    /// - **Advanced Dialog Management**: When you need dialog state before sending requests
1152    /// - **Testing Scenarios**: When you want to test dialog creation independently
1153    /// - **Batch Operations**: When preparing multiple dialogs for coordinated operations
1154    ///
1155    /// ## Usage Examples
1156    ///
1157    /// ### Basic Dialog Creation
1158    ///
1159    /// ```rust,no_run
1160    /// use rvoip_dialog_core::api::DialogClient;
1161    /// use rvoip_sip_core::Method;
1162    ///
1163    /// # async fn basic_dialog(client: DialogClient) -> Result<(), Box<dyn std::error::Error>> {
1164    /// // Create dialog without initial request
1165    /// let dialog = client.create_dialog(
1166    ///     "sip:alice@company.com",
1167    ///     "sip:bob@partner.com"
1168    /// ).await?;
1169    ///
1170    /// println!("Dialog created: {}", dialog.id());
1171    ///
1172    /// // Now you can send custom requests
1173    /// dialog.send_request(Method::Options, None).await?;
1174    /// dialog.send_request(Method::Info, Some("Custom info".to_string())).await?;
1175    ///
1176    /// // Check dialog state
1177    /// let state = dialog.state().await?;
1178    /// println!("Dialog state: {:?}", state);
1179    /// # Ok(())
1180    /// # }
1181    /// ```
1182    ///
1183    /// ### Conditional Call Setup
1184    ///
1185    /// ```rust,no_run
1186    /// use rvoip_dialog_core::api::DialogClient;
1187    /// use rvoip_sip_core::Method;
1188    ///
1189    /// # async fn conditional_setup(client: DialogClient) -> Result<(), Box<dyn std::error::Error>> {
1190    /// // Create dialog first
1191    /// let dialog = client.create_dialog(
1192    ///     "sip:service@company.com",
1193    ///     "sip:customer@external.com"
1194    /// ).await?;
1195    ///
1196    /// // Check external conditions before proceeding
1197    /// if check_customer_availability().await? {
1198    ///     // Send INVITE if customer is available
1199    ///     dialog.send_request(Method::Invite, Some("SDP offer".to_string())).await?;
1200    ///     println!("Call initiated for available customer");
1201    /// } else {
1202    ///     // Send MESSAGE instead
1203    ///     dialog.send_request(Method::Message, Some("Customer callback requested".to_string())).await?;
1204    ///     println!("Message sent to unavailable customer");
1205    /// }
1206    /// # Ok(())
1207    /// # }
1208    /// # async fn check_customer_availability() -> Result<bool, Box<dyn std::error::Error>> { Ok(true) }
1209    /// ```
1210    ///
1211    /// ### Advanced Protocol Sequences
1212    ///
1213    /// ```rust,no_run
1214    /// use rvoip_dialog_core::api::DialogClient;
1215    /// use rvoip_sip_core::Method;
1216    ///
1217    /// # async fn advanced_protocol(client: DialogClient) -> Result<(), Box<dyn std::error::Error>> {
1218    /// let dialog = client.create_dialog(
1219    ///     "sip:alice@company.com",
1220    ///     "sip:conference@partner.com"
1221    /// ).await?;
1222    ///
1223    /// // Custom protocol: Send OPTIONS first to check capabilities
1224    /// let options_tx = dialog.send_request(Method::Options, None).await?;
1225    /// println!("Sent OPTIONS: {}", options_tx);
1226    ///
1227    /// // Wait for response processing (in real code, you'd handle this via events)
1228    /// tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;
1229    ///
1230    /// // Send SUBSCRIBE for presence information
1231    /// let subscribe_tx = dialog.send_request(
1232    ///     Method::Subscribe, 
1233    ///     Some("Event: presence\r\nExpires: 3600".to_string())
1234    /// ).await?;
1235    /// println!("Sent SUBSCRIBE: {}", subscribe_tx);
1236    ///
1237    /// // Finally, send INVITE for the actual call
1238    /// let invite_tx = dialog.send_request(Method::Invite, Some("SDP offer".to_string())).await?;
1239    /// println!("Sent INVITE: {}", invite_tx);
1240    /// # Ok(())
1241    /// # }
1242    /// ```
1243    ///
1244    /// ### Batch Dialog Preparation
1245    ///
1246    /// ```rust,no_run
1247    /// use rvoip_dialog_core::api::DialogClient;
1248    /// use std::future::Future;
1249    ///
1250    /// # async fn batch_dialogs(client: DialogClient) -> Result<(), Box<dyn std::error::Error>> {
1251    /// let targets = vec![
1252    ///     "sip:customer1@external.com",
1253    ///     "sip:customer2@external.com", 
1254    ///     "sip:customer3@external.com",
1255    /// ];
1256    ///
1257    /// // Create multiple dialogs concurrently using join_all from std
1258    /// let mut dialogs = Vec::new();
1259    /// for target in targets {
1260    ///     let dialog = client.create_dialog("sip:service@company.com", target).await?;
1261    ///     dialogs.push(dialog);
1262    /// }
1263    /// println!("Created {} dialogs", dialogs.len());
1264    ///
1265    /// // Now you can coordinate operations across all dialogs
1266    /// for (i, dialog) in dialogs.iter().enumerate() {
1267    ///     // Stagger call initiation
1268    ///     tokio::time::sleep(tokio::time::Duration::from_millis(i as u64 * 100)).await;
1269    ///     dialog.send_request(rvoip_sip_core::Method::Invite, None).await?;
1270    /// }
1271    /// # Ok(())
1272    /// # }
1273    /// ```
1274    /// 
1275    /// # Arguments
1276    /// * `from_uri` - Local SIP URI (From header)
1277    /// * `to_uri` - Remote SIP URI (To header)
1278    /// 
1279    /// # Returns
1280    /// A DialogHandle for the new dialog
1281    ///
1282    /// # Errors
1283    /// - `ApiError::Configuration` - Invalid SIP URIs provided
1284    /// - `ApiError::Dialog` - Failed to create dialog
1285    /// - `ApiError::Internal` - Internal dialog manager error
1286    pub async fn create_dialog(&self, from_uri: &str, to_uri: &str) -> ApiResult<DialogHandle> {
1287        debug!("Creating dialog from {} to {}", from_uri, to_uri);
1288        
1289        // Parse URIs
1290        let local_uri: Uri = from_uri.parse()
1291            .map_err(|e| ApiError::Configuration { 
1292                message: format!("Invalid from URI '{}': {}", from_uri, e) 
1293            })?;
1294        
1295        let remote_uri: Uri = to_uri.parse()
1296            .map_err(|e| ApiError::Configuration { 
1297                message: format!("Invalid to URI '{}': {}", to_uri, e) 
1298            })?;
1299        
1300        // Create outgoing dialog
1301        let dialog_id = self.dialog_manager.create_outgoing_dialog(
1302            local_uri,
1303            remote_uri,
1304            None,
1305        ).await.map_err(ApiError::from)?;
1306        
1307        // Update statistics
1308        {
1309            let mut stats = self.stats.write().await;
1310            stats.active_dialogs += 1;
1311            stats.total_dialogs += 1;
1312        }
1313        
1314        Ok(DialogHandle::new(dialog_id, self.dialog_manager.clone()))
1315    }
1316    
1317    // **NEW**: Dialog-level coordination methods for session-core integration
1318    
1319    /// Send a SIP request within an existing dialog
1320    /// 
1321    /// Sends arbitrary SIP methods within an established dialog. This method provides
1322    /// direct access to SIP protocol operations and is essential for session-core
1323    /// coordination, custom protocol implementations, and advanced call control.
1324    ///
1325    /// ## Supported SIP Methods
1326    ///
1327    /// This method can send any SIP method within a dialog context:
1328    ///
1329    /// - **INVITE**: Re-INVITE for media changes, call transfers
1330    /// - **BYE**: Call termination (prefer using `send_bye()` convenience method)
1331    /// - **UPDATE**: Media parameter updates (RFC 3311)
1332    /// - **INFO**: Application-specific information (RFC 6086)
1333    /// - **REFER**: Call transfers and redirections (RFC 3515)
1334    /// - **NOTIFY**: Event notifications (RFC 3265)
1335    /// - **MESSAGE**: Instant messaging within dialogs
1336    /// - **OPTIONS**: Capability queries
1337    /// - **SUBSCRIBE**: Event subscriptions
1338    ///
1339    /// ## Usage Examples
1340    ///
1341    /// ### Media Management with UPDATE
1342    ///
1343    /// ```rust,no_run
1344    /// use rvoip_dialog_core::api::DialogClient;
1345    /// use rvoip_sip_core::Method;
1346    /// use rvoip_dialog_core::dialog::DialogId;
1347    ///
1348    /// # async fn media_update(client: DialogClient, dialog_id: DialogId) -> Result<(), Box<dyn std::error::Error>> {
1349    /// // Send UPDATE to modify media parameters
1350    /// let updated_sdp = r#"v=0
1351    /// o=alice 2890844526 2890844528 IN IP4 192.168.1.100
1352    /// s=Updated Call
1353    /// c=IN IP4 192.168.1.100
1354    /// t=0 0
1355    /// m=audio 5004 RTP/AVP 0 8
1356    /// a=rtpmap:0 PCMU/8000
1357    /// a=rtpmap:8 PCMA/8000"#;
1358    ///
1359    /// let tx_key = client.send_request_in_dialog(
1360    ///     &dialog_id,
1361    ///     Method::Update,
1362    ///     Some(bytes::Bytes::from(updated_sdp))
1363    /// ).await?;
1364    ///
1365    /// println!("Sent UPDATE request: {}", tx_key);
1366    /// # Ok(())
1367    /// # }
1368    /// ```
1369    ///
1370    /// ### Application Information Exchange
1371    ///
1372    /// ```rust,no_run
1373    /// use rvoip_dialog_core::api::DialogClient;
1374    /// use rvoip_sip_core::Method;
1375    /// use rvoip_dialog_core::dialog::DialogId;
1376    ///
1377    /// # async fn info_exchange(client: DialogClient, dialog_id: DialogId) -> Result<(), Box<dyn std::error::Error>> {
1378    /// // Send application-specific information
1379    /// let app_data = r#"Content-Type: application/json
1380    ///
1381    /// {
1382    ///   "action": "screen_share_request",
1383    ///   "timestamp": "2024-01-15T10:30:00Z",
1384    ///   "session_id": "abc123"
1385    /// }"#;
1386    ///
1387    /// let tx_key = client.send_request_in_dialog(
1388    ///     &dialog_id,
1389    ///     Method::Info,
1390    ///     Some(bytes::Bytes::from(app_data))
1391    /// ).await?;
1392    ///
1393    /// println!("Sent INFO with application data: {}", tx_key);
1394    /// # Ok(())
1395    /// # }
1396    /// ```
1397    ///
1398    /// ### Event Notifications
1399    ///
1400    /// ```rust,no_run
1401    /// use rvoip_dialog_core::api::DialogClient;
1402    /// use rvoip_sip_core::Method;
1403    /// use rvoip_dialog_core::dialog::DialogId;
1404    ///
1405    /// # async fn event_notification(client: DialogClient, dialog_id: DialogId) -> Result<(), Box<dyn std::error::Error>> {
1406    /// // Send NOTIFY for presence update
1407    /// let notify_body = r#"Event: presence
1408    /// Subscription-State: active;expires=3600
1409    /// Content-Type: application/pidf+xml
1410    ///
1411    /// <?xml version="1.0" encoding="UTF-8"?>
1412    /// <presence xmlns="urn:ietf:params:xml:ns:pidf" entity="sip:alice@company.com">
1413    ///   <tuple id="tuple1">
1414    ///     <status><basic>open</basic></status>
1415    ///     <contact>sip:alice@company.com</contact>
1416    ///   </tuple>
1417    /// </presence>"#;
1418    ///
1419    /// let tx_key = client.send_request_in_dialog(
1420    ///     &dialog_id,
1421    ///     Method::Notify,
1422    ///     Some(bytes::Bytes::from(notify_body))
1423    /// ).await?;
1424    ///
1425    /// println!("Sent NOTIFY for presence: {}", tx_key);
1426    /// # Ok(())
1427    /// # }
1428    /// ```
1429    ///
1430    /// ### Call Transfer with REFER
1431    ///
1432    /// ```rust,no_run
1433    /// use rvoip_dialog_core::api::DialogClient;
1434    /// use rvoip_sip_core::Method;
1435    /// use rvoip_dialog_core::dialog::DialogId;
1436    ///
1437    /// # async fn call_transfer(client: DialogClient, dialog_id: DialogId) -> Result<(), Box<dyn std::error::Error>> {
1438    /// // Send REFER for call transfer
1439    /// let refer_body = r#"Refer-To: sip:bob@partner.com
1440    /// Referred-By: sip:alice@company.com
1441    /// Contact: sip:alice@company.com
1442    /// Content-Length: 0"#;
1443    ///
1444    /// let tx_key = client.send_request_in_dialog(
1445    ///     &dialog_id,
1446    ///     Method::Refer,
1447    ///     Some(bytes::Bytes::from(refer_body))
1448    /// ).await?;
1449    ///
1450    /// println!("Sent REFER for transfer: {}", tx_key);
1451    /// # Ok(())
1452    /// # }
1453    /// ```
1454    ///
1455    /// ### Session Coordination Pattern
1456    ///
1457    /// ```rust,no_run
1458    /// use rvoip_dialog_core::api::DialogClient;
1459    /// use rvoip_sip_core::Method;
1460    /// use rvoip_dialog_core::dialog::DialogId;
1461    ///
1462    /// # async fn session_coordination(client: DialogClient, dialog_id: DialogId) -> Result<(), Box<dyn std::error::Error>> {
1463    /// // Coordinate with session layer for media changes
1464    /// let media_session = setup_media_session().await?;
1465    /// let new_sdp = media_session.generate_offer().await?;
1466    ///
1467    /// // Send re-INVITE with new media parameters
1468    /// let tx_key = client.send_request_in_dialog(
1469    ///     &dialog_id,
1470    ///     Method::Invite,
1471    ///     Some(bytes::Bytes::from(new_sdp))
1472    /// ).await?;
1473    ///
1474    /// println!("Sent re-INVITE for media change: {}", tx_key);
1475    ///
1476    /// // The response will be handled by session coordination events
1477    /// # Ok(())
1478    /// # }
1479    /// # async fn setup_media_session() -> Result<MediaSession, Box<dyn std::error::Error>> { Ok(MediaSession) }
1480    /// # struct MediaSession;
1481    /// # impl MediaSession {
1482    /// #     async fn generate_offer(&self) -> Result<String, Box<dyn std::error::Error>> { Ok("SDP".to_string()) }
1483    /// # }
1484    /// ```
1485    /// 
1486    /// # Arguments
1487    /// * `dialog_id` - The dialog ID to send the request within
1488    /// * `method` - SIP method to send (INVITE, BYE, UPDATE, INFO, etc.)
1489    /// * `body` - Optional message body (SDP, application data, etc.)
1490    /// 
1491    /// # Returns
1492    /// Transaction key for tracking the request and its response
1493    ///
1494    /// # Errors
1495    /// - `ApiError::Dialog` - Dialog not found or invalid state
1496    /// - `ApiError::Protocol` - Invalid method for current dialog state
1497    /// - `ApiError::Internal` - Failed to send request
1498    pub async fn send_request_in_dialog(
1499        &self,
1500        dialog_id: &DialogId,
1501        method: Method,
1502        body: Option<bytes::Bytes>
1503    ) -> ApiResult<TransactionKey> {
1504        debug!("Sending {} request in dialog {}", method, dialog_id);
1505        
1506        self.dialog_manager.send_request(dialog_id, method, body).await
1507            .map_err(ApiError::from)
1508    }
1509    
1510    /// Get detailed information about a dialog
1511    /// 
1512    /// Provides access to the complete dialog state for session coordination
1513    /// and monitoring purposes.
1514    /// 
1515    /// # Arguments
1516    /// * `dialog_id` - The dialog ID to query
1517    /// 
1518    /// # Returns
1519    /// Complete dialog information
1520    pub async fn get_dialog_info(&self, dialog_id: &DialogId) -> ApiResult<Dialog> {
1521        self.dialog_manager.get_dialog(dialog_id)
1522            .map_err(ApiError::from)
1523    }
1524    
1525    /// Get the current state of a dialog
1526    /// 
1527    /// Provides quick access to dialog state without retrieving the full
1528    /// dialog information.
1529    /// 
1530    /// # Arguments
1531    /// * `dialog_id` - The dialog ID to query
1532    /// 
1533    /// # Returns
1534    /// Current dialog state
1535    pub async fn get_dialog_state(&self, dialog_id: &DialogId) -> ApiResult<DialogState> {
1536        self.dialog_manager.get_dialog_state(dialog_id)
1537            .map_err(ApiError::from)
1538    }
1539    
1540    /// Terminate a dialog and clean up resources
1541    /// 
1542    /// This method provides direct control over dialog termination,
1543    /// which is essential for session lifecycle management.
1544    /// 
1545    /// # Arguments
1546    /// * `dialog_id` - The dialog ID to terminate
1547    /// 
1548    /// # Returns
1549    /// Success or error
1550    pub async fn terminate_dialog(&self, dialog_id: &DialogId) -> ApiResult<()> {
1551        debug!("Terminating dialog {}", dialog_id);
1552        
1553        self.dialog_manager.terminate_dialog(dialog_id).await
1554            .map_err(ApiError::from)
1555    }
1556    
1557    /// List all active dialog IDs
1558    /// 
1559    /// Provides access to all active dialogs for monitoring and
1560    /// management purposes.
1561    /// 
1562    /// # Returns
1563    /// Vector of active dialog IDs
1564    pub async fn list_active_dialogs(&self) -> Vec<DialogId> {
1565        self.dialog_manager.list_dialogs()
1566    }
1567    
1568    /// Send a SIP response for a transaction
1569    /// 
1570    /// Provides direct control over response generation, which is essential
1571    /// for custom response handling in session coordination.
1572    /// 
1573    /// # Arguments
1574    /// * `transaction_id` - Transaction to respond to
1575    /// * `response` - Complete SIP response
1576    /// 
1577    /// # Returns
1578    /// Success or error
1579    pub async fn send_response(
1580        &self,
1581        transaction_id: &TransactionKey,
1582        response: Response
1583    ) -> ApiResult<()> {
1584        debug!("Sending response for transaction {}", transaction_id);
1585        
1586        self.dialog_manager.send_response(transaction_id, response).await
1587            .map_err(ApiError::from)
1588    }
1589    
1590    /// Build a SIP response with automatic header generation
1591    /// 
1592    /// Convenience method for creating properly formatted SIP responses
1593    /// with correct headers and routing information using Phase 3 dialog functions.
1594    /// 
1595    /// # Arguments
1596    /// * `transaction_id` - Transaction to respond to
1597    /// * `status_code` - SIP status code
1598    /// * `body` - Optional response body
1599    /// 
1600    /// # Returns
1601    /// Built SIP response ready for sending
1602    pub async fn build_response(
1603        &self,
1604        transaction_id: &TransactionKey,
1605        status_code: StatusCode,
1606        body: Option<String>
1607    ) -> ApiResult<Response> {
1608        debug!("Building response with status {} for transaction {} using Phase 3 functions", status_code, transaction_id);
1609        
1610        // Get original request from transaction manager
1611        let original_request = self.dialog_manager()
1612            .transaction_manager()
1613            .original_request(transaction_id)
1614            .await
1615            .map_err(|e| ApiError::Internal { 
1616                message: format!("Failed to get original request: {}", e) 
1617            })?
1618            .ok_or_else(|| ApiError::Internal { 
1619                message: "No original request found for transaction".to_string() 
1620            })?;
1621        
1622        // Use Phase 3 dialog quick function for instant response creation - ONE LINER!
1623        let response = dialog_quick::response_for_dialog_transaction(
1624            transaction_id.to_string(),
1625            original_request,
1626            None, // No specific dialog ID
1627            status_code,
1628            self.dialog_manager.local_address,
1629            body,
1630            None // No custom reason
1631        ).map_err(|e| ApiError::Internal { 
1632            message: format!("Failed to build response using Phase 3 functions: {}", e) 
1633        })?;
1634        
1635        debug!("Successfully built response with status {} for transaction {} using Phase 3 functions", status_code, transaction_id);
1636        Ok(response)
1637    }
1638    
1639    /// Build a dialog-aware response with enhanced context
1640    /// 
1641    /// This method provides dialog-aware response building using Phase 3 dialog utilities
1642    /// to ensure proper response construction for dialog transactions.
1643    /// 
1644    /// # Arguments
1645    /// * `transaction_id` - Transaction to respond to
1646    /// * `dialog_id` - Dialog ID for context
1647    /// * `status_code` - SIP status code
1648    /// * `body` - Optional response body
1649    /// 
1650    /// # Returns
1651    /// Built SIP response with dialog awareness
1652    pub async fn build_dialog_response(
1653        &self,
1654        transaction_id: &TransactionKey,
1655        dialog_id: &DialogId,
1656        status_code: StatusCode,
1657        body: Option<String>
1658    ) -> ApiResult<Response> {
1659        debug!("Building dialog-aware response with status {} for transaction {} in dialog {} using Phase 3 functions", 
1660               status_code, transaction_id, dialog_id);
1661        
1662        // Get original request from transaction manager
1663        let original_request = self.dialog_manager()
1664            .transaction_manager()
1665            .original_request(transaction_id)
1666            .await
1667            .map_err(|e| ApiError::Internal { 
1668                message: format!("Failed to get original request: {}", e) 
1669            })?
1670            .ok_or_else(|| ApiError::Internal { 
1671                message: "No original request found for transaction".to_string() 
1672            })?;
1673        
1674        // Use Phase 3 dialog quick function with dialog context - ONE LINER!
1675        let response = dialog_quick::response_for_dialog_transaction(
1676            transaction_id.to_string(),
1677            original_request,
1678            Some(dialog_id.to_string()),
1679            status_code,
1680            self.dialog_manager.local_address,
1681            body,
1682            None // No custom reason
1683        ).map_err(|e| ApiError::Internal { 
1684            message: format!("Failed to build dialog response using Phase 3 functions: {}", e) 
1685        })?;
1686        
1687        debug!("Successfully built dialog-aware response with status {} for transaction {} in dialog {} using Phase 3 functions", 
1688               status_code, transaction_id, dialog_id);
1689        Ok(response)
1690    }
1691    
1692    /// Send a status response with automatic response building
1693    /// 
1694    /// Convenience method for sending simple status responses without
1695    /// manual response construction.
1696    /// 
1697    /// # Arguments
1698    /// * `transaction_id` - Transaction to respond to
1699    /// * `status_code` - SIP status code
1700    /// * `reason` - Optional reason phrase
1701    /// 
1702    /// # Returns
1703    /// Success or error
1704    pub async fn send_status_response(
1705        &self,
1706        transaction_id: &TransactionKey,
1707        status_code: StatusCode,
1708        reason: Option<String>
1709    ) -> ApiResult<()> {
1710        debug!("Sending status response {} for transaction {}", status_code, transaction_id);
1711        
1712        // Build the response using our build_response method
1713        let response = self.build_response(transaction_id, status_code, reason).await?;
1714        
1715        // Send the response using the dialog manager
1716        self.send_response(transaction_id, response).await?;
1717        
1718        debug!("Successfully sent status response {} for transaction {}", status_code, transaction_id);
1719        Ok(())
1720    }
1721    
1722    // **NEW**: SIP method-specific convenience methods
1723    
1724    /// Send a BYE request to terminate a dialog
1725    /// 
1726    /// Convenience method for the common operation of ending a call
1727    /// by sending a BYE request.
1728    /// 
1729    /// # Arguments
1730    /// * `dialog_id` - Dialog to terminate
1731    /// 
1732    /// # Returns
1733    /// Transaction key for the BYE request
1734    pub async fn send_bye(&self, dialog_id: &DialogId) -> ApiResult<TransactionKey> {
1735        info!("Sending BYE for dialog {}", dialog_id);
1736        self.send_request_in_dialog(dialog_id, Method::Bye, None).await
1737    }
1738    
1739    /// Send a REFER request for call transfer
1740    /// 
1741    /// Convenience method for initiating call transfers using the
1742    /// REFER method as defined in RFC 3515.
1743    /// 
1744    /// # Arguments
1745    /// * `dialog_id` - Dialog to send REFER within
1746    /// * `target_uri` - URI to transfer the call to
1747    /// * `refer_body` - Optional REFER body with additional headers
1748    /// 
1749    /// # Returns
1750    /// Transaction key for the REFER request
1751    pub async fn send_refer(
1752        &self,
1753        dialog_id: &DialogId,
1754        target_uri: String,
1755        refer_body: Option<String>
1756    ) -> ApiResult<TransactionKey> {
1757        info!("Sending REFER for dialog {} to {}", dialog_id, target_uri);
1758        
1759        let body = if let Some(custom_body) = refer_body {
1760            custom_body
1761        } else {
1762            format!("Refer-To: {}\r\n", target_uri)
1763        };
1764        
1765        self.send_request_in_dialog(dialog_id, Method::Refer, Some(bytes::Bytes::from(body))).await
1766    }
1767    
1768    /// Send a NOTIFY request for event notifications
1769    /// 
1770    /// Convenience method for sending event notifications using the
1771    /// NOTIFY method as defined in RFC 3265.
1772    /// 
1773    /// # Arguments
1774    /// * `dialog_id` - Dialog to send NOTIFY within
1775    /// * `event` - Event type being notified
1776    /// * `body` - Optional notification body
1777    /// 
1778    /// # Returns
1779    /// Transaction key for the NOTIFY request
1780    pub async fn send_notify(
1781        &self,
1782        dialog_id: &DialogId,
1783        event: String,
1784        body: Option<String>
1785    ) -> ApiResult<TransactionKey> {
1786        info!("Sending NOTIFY for dialog {} event {}", dialog_id, event);
1787        
1788        let notify_body = body.map(|b| bytes::Bytes::from(b));
1789        self.send_request_in_dialog(dialog_id, Method::Notify, notify_body).await
1790    }
1791    
1792    /// Send an UPDATE request for media modifications
1793    /// 
1794    /// Convenience method for updating media parameters using the
1795    /// UPDATE method as defined in RFC 3311.
1796    /// 
1797    /// # Arguments
1798    /// * `dialog_id` - Dialog to send UPDATE within
1799    /// * `sdp` - Optional SDP body with new media parameters
1800    /// 
1801    /// # Returns
1802    /// Transaction key for the UPDATE request
1803    pub async fn send_update(
1804        &self,
1805        dialog_id: &DialogId,
1806        sdp: Option<String>
1807    ) -> ApiResult<TransactionKey> {
1808        info!("Sending UPDATE for dialog {}", dialog_id);
1809        
1810        let update_body = sdp.map(|s| bytes::Bytes::from(s));
1811        self.send_request_in_dialog(dialog_id, Method::Update, update_body).await
1812    }
1813    
1814    /// Send an INFO request for application-specific information
1815    /// 
1816    /// Convenience method for sending application-specific information
1817    /// using the INFO method as defined in RFC 6086.
1818    /// 
1819    /// # Arguments
1820    /// * `dialog_id` - Dialog to send INFO within
1821    /// * `info_body` - Information to send
1822    /// 
1823    /// # Returns
1824    /// Transaction key for the INFO request
1825    pub async fn send_info(
1826        &self,
1827        dialog_id: &DialogId,
1828        info_body: String
1829    ) -> ApiResult<TransactionKey> {
1830        info!("Sending INFO for dialog {}", dialog_id);
1831        
1832        self.send_request_in_dialog(dialog_id, Method::Info, Some(bytes::Bytes::from(info_body))).await
1833    }
1834    
1835    /// Get client configuration
1836    pub fn config(&self) -> &ClientConfig {
1837        &self.config
1838    }
1839    
1840    /// Get a list of all active dialog handles
1841    pub async fn active_dialogs(&self) -> Vec<DialogHandle> {
1842        let dialog_ids = self.dialog_manager.list_dialogs();
1843        dialog_ids.into_iter()
1844            .map(|id| DialogHandle::new(id, self.dialog_manager.clone()))
1845            .collect()
1846    }
1847}
1848
1849impl DialogApi for DialogClient {
1850    fn dialog_manager(&self) -> &Arc<DialogManager> {
1851        &self.dialog_manager
1852    }
1853    
1854    async fn set_session_coordinator(&self, sender: mpsc::Sender<SessionCoordinationEvent>) -> ApiResult<()> {
1855        info!("Setting session coordinator for dialog client");
1856        self.dialog_manager.set_session_coordinator(sender).await;
1857        Ok(())
1858    }
1859    
1860    async fn start(&self) -> ApiResult<()> {
1861        info!("Starting dialog client");
1862        
1863        self.dialog_manager.start().await
1864            .map_err(ApiError::from)?;
1865        
1866        info!("✅ Dialog client started successfully");
1867        Ok(())
1868    }
1869    
1870    async fn stop(&self) -> ApiResult<()> {
1871        info!("Stopping dialog client");
1872        
1873        self.dialog_manager.stop().await
1874            .map_err(ApiError::from)?;
1875        
1876        info!("✅ Dialog client stopped successfully");
1877        Ok(())
1878    }
1879    
1880    async fn get_stats(&self) -> DialogStats {
1881        let stats = self.stats.read().await;
1882        DialogStats {
1883            active_dialogs: stats.active_dialogs,
1884            total_dialogs: stats.total_dialogs,
1885            successful_calls: stats.successful_calls,
1886            failed_calls: stats.failed_calls,
1887            avg_call_duration: if stats.successful_calls > 0 {
1888                stats.total_call_duration / stats.successful_calls as f64
1889            } else {
1890                0.0
1891            },
1892        }
1893    }
1894}