//! Axum Router Integration for ACP Server
//!
//! This module provides the Axum router and HTTP handlers for the ACP Server.
//! It exposes a JSON-RPC endpoint at `/rpc` and an SSE events endpoint at `/events`.
//!
//! ## Architecture
//!
//! The router is generic over `ConnectorOperations`, allowing different implementations
//! to be used (e.g., one backed by `CoreHandle` for production, or `NoOpConnectorOperations`
//! for testing).
//!
//! ## Endpoints
//!
//! - `POST /rpc` - JSON-RPC 2.0 endpoint for method calls
//! - `GET /events?client_id=...` - SSE stream for real-time notifications
//! - `GET /health` - Health check endpoint
//!
//! ## Example
//!
//! ```rust,ignore
//! use dirigent_acp_api::router::{AcpServerState, create_acp_server_router};
//! use dirigent_acp_api::{AcpServerConfig, NoOpConnectorOperations};
//!
//! // Create state
//! let state = AcpServerState::new(AcpServerConfig::enabled());
//!
//! // Create router with no-op operations for testing
//! let router = create_acp_server_router(state, NoOpConnectorOperations);
//! ```

use std::convert::Infallible;
use std::sync::Arc;
use std::time::Instant;

use axum::{
    extract::{Query, State},
    http::{HeaderMap, StatusCode},
    response::{
        sse::{Event, KeepAlive, Sse},
        IntoResponse, Json,
    },
    routing::{get, post},
    Router,
};
use serde::{Deserialize, Serialize};
use tokio_stream::StreamExt;
use tower_http::cors::{Any, CorsLayer};
use tracing::{debug, info, trace, warn};

use crate::config::AcpServerConfig;
use crate::rpc::{ConnectorOperations, RpcHandler};
use crate::session_manager::SessionManager;
use crate::sse::SseNotifier;

// ============================================================================
// AcpServerState (T031)
// ============================================================================

/// Internal state shared across handlers
struct AcpServerStateInner {
    /// Session manager for tracking client sessions
    session_manager: SessionManager,

    /// SSE notifier for broadcasting events to clients
    sse_notifier: SseNotifier,

    /// Agent request tracker for bidirectional request/response
    agent_request_tracker: Arc<crate::agent_requests::AgentRequestTracker>,

    /// Server configuration
    config: AcpServerConfig,
}

/// Shared state for the ACP Server Axum handlers
///
/// This struct contains all the state needed by the HTTP handlers:
/// - Session manager for tracking client sessions and mappings
/// - SSE notifier for broadcasting events to connected clients
/// - Server configuration
///
/// The state is wrapped in `Arc` internally, making it cheap to clone and
/// share across async tasks and handlers.
///
/// Note: `RpcHandler` is created per-request with `ConnectorOperations` passed in,
/// as the connector operations implementation cannot be stored in the shared state
/// (it may contain non-Clone types like `CoreHandle`).
#[derive(Clone)]
pub struct AcpServerState {
    inner: Arc<AcpServerStateInner>,
}

impl AcpServerState {
    /// Create a new ACP server state with the given configuration
    ///
    /// # Parameters
    ///
    /// - `config`: The server configuration
    ///
    /// # Example
    ///
    /// ```rust
    /// use dirigent_acp_api::router::AcpServerState;
    /// use dirigent_acp_api::AcpServerConfig;
    ///
    /// let state = AcpServerState::new(AcpServerConfig::enabled());
    /// ```
    pub fn new(config: AcpServerConfig) -> Self {
        Self {
            inner: Arc::new(AcpServerStateInner {
                session_manager: SessionManager::new(),
                sse_notifier: SseNotifier::new(),
                agent_request_tracker: Arc::new(crate::agent_requests::AgentRequestTracker::new()),
                config,
            }),
        }
    }

    /// Create a new ACP server state with custom components
    ///
    /// This is useful for testing or when you need to share a session manager
    /// or SSE notifier with other parts of the application.
    ///
    /// # Parameters
    ///
    /// - `session_manager`: The session manager instance
    /// - `sse_notifier`: The SSE notifier instance
    /// - `agent_request_tracker`: The agent request tracker instance
    /// - `config`: The server configuration
    pub fn with_components(
        session_manager: SessionManager,
        sse_notifier: SseNotifier,
        agent_request_tracker: Arc<crate::agent_requests::AgentRequestTracker>,
        config: AcpServerConfig,
    ) -> Self {
        Self {
            inner: Arc::new(AcpServerStateInner {
                session_manager,
                sse_notifier,
                agent_request_tracker,
                config,
            }),
        }
    }

    /// Get a reference to the session manager
    pub fn session_manager(&self) -> &SessionManager {
        &self.inner.session_manager
    }

    /// Get a reference to the SSE notifier
    pub fn sse_notifier(&self) -> &SseNotifier {
        &self.inner.sse_notifier
    }

    /// Get a reference to the agent request tracker
    pub fn agent_request_tracker(&self) -> &Arc<crate::agent_requests::AgentRequestTracker> {
        &self.inner.agent_request_tracker
    }

    /// Get a reference to the configuration
    pub fn config(&self) -> &AcpServerConfig {
        &self.inner.config
    }
}

impl std::fmt::Debug for AcpServerState {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("AcpServerState")
            .field("session_count", &self.inner.session_manager.mapping_count())
            .field("client_count", &self.inner.sse_notifier.client_count())
            .field("config", &self.inner.config)
            .finish()
    }
}

// ============================================================================
// Router State with ConnectorOperations (T028)
// ============================================================================

/// Combined state for Axum handlers that includes both server state and connector operations
///
/// This struct combines the shared `AcpServerState` with a `ConnectorOperations`
/// implementation. It's used as the Axum state to provide handlers with access
/// to both session management and connector functionality.
#[derive(Clone)]
pub struct RouterState<C: ConnectorOperations + Clone + Send + Sync + 'static> {
    /// The ACP server state (session manager, SSE notifier, config)
    pub state: AcpServerState,

    /// The connector operations implementation
    pub connector_ops: C,
}

impl<C: ConnectorOperations + Clone + Send + Sync + 'static> RouterState<C> {
    /// Create a new router state
    pub fn new(state: AcpServerState, connector_ops: C) -> Self {
        Self {
            state,
            connector_ops,
        }
    }
}

// ============================================================================
// Router Factory (T028)
// ============================================================================

/// Create the ACP server router with all endpoints configured
///
/// This function creates an Axum router with the following endpoints:
/// - `POST /rpc` - JSON-RPC 2.0 endpoint
/// - `GET /events` - SSE events stream
/// - `GET /health` - Health check
///
/// CORS middleware is applied based on the configuration.
///
/// # Type Parameters
///
/// - `C`: The connector operations implementation
///
/// # Parameters
///
/// - `state`: The ACP server state
/// - `connector_ops`: The connector operations implementation
///
/// # Example
///
/// ```rust,ignore
/// use dirigent_acp_api::router::{AcpServerState, create_acp_server_router};
/// use dirigent_acp_api::{AcpServerConfig, NoOpConnectorOperations};
///
/// let state = AcpServerState::new(AcpServerConfig::enabled());
/// let router = create_acp_server_router(state, NoOpConnectorOperations);
///
/// // Use with axum server
/// let listener = tokio::net::TcpListener::bind("0.0.0.0:3001").await?;
/// axum::serve(listener, router).await?;
/// ```
pub fn create_acp_server_router<C>(state: AcpServerState, connector_ops: C) -> Router
where
    C: ConnectorOperations + Clone + Send + Sync + 'static,
{
    // Build CORS layer based on configuration
    let cors = build_cors_layer(&state.config());

    // Create combined router state
    let router_state = RouterState::new(state, connector_ops);

    // Build the router (T023: added /agent_response route)
    Router::new()
        .route("/rpc", post(handle_rpc::<C>))
        .route("/events", get(handle_sse::<C>))
        .route("/health", get(handle_health::<C>))
        .route("/agent_response", post(handle_agent_response::<C>))
        .layer(cors)
        .with_state(router_state)
}

/// Build CORS layer from configuration
fn build_cors_layer(config: &AcpServerConfig) -> CorsLayer {
    let cors = CorsLayer::new()
        .allow_methods([
            axum::http::Method::GET,
            axum::http::Method::POST,
            axum::http::Method::OPTIONS,
        ])
        .allow_headers(Any);

    match &config.allowed_origins {
        Some(origins) if !origins.is_empty() => {
            // Parse origins and set specific allowed origins
            let parsed_origins: Vec<_> = origins
                .iter()
                .filter_map(|o| o.parse().ok())
                .collect();

            if parsed_origins.is_empty() {
                warn!("No valid origins in allowed_origins, allowing any origin");
                cors.allow_origin(Any)
            } else {
                debug!("CORS configured with {} allowed origins", parsed_origins.len());
                cors.allow_origin(parsed_origins)
            }
        }
        _ => {
            debug!("CORS configured to allow any origin");
            cors.allow_origin(Any)
        }
    }
}

// ============================================================================
// Request/Response Types
// ============================================================================

/// Query parameters for the SSE events endpoint
#[derive(Debug, Deserialize)]
pub struct SseQuery {
    /// The client ID (required for SSE subscription)
    pub client_id: Option<String>,
}

/// Health check response
#[derive(Debug, Serialize, Deserialize)]
pub struct HealthResponse {
    /// Health status
    pub status: String,

    /// Server message
    pub message: String,

    /// Number of connected clients
    pub clients: usize,

    /// Number of active sessions
    pub sessions: usize,
}

/// Error response for SSE endpoint
#[derive(Debug, Serialize, Deserialize)]
pub struct SseErrorResponse {
    /// Error message
    pub error: String,

    /// Error code
    pub code: String,
}

// ============================================================================
// Endpoint Handlers (T029, T030)
// ============================================================================

/// Handle POST /rpc requests (T029)
///
/// Extracts the JSON body, processes it through the RPC handler, and returns
/// the JSON-RPC response.
async fn handle_rpc<C>(
    State(router_state): State<RouterState<C>>,
    headers: HeaderMap,
    body: String,
) -> impl IntoResponse
where
    C: ConnectorOperations + Clone + Send + Sync + 'static,
{
    debug!("Received RPC request: {} bytes", body.len());

    // Extract client_id from X-Client-ID header
    let client_id = headers
        .get("X-Client-ID")
        .and_then(|v| v.to_str().ok());

    // Extract select_connector from X-Select-Connector header (sent during initialize)
    let select_connector = headers
        .get("X-Select-Connector")
        .and_then(|v| v.to_str().ok())
        .map(|s| s.to_string());

    if let Some(id) = client_id {
        info!(client_id = %id, "Received X-Client-ID header from RPC request");
        debug!("RPC headers: {:?}", headers);
    } else {
        warn!("No X-Client-ID header provided in RPC request");
        debug!("Available headers: {:?}", headers.keys().collect::<Vec<_>>());
    }

    // Log the incoming request
    debug!("Received RPC request body: {}", serde_json::to_string(&body).unwrap_or_else(|_| "failed to serialize".to_string()));

    // Create an RPC handler for this request
    let handler = RpcHandler::new(
        router_state.state.session_manager().clone(),
        router_state.connector_ops.clone(),
        router_state.state.sse_notifier().clone(),
        router_state.state.agent_request_tracker().clone(),
    );

    // Process the request with the client_id from header
    let response = handler.handle_request(&body, client_id).await;

    // If this was an initialize request with X-Select-Connector header,
    // store the preference after the client is registered
    if let Some(ref connector) = select_connector {
        // Check if this is an initialize response by looking for clientId in result
        if let crate::jsonrpc::JsonRpcResponseBatch::Single(ref resp) = response {
            if let Some(ref result) = resp.result {
                if let Some(new_client_id) = result.get("clientId").and_then(|v| v.as_str()) {
                    router_state.state.session_manager().update_client_preferred_connector(
                        new_client_id,
                        Some(connector.clone()),
                    );
                    info!(
                        client_id = %new_client_id,
                        select_connector = %connector,
                        "Stored preferred connector from X-Select-Connector header"
                    );
                }
            }
        }
    }

    // Log the response being sent
    debug!("Sending RPC response: {}", serde_json::to_string(&response).unwrap_or_else(|_| "failed to serialize".to_string()));

    // Return JSON response
    Json(response)
}

/// Handle GET /events SSE requests (T030)
///
/// Creates an SSE stream subscription for the client. The client_id must be
/// provided as a query parameter.
async fn handle_sse<C>(
    State(router_state): State<RouterState<C>>,
    Query(query): Query<SseQuery>,
) -> Result<Sse<impl tokio_stream::Stream<Item = Result<Event, Infallible>>>, (StatusCode, Json<SseErrorResponse>)>
where
    C: ConnectorOperations + Clone + Send + Sync + 'static,
{
    // Validate client_id
    let client_id = match query.client_id {
        Some(id) if !id.is_empty() => id,
        _ => {
            warn!("SSE request missing client_id");
            return Err((
                StatusCode::BAD_REQUEST,
                Json(SseErrorResponse {
                    error: "Missing required parameter: client_id".to_string(),
                    code: "MISSING_CLIENT_ID".to_string(),
                }),
            ));
        }
    };

    info!("SSE subscription requested for client: {}", client_id);
    debug!(
        "SSE notifier state before subscribe: client_count={}, subscribed_clients={:?}",
        router_state.state.sse_notifier().client_count(),
        router_state.state.sse_notifier().subscribed_clients()
    );

    // Subscribe to notifications
    let notifier = router_state.state.sse_notifier();
    let notification_stream = notifier.subscribe(&client_id);

    info!(
        "SSE client subscribed: client_id={}, total_clients={}, is_subscribed={}",
        client_id,
        notifier.client_count(),
        notifier.is_subscribed(&client_id)
    );

    // Check if this client has any sessions that need tool updates
    // This handles the race condition where session/new happens before SSE subscription
    let session_manager = router_state.state.session_manager();
    let client_sessions = session_manager.list_client_sessions(&client_id);

    if !client_sessions.is_empty() {
        debug!(
            "Client {} has {} existing sessions, sending tool updates",
            client_id,
            client_sessions.len()
        );

        // For each session, get the connector and send tool updates
        for session_id in client_sessions {
            if let Some(mapping) = session_manager.get_mapping(&session_id) {
                debug!(
                    "Fetching tool updates for session {} on connector {}",
                    session_id,
                    mapping.connector_id
                );

                // Get available commands from the connector
                match router_state.connector_ops.get_connector_commands(&mapping.connector_id).await {
                    Ok(commands) => {
                        let update_params = crate::sse::SessionUpdateParams {
                            session_id: session_id.clone(),
                            update: crate::sse::SessionUpdateVariant::AvailableCommandsUpdate {
                                available_commands: commands.clone(),
                            },
                            event_type_override: None,
                        };

                        // Broadcast the tool update now that client is subscribed
                        match notifier.broadcast(&client_id, update_params) {
                            Ok(n) => {
                                info!(
                                    "Sent deferred available_commands_update for session {}: {} commands to {} receivers",
                                    session_id,
                                    commands.len(),
                                    n
                                );
                            }
                            Err(_) => {
                                warn!(
                                    "Failed to send deferred available_commands_update for session {}",
                                    session_id
                                );
                            }
                        }
                    }
                    Err(e) => {
                        warn!(
                            "Failed to get connector commands for session {}: {}",
                            session_id,
                            e
                        );
                    }
                }
            }
        }
    }

    // Map the notification stream to SSE events
    let client_id_for_log = client_id.clone();
    let sse_stream = notification_stream.map(move |result| {
        match result {
            Ok(notification) => {
                // T013: Start timing for SSE event creation
                let event_start = Instant::now();

                // Convert notification to SSE event
                // Use to_sse_json() which handles raw events correctly
                let event_type = notification.event_type();
                let data = notification.to_sse_json();

                // T011: Log before SSE event is written (T023: includes session_id for correlation)
                trace!(
                    client_id = %client_id_for_log,
                    event_type = %event_type,
                    session_id = %notification.session_id,
                    data_len = data.len(),
                    "Writing SSE event to client stream"
                );

                debug!(
                    "SSE: Sending event to client {}: type={}, session_id={}, data_len={}",
                    client_id_for_log,
                    event_type,
                    notification.session_id,
                    data.len()
                );
                trace!("SSE event data: {}", data);

                let event = Event::default()
                    .event(event_type)
                    .data(data);

                // T012: Log after Event::default() construction (T023: includes session_id for correlation)
                trace!(
                    client_id = %client_id_for_log,
                    event_type = %event_type,
                    session_id = %notification.session_id,
                    "SSE event constructed, sending to client"
                );

                // T013: Log SSE event write completion with timing (T023: includes session_id for correlation)
                let elapsed_ms = event_start.elapsed().as_millis();
                trace!(
                    client_id = %client_id_for_log,
                    event_type = %event_type,
                    session_id = %notification.session_id,
                    elapsed_ms = elapsed_ms,
                    "SSE event write completed"
                );

                // T014: Warn for slow SSE event writes (T023: includes session_id for correlation)
                if elapsed_ms > 100 {
                    warn!(
                        elapsed_ms = elapsed_ms,
                        client_id = %client_id_for_log,
                        event_type = %event_type,
                        session_id = %notification.session_id,
                        "Slow SSE event write detected"
                    );
                }

                Ok(event)
            }
            Err(e) => {
                // Broadcast stream error (e.g., lagged receiver)
                // We send an error event but keep the stream open
                warn!("SSE stream error for client {}: {:?}", client_id_for_log, e);
                Ok(Event::default()
                    .event("error")
                    .data(format!(r#"{{"error":"Stream error: {:?}"}}"#, e)))
            }
        }
    });

    // Return SSE response with keep-alive
    Ok(Sse::new(sse_stream).keep_alive(KeepAlive::default()))
}

/// Handle GET /health requests
///
/// Returns basic health information about the server.
async fn handle_health<C>(
    State(router_state): State<RouterState<C>>,
) -> Json<HealthResponse>
where
    C: ConnectorOperations + Clone + Send + Sync + 'static,
{
    Json(HealthResponse {
        status: "healthy".to_string(),
        message: "Dirigent ACP Server is running".to_string(),
        clients: router_state.state.sse_notifier().client_count(),
        sessions: router_state.state.session_manager().mapping_count(),
    })
}

/// Response for agent response endpoint
#[derive(Debug, Serialize, Deserialize)]
pub struct AgentResponseResult {
    /// Status of the response
    pub status: String,
}

/// Handle POST /agent_response requests (T019)
///
/// Accepts JSON-RPC responses from clients for pending agent requests.
/// When a client approves/denies a permission request, they POST the
/// response to this endpoint, which completes the pending request.
/// (Note: When nested at /acp, the full path becomes /acp/agent_response)
///
/// Expected body format:
/// ```json
/// {
///   "jsonrpc": "2.0",
///   "id": 0,
///   "result": { "selectedOptionId": "allow" }
/// }
/// ```
///
/// The response delivery to the connector happens through the oneshot channel
/// registered in the event bridge (Phase 2.4). The tracker's `complete()` method
/// sends the response value through the oneshot channel, and the event bridge task
/// (which registered the request) will receive it and send the `ConnectorCommand::AgentResponse`.
async fn handle_agent_response<C>(
    State(router_state): State<RouterState<C>>,
    headers: HeaderMap,
    Json(response): Json<serde_json::Value>,
) -> Result<Json<AgentResponseResult>, (StatusCode, String)>
where
    C: ConnectorOperations + Clone + Send + Sync + 'static,
{
    // Extract client_id from X-Client-ID header (T020)
    let client_id = match headers.get("X-Client-ID").and_then(|v| v.to_str().ok()) {
        Some(id) if !id.is_empty() => id,
        _ => {
            warn!("Agent response request missing X-Client-ID header");
            return Err((
                StatusCode::BAD_REQUEST,
                "Missing required header: X-Client-ID".to_string(),
            ));
        }
    };

    debug!(
        "Received agent response from client: {}",
        client_id
    );
    trace!("Agent response body: {}", response);

    // Extract request_id from JSON body (T020)
    let request_id = match response.get("id") {
        Some(id) => id.clone(),
        None => {
            warn!(
                client_id = %client_id,
                "Agent response missing 'id' field in body"
            );
            return Err((
                StatusCode::BAD_REQUEST,
                "Missing required field: id".to_string(),
            ));
        }
    };

    info!(
        client_id = %client_id,
        request_id = %request_id,
        "Processing agent response"
    );

    // Call AgentRequestTracker::complete() (T021)
    let tracker = router_state.state.agent_request_tracker();
    match tracker.complete(client_id, request_id.clone(), response.clone()) {
        Ok(()) => {
            info!(
                client_id = %client_id,
                request_id = %request_id,
                "Agent response delivered successfully"
            );

            // Note (T022): The response delivery to the connector happens through
            // the oneshot channel in the event bridge. When the event bridge handles
            // an `Event::AgentRequest`, it registers the request with the tracker and
            // gets a receiver. After we complete the request here, the receiver in the
            // event bridge gets the response value and sends the `ConnectorCommand::AgentResponse`
            // to the connector. This flow is implemented in Phase 2.4 (T024-T030).

            Ok(Json(AgentResponseResult {
                status: "ok".to_string(),
            }))
        }
        Err(e) => {
            warn!(
                client_id = %client_id,
                request_id = %request_id,
                error = %e,
                "Agent response failed: request not found (may have timed out)"
            );

            Err((
                StatusCode::NOT_FOUND,
                format!("Request ID {} not found or already completed", request_id),
            ))
        }
    }
}

// ============================================================================
// Tests
// ============================================================================

#[cfg(test)]
mod tests {
    use super::*;
    use crate::jsonrpc::JsonRpcResponseBatch;
    use crate::rpc::NoOpConnectorOperations;
    use axum::{
        body::Body,
        http::{Request, StatusCode},
    };
    use tower::ServiceExt;

    fn create_test_router() -> Router {
        let state = AcpServerState::new(AcpServerConfig::enabled());
        create_acp_server_router(state, NoOpConnectorOperations)
    }

    #[tokio::test]
    async fn test_health_endpoint() {
        let router = create_test_router();

        let request = Request::builder()
            .uri("/health")
            .body(Body::empty())
            .unwrap();

        let response = router.oneshot(request).await.unwrap();

        assert_eq!(response.status(), StatusCode::OK);

        let body = axum::body::to_bytes(response.into_body(), usize::MAX)
            .await
            .unwrap();
        let health: HealthResponse = serde_json::from_slice(&body).unwrap();

        assert_eq!(health.status, "healthy");
        assert_eq!(health.clients, 0);
        assert_eq!(health.sessions, 0);
    }

    #[tokio::test]
    async fn test_rpc_initialize() {
        let router = create_test_router();

        let request_body = r#"{"jsonrpc":"2.0","method":"initialize","id":1}"#;

        let request = Request::builder()
            .method("POST")
            .uri("/rpc")
            .header("content-type", "application/json")
            .body(Body::from(request_body))
            .unwrap();

        let response = router.oneshot(request).await.unwrap();

        assert_eq!(response.status(), StatusCode::OK);

        let body = axum::body::to_bytes(response.into_body(), usize::MAX)
            .await
            .unwrap();
        let resp: JsonRpcResponseBatch = serde_json::from_slice(&body).unwrap();

        match resp {
            JsonRpcResponseBatch::Single(r) => {
                assert!(r.is_success());
                let result = r.result.unwrap();
                assert_eq!(result["agentInfo"]["name"], "dirigent-acp-server");
            }
            _ => panic!("Expected single response"),
        }
    }

    #[tokio::test]
    async fn test_rpc_invalid_json() {
        let router = create_test_router();

        let request = Request::builder()
            .method("POST")
            .uri("/rpc")
            .header("content-type", "application/json")
            .body(Body::from("not valid json"))
            .unwrap();

        let response = router.oneshot(request).await.unwrap();

        assert_eq!(response.status(), StatusCode::OK);

        let body = axum::body::to_bytes(response.into_body(), usize::MAX)
            .await
            .unwrap();
        let resp: JsonRpcResponseBatch = serde_json::from_slice(&body).unwrap();

        match resp {
            JsonRpcResponseBatch::Single(r) => {
                assert!(r.is_error());
                let error = r.error.unwrap();
                assert_eq!(error.code, crate::error::error_codes::PARSE_ERROR);
            }
            _ => panic!("Expected single response"),
        }
    }

    #[tokio::test]
    async fn test_sse_missing_client_id() {
        let router = create_test_router();

        let request = Request::builder()
            .uri("/events")
            .body(Body::empty())
            .unwrap();

        let response = router.oneshot(request).await.unwrap();

        assert_eq!(response.status(), StatusCode::BAD_REQUEST);

        let body = axum::body::to_bytes(response.into_body(), usize::MAX)
            .await
            .unwrap();
        let error: SseErrorResponse = serde_json::from_slice(&body).unwrap();

        assert_eq!(error.code, "MISSING_CLIENT_ID");
    }

    #[tokio::test]
    async fn test_sse_with_client_id() {
        let router = create_test_router();

        let request = Request::builder()
            .uri("/events?client_id=test-client")
            .body(Body::empty())
            .unwrap();

        let response = router.oneshot(request).await.unwrap();

        // Should return 200 OK with SSE content type
        assert_eq!(response.status(), StatusCode::OK);

        let content_type = response
            .headers()
            .get("content-type")
            .and_then(|v| v.to_str().ok());

        assert!(content_type.is_some());
        assert!(content_type.unwrap().contains("text/event-stream"));
    }

    #[test]
    fn test_acp_server_state_new() {
        let state = AcpServerState::new(AcpServerConfig::enabled());

        assert!(state.config().enabled);
        assert_eq!(state.session_manager().mapping_count(), 0);
        assert_eq!(state.sse_notifier().client_count(), 0);
    }

    #[test]
    fn test_acp_server_state_with_components() {
        let session_manager = SessionManager::new();
        let sse_notifier = SseNotifier::new();
        let config = AcpServerConfig::enabled().set_port(4000);

        // Pre-populate some state
        session_manager.register_client(None);

        let agent_request_tracker = Arc::new(crate::agent_requests::AgentRequestTracker::new());

        let state = AcpServerState::with_components(
            session_manager,
            sse_notifier,
            agent_request_tracker,
            config,
        );

        assert!(state.config().enabled);
        assert_eq!(state.config().port, 4000);
        assert_eq!(state.session_manager().client_count(), 1);
    }

    #[test]
    fn test_acp_server_state_clone() {
        let state1 = AcpServerState::new(AcpServerConfig::enabled());
        let state2 = state1.clone();

        // Both should share the same underlying state
        let client_id = state1.session_manager().register_client(None);
        assert_eq!(state2.session_manager().client_count(), 1);
        assert!(state2.session_manager().get_client(&client_id).is_some());
    }

    #[test]
    fn test_acp_server_state_debug() {
        let state = AcpServerState::new(AcpServerConfig::enabled());
        let debug_str = format!("{:?}", state);

        assert!(debug_str.contains("AcpServerState"));
        assert!(debug_str.contains("session_count"));
        assert!(debug_str.contains("client_count"));
    }

    #[test]
    fn test_build_cors_layer_any_origin() {
        let config = AcpServerConfig::default();
        let _cors = build_cors_layer(&config);
        // No assertion needed - just verify it doesn't panic
    }

    #[test]
    fn test_build_cors_layer_specific_origins() {
        let config = AcpServerConfig::default()
            .set_allowed_origins(Some(vec![
                "http://localhost:3000".to_string(),
                "https://app.example.com".to_string(),
            ]));
        let _cors = build_cors_layer(&config);
        // No assertion needed - just verify it doesn't panic
    }

    #[test]
    fn test_build_cors_layer_empty_origins() {
        let config = AcpServerConfig::default()
            .set_allowed_origins(Some(vec![]));
        let _cors = build_cors_layer(&config);
        // Should fall back to any origin
    }
}
