//! TEST-002: Comprehensive HTTP Integration Tests
//!
//! This file contains comprehensive integration tests for the ACP client over HTTP transport.
//! It tests the full lifecycle of ACP communication using the dirigate HTTP server.
//!
//! Test Coverage:
//! - T009: Full HTTP lifecycle (initialize, new session, prompt, cancel)
//! - T010: HTTP initialize handshake
//! - T011: HTTP session creation
//! - T012: HTTP message sending with SSE streaming
//! - T013: HTTP session cancellation
//! - T014: SSE connection and events
//! - T015: Multiple concurrent HTTP sessions
//! - T016: HTTP connection recovery

#![cfg(feature = "server")]

use dirigent_core::connectors::acp::{AcpConfig, AcpConnector};
use dirigent_core::connectors::{Connector, ConnectorCommand};
use dirigent_protocol::Event;
use std::path::PathBuf;
use std::time::Duration;
use tokio::time::timeout;

/// Helper to get test port (unique per test to avoid conflicts)
fn test_port(offset: u16) -> u16 {
    9000 + offset
}

/// Helper to create a test fixture path
fn test_fixture_path() -> PathBuf {
    PathBuf::from(env!("CARGO_MANIFEST_DIR"))
        .join("tests")
        .join("fixtures")
        .join("acp_test.yaml")
}

/// Helper to spawn the mocker HTTP server in the background
async fn spawn_mocker_server(port: u16) -> anyhow::Result<tokio::task::JoinHandle<()>> {
    let fixture_path = test_fixture_path();

    // Build the mocker
    tracing::info!("Building dirigate...");
    let build_status = tokio::process::Command::new("cargo")
        .args(&["build", "--package", "dirigate"])
        .status()
        .await?;

    if !build_status.success() {
        anyhow::bail!("Failed to build dirigate");
    }

    // Find the binary
    let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
    let project_root = manifest_dir.parent().unwrap().parent().unwrap();
    let binary_path = project_root
        .join("target")
        .join("debug")
        .join("dirigate.exe");

    if !binary_path.exists() {
        anyhow::bail!("Mocker binary not found at {:?}", binary_path);
    }

    // Spawn the server
    tracing::info!("Starting mocker server on port {}", port);
    let handle = tokio::spawn(async move {
        let _ = tokio::process::Command::new(&binary_path)
            .args(&[
                "serve",
                "--fixtures",
                fixture_path.to_str().unwrap(),
                "--port",
                &port.to_string(),
            ])
            .status()
            .await;
    });

    // Wait for server to be ready
    tokio::time::sleep(Duration::from_secs(2)).await;

    Ok(handle)
}

/// Helper to create a test ACP connector with HTTP transport
fn create_http_connector(port: u16) -> AcpConnector {
    let config = AcpConfig::http(format!("http://127.0.0.1:{}", port))
        .with_retry_max_attempts(3)
        .with_retry_initial_delay(Duration::from_millis(500));

    AcpConnector::new(
        format!("http-test-connector-{}", port),
        uuid::Uuid::nil(),
        "HTTP Test Connector".to_string(),
        config,
        dirigent_core::sharing::bus::SharingBus::new(),
    )
    .expect("Failed to create connector")
}

/// Helper to wait for event with timeout
async fn wait_for_event<F>(
    events_rx: &mut tokio::sync::broadcast::Receiver<Event>,
    predicate: F,
    timeout_duration: Duration,
) -> anyhow::Result<Event>
where
    F: Fn(&Event) -> bool,
{
    let result = timeout(timeout_duration, async {
        loop {
            match events_rx.recv().await {
                Ok(event) => {
                    if predicate(&event) {
                        return Ok(event);
                    }
                }
                Err(e) => {
                    return Err(anyhow::anyhow!("Event receive error: {}", e));
                }
            }
        }
    })
    .await;

    match result {
        Ok(Ok(event)) => Ok(event),
        Ok(Err(e)) => Err(e),
        Err(_) => Err(anyhow::anyhow!("Timeout waiting for event")),
    }
}

// ============================================================================
// T009: Full HTTP Lifecycle Test
// ============================================================================

#[tokio::test]
#[ignore = "Requires building mocker and port availability"]
async fn test_t009_full_lifecycle_http() -> anyhow::Result<()> {
    tracing_subscriber::fmt()
        .with_env_filter("debug")
        .with_test_writer()
        .try_init()
        .ok();

    let port = test_port(1);
    let _server_handle = spawn_mocker_server(port).await?;

    // Create connector
    let connector = create_http_connector(port);
    let mut events = connector.subscribe();

    // Start the connector task
    let task_handle = connector.start_task().await;

    // Wait for connection
    let connected_event = wait_for_event(
        &mut events,
        |e| matches!(e, Event::Connected),
        Duration::from_secs(10),
    )
    .await?;

    assert!(matches!(connected_event, Event::Connected));
    tracing::info!("✓ Connected to HTTP mocker");

    // Create a new session
    let cmd_tx = connector.command_tx();
    cmd_tx
        .send(ConnectorCommand::CreateSession {
            cwd: None,
            project_id: None,
            ownership: dirigent_protocol::SessionOwnership::internal(),
        })
        .await?;

    let session_created = wait_for_event(
        &mut events,
        |e| matches!(e, Event::SessionCreated { .. }),
        Duration::from_secs(5),
    )
    .await?;

    let session_id = match session_created {
        Event::SessionCreated { session, .. } => {
            tracing::info!("✓ Session created: {}", session.id);
            session.id
        }
        _ => anyhow::bail!("Expected SessionCreated event"),
    };

    // Send a message
    cmd_tx
        .send(ConnectorCommand::SendMessage {
            session_id: session_id.clone(),
            text: "Hello via HTTP!".to_string(),
        })
        .await?;

    // Wait for message started
    wait_for_event(
        &mut events,
        |e| matches!(e, Event::MessageStarted { .. }),
        Duration::from_secs(5),
    )
    .await?;

    tracing::info!("✓ Message started");

    // Wait for some content
    let mut received_content = false;
    for _ in 0..10 {
        if let Ok(event) = timeout(Duration::from_secs(2), events.recv()).await {
            match event? {
                Event::SessionUpdate { .. } => {
                    received_content = true;
                    tracing::info!("✓ Received content via SSE");
                    break;
                }
                Event::MessageCompleted { .. } => {
                    received_content = true;
                    break;
                }
                _ => {}
            }
        }
    }

    assert!(received_content, "Should receive content via SSE");

    // Stop the connector
    connector.stop();
    let _ = timeout(Duration::from_secs(5), task_handle).await?;

    tracing::info!("✓ Full HTTP lifecycle completed successfully");
    Ok(())
}

// ============================================================================
// T010: HTTP Initialize Handshake
// ============================================================================

#[tokio::test]
#[ignore = "Requires building mocker and port availability"]
async fn test_t010_http_initialize() -> anyhow::Result<()> {
    tracing_subscriber::fmt()
        .with_env_filter("debug")
        .with_test_writer()
        .try_init()
        .ok();

    let port = test_port(2);
    let _server_handle = spawn_mocker_server(port).await?;

    let connector = create_http_connector(port);
    let mut events = connector.subscribe();
    let task_handle = connector.start_task().await;

    // Wait for connected event
    let connected = wait_for_event(
        &mut events,
        |e| matches!(e, Event::Connected),
        Duration::from_secs(10),
    )
    .await?;

    assert!(matches!(connected, Event::Connected));
    tracing::info!("✓ HTTP initialize handshake completed");

    // Cleanup
    connector.stop();
    let _ = timeout(Duration::from_secs(5), task_handle).await?;

    Ok(())
}

// ============================================================================
// T011: HTTP Session Creation
// ============================================================================

#[tokio::test]
#[ignore = "Requires building mocker and port availability"]
async fn test_t011_http_session_creation() -> anyhow::Result<()> {
    tracing_subscriber::fmt()
        .with_env_filter("debug")
        .with_test_writer()
        .try_init()
        .ok();

    let port = test_port(3);
    let _server_handle = spawn_mocker_server(port).await?;

    let connector = create_http_connector(port);
    let mut events = connector.subscribe();
    let task_handle = connector.start_task().await;

    // Wait for connection
    wait_for_event(
        &mut events,
        |e| matches!(e, Event::Connected),
        Duration::from_secs(10),
    )
    .await?;

    // Create session
    let cmd_tx = connector.command_tx();
    cmd_tx
        .send(ConnectorCommand::CreateSession {
            cwd: None,
            project_id: None,
            ownership: dirigent_protocol::SessionOwnership::internal(),
        })
        .await?;

    // Wait for session created event
    let session_created = wait_for_event(
        &mut events,
        |e| matches!(e, Event::SessionCreated { .. }),
        Duration::from_secs(5),
    )
    .await?;

    match session_created {
        Event::SessionCreated { session, .. } => {
            assert_eq!(session.title, "HTTP Session Test");
            tracing::info!("✓ HTTP session created with correct title");
        }
        _ => anyhow::bail!("Expected SessionCreated event"),
    }

    // Cleanup
    connector.stop();
    let _ = timeout(Duration::from_secs(5), task_handle).await?;

    Ok(())
}

// ============================================================================
// T012: HTTP Message Sending with SSE Streaming
// ============================================================================

#[tokio::test]
#[ignore = "Requires building mocker and port availability"]
async fn test_t012_http_sse_streaming() -> anyhow::Result<()> {
    tracing_subscriber::fmt()
        .with_env_filter("debug")
        .with_test_writer()
        .try_init()
        .ok();

    let port = test_port(4);
    let _server_handle = spawn_mocker_server(port).await?;

    let connector = create_http_connector(port);
    let mut events = connector.subscribe();
    let task_handle = connector.start_task().await;

    // Setup
    wait_for_event(
        &mut events,
        |e| matches!(e, Event::Connected),
        Duration::from_secs(10),
    )
    .await?;

    let cmd_tx = connector.command_tx();
    cmd_tx
        .send(ConnectorCommand::CreateSession {
            cwd: None,
            project_id: None,
            ownership: dirigent_protocol::SessionOwnership::internal(),
        })
        .await?;

    let session_id = match wait_for_event(
        &mut events,
        |e| matches!(e, Event::SessionCreated { .. }),
        Duration::from_secs(5),
    )
    .await?
    {
        Event::SessionCreated { session, .. } => session.id,
        _ => anyhow::bail!("Expected SessionCreated"),
    };

    // Send message
    cmd_tx
        .send(ConnectorCommand::SendMessage {
            session_id: session_id.clone(),
            text: "Stream me some content".to_string(),
        })
        .await?;

    // Wait for message started
    wait_for_event(
        &mut events,
        |e| matches!(e, Event::MessageStarted { .. }),
        Duration::from_secs(5),
    )
    .await?;

    // Collect SSE chunks
    let mut chunk_count = 0;
    let mut completed = false;

    for _ in 0..20 {
        if let Ok(result) = timeout(Duration::from_secs(2), events.recv()).await {
            match result? {
                Event::SessionUpdate { update, .. } => {
                    if let dirigent_protocol::SessionUpdate::AgentMessageChunk { .. } = update {
                        chunk_count += 1;
                        tracing::info!("Received SSE chunk {}", chunk_count);
                    }
                }
                Event::MessageCompleted { .. } => {
                    completed = true;
                    tracing::info!("✓ Message completed via SSE after {} chunks", chunk_count);
                    break;
                }
                _ => {}
            }
        }
    }

    assert!(chunk_count > 0, "Should receive SSE chunks");
    assert!(completed, "Message should complete via SSE");

    // Cleanup
    connector.stop();
    let _ = timeout(Duration::from_secs(5), task_handle).await?;

    Ok(())
}

// ============================================================================
// T013: HTTP Session Cancellation
// ============================================================================

#[tokio::test]
#[ignore = "Requires building mocker and port availability"]
async fn test_t013_http_cancellation() -> anyhow::Result<()> {
    tracing_subscriber::fmt()
        .with_env_filter("debug")
        .with_test_writer()
        .try_init()
        .ok();

    let port = test_port(5);
    let _server_handle = spawn_mocker_server(port).await?;

    let connector = create_http_connector(port);
    let mut events = connector.subscribe();
    let task_handle = connector.start_task().await;

    // Setup
    wait_for_event(
        &mut events,
        |e| matches!(e, Event::Connected),
        Duration::from_secs(10),
    )
    .await?;

    let cmd_tx = connector.command_tx();
    cmd_tx
        .send(ConnectorCommand::CreateSession {
            cwd: None,
            project_id: None,
            ownership: dirigent_protocol::SessionOwnership::internal(),
        })
        .await?;

    let session_id = match wait_for_event(
        &mut events,
        |e| matches!(e, Event::SessionCreated { .. }),
        Duration::from_secs(5),
    )
    .await?
    {
        Event::SessionCreated { session, .. } => session.id,
        _ => anyhow::bail!("Expected SessionCreated"),
    };

    // Send message
    cmd_tx
        .send(ConnectorCommand::SendMessage {
            session_id: session_id.clone(),
            text: "Long response".to_string(),
        })
        .await?;

    // Wait for streaming to start
    wait_for_event(
        &mut events,
        |e| matches!(e, Event::MessageStarted { .. }),
        Duration::from_secs(5),
    )
    .await?;

    wait_for_event(
        &mut events,
        |e| matches!(e, Event::SessionUpdate { .. }),
        Duration::from_secs(5),
    )
    .await?;

    // Cancel
    cmd_tx
        .send(ConnectorCommand::CancelGeneration {
            session_id: session_id.clone(),
        })
        .await?;

    tracing::info!("✓ HTTP cancellation sent successfully");

    // Cleanup
    connector.stop();
    let _ = timeout(Duration::from_secs(5), task_handle).await?;

    Ok(())
}

// ============================================================================
// T014: SSE Connection and Events
// ============================================================================

#[tokio::test]
#[ignore = "Requires building mocker and port availability"]
async fn test_t014_sse_connection() -> anyhow::Result<()> {
    tracing_subscriber::fmt()
        .with_env_filter("debug")
        .with_test_writer()
        .try_init()
        .ok();

    let port = test_port(6);
    let _server_handle = spawn_mocker_server(port).await?;

    let connector = create_http_connector(port);
    let mut events = connector.subscribe();
    let task_handle = connector.start_task().await;

    // The SSE connection is established during initialization
    wait_for_event(
        &mut events,
        |e| matches!(e, Event::Connected),
        Duration::from_secs(10),
    )
    .await?;

    tracing::info!("✓ SSE connection established");

    // Create a session and send a message to verify SSE delivery
    let cmd_tx = connector.command_tx();
    cmd_tx
        .send(ConnectorCommand::CreateSession {
            cwd: None,
            project_id: None,
            ownership: dirigent_protocol::SessionOwnership::internal(),
        })
        .await?;

    let session_id = match wait_for_event(
        &mut events,
        |e| matches!(e, Event::SessionCreated { .. }),
        Duration::from_secs(5),
    )
    .await?
    {
        Event::SessionCreated { session, .. } => session.id,
        _ => anyhow::bail!("Expected SessionCreated"),
    };

    cmd_tx
        .send(ConnectorCommand::SendMessage {
            session_id,
            text: "Test SSE".to_string(),
        })
        .await?;

    // Verify we receive events via SSE
    let mut received_via_sse = false;
    for _ in 0..10 {
        if let Ok(event) = timeout(Duration::from_secs(1), events.recv()).await {
            match event? {
                Event::SessionUpdate { .. }
                | Event::MessageStarted { .. }
                | Event::MessageCompleted { .. } => {
                    received_via_sse = true;
                    break;
                }
                _ => {}
            }
        }
    }

    assert!(received_via_sse, "Should receive events via SSE");
    tracing::info!("✓ SSE event delivery verified");

    // Cleanup
    connector.stop();
    let _ = timeout(Duration::from_secs(5), task_handle).await?;

    Ok(())
}

// ============================================================================
// T015: Multiple Concurrent HTTP Sessions
// ============================================================================

#[tokio::test]
#[ignore = "Requires building mocker and port availability"]
async fn test_t015_http_multiple_sessions() -> anyhow::Result<()> {
    tracing_subscriber::fmt()
        .with_env_filter("debug")
        .with_test_writer()
        .try_init()
        .ok();

    let port = test_port(7);
    let _server_handle = spawn_mocker_server(port).await?;

    let connector = create_http_connector(port);
    let mut events = connector.subscribe();
    let task_handle = connector.start_task().await;

    wait_for_event(
        &mut events,
        |e| matches!(e, Event::Connected),
        Duration::from_secs(10),
    )
    .await?;

    let cmd_tx = connector.command_tx();
    let mut session_ids = Vec::new();

    // Create multiple sessions via HTTP
    for _i in 0..3 {
        cmd_tx
            .send(ConnectorCommand::CreateSession {
                cwd: None,
                project_id: None,
                ownership: dirigent_protocol::SessionOwnership::internal(),
            })
            .await?;

        let session_id = match wait_for_event(
            &mut events,
            |e| matches!(e, Event::SessionCreated { .. }),
            Duration::from_secs(5),
        )
        .await?
        {
            Event::SessionCreated { session, .. } => session.id,
            _ => anyhow::bail!("Expected SessionCreated"),
        };

        session_ids.push(session_id);
    }

    assert_eq!(session_ids.len(), 3);
    tracing::info!("✓ Created 3 concurrent HTTP sessions");

    // Send messages to all sessions
    for session_id in &session_ids {
        cmd_tx
            .send(ConnectorCommand::SendMessage {
                session_id: session_id.clone(),
                text: "Test".to_string(),
            })
            .await?;
    }

    // Wait for responses
    let mut started_count = 0;
    for _ in 0..30 {
        if let Ok(result) = timeout(Duration::from_secs(1), events.recv()).await {
            if let Event::MessageStarted { .. } = result? {
                started_count += 1;
                if started_count == 3 {
                    break;
                }
            }
        }
    }

    assert_eq!(started_count, 3);
    tracing::info!("✓ All HTTP messages received responses");

    // Cleanup
    connector.stop();
    let _ = timeout(Duration::from_secs(5), task_handle).await?;

    Ok(())
}

// ============================================================================
// T016: HTTP Connection Recovery
// ============================================================================

#[tokio::test]
#[ignore = "Requires building mocker and port availability"]
async fn test_t016_http_connection_recovery() -> anyhow::Result<()> {
    tracing_subscriber::fmt()
        .with_env_filter("debug")
        .with_test_writer()
        .try_init()
        .ok();

    let port = test_port(8);

    // Create connector first (without server running)
    let connector = create_http_connector(port);
    let mut events = connector.subscribe();
    let task_handle = connector.start_task().await;

    // Wait a bit for failed connection attempts
    tokio::time::sleep(Duration::from_secs(2)).await;

    // Now start the server
    tracing::info!("Starting server after connector creation");
    let _server_handle = spawn_mocker_server(port).await?;

    // Should eventually connect (with retry)
    let connected = wait_for_event(
        &mut events,
        |e| matches!(e, Event::Connected),
        Duration::from_secs(15),
    )
    .await?;

    assert!(matches!(connected, Event::Connected));
    tracing::info!("✓ HTTP connector recovered and connected");

    // Cleanup
    connector.stop();
    let _ = timeout(Duration::from_secs(5), task_handle).await?;

    Ok(())
}
