//! ACP Transport Layer
//!
//! This module provides transport abstractions for the Agent-Client Protocol (ACP).
//! Transports handle the low-level communication with ACP agents via different
//! mechanisms (stdio, HTTP/SSE, WebSocket).
//!
//! # Architecture
//!
//! The `AcpTransport` trait defines a standard interface for sending and receiving
//! JSON-RPC messages. Concrete implementations handle the protocol-specific details:
//!
//! - **StdioTransport**: Line-delimited JSON over stdin/stdout (for process spawning)
//! - **HttpTransport**: HTTP POST for requests + SSE for notifications
//!
//! # Usage
//!
//! ```no_run
//! use dirigent_core::connectors::acp::transport::{AcpTransport, StdioTransport};
//! use serde_json::json;
//!
//! # async fn example() -> anyhow::Result<()> {
//! // Create and connect a transport
//! let mut transport = StdioTransport::new("dirigate", &["serve", "--stdio"]);
//! transport.connect().await?;
//!
//! // Send a request
//! let request = json!({
//!     "jsonrpc": "2.0",
//!     "method": "initialize",
//!     "id": 1,
//!     "params": {
//!         "protocolVersion": 1,
//!         "clientCapabilities": {}
//!     }
//! });
//! transport.send(request).await?;
//!
//! // Receive response
//! if let Some(response) = transport.recv().await? {
//!     println!("Response: {:?}", response);
//! }
//!
//! // Clean up
//! transport.close().await?;
//! # Ok(())
//! # }
//! ```

use async_trait::async_trait;
use serde_json::Value;
use std::error::Error;

pub mod http;
pub mod stdio;

pub use http::HttpTransport;
pub use stdio::CrashContext;
pub use stdio::StdioTransport;

/// Result type for transport operations
pub type TransportResult<T> = Result<T, Box<dyn Error + Send + Sync>>;

/// Transport abstraction for ACP communication
///
/// This trait defines the interface for sending and receiving JSON-RPC messages
/// over various transport mechanisms. Implementations must be thread-safe (Send + Sync)
/// to allow concurrent operations.
///
/// # Object Safety
///
/// This trait is object-safe and can be used as `Box<dyn AcpTransport>` for
/// dynamic dispatch when the concrete transport type is not known at compile time.
///
/// # Message Format
///
/// All messages are JSON-RPC 2.0 format, represented as `serde_json::Value`.
/// The transport layer does not interpret message semantics - it only handles
/// serialization and transmission.
///
/// # Lifecycle
///
/// 1. Create the transport instance with configuration
/// 2. Call `connect()` to establish the connection
/// 3. Use `send()` and `recv()` for bidirectional communication
/// 4. Call `close()` to clean up resources gracefully
#[async_trait]
pub trait AcpTransport: Send + Sync {
    /// Establish the connection to the ACP agent
    ///
    /// This method must be called before any `send()` or `recv()` operations.
    /// For stdio transports, this spawns the process. For network transports,
    /// this establishes the TCP/HTTP connection.
    ///
    /// # Errors
    ///
    /// Returns an error if:
    /// - The connection cannot be established (network unreachable, process failed to start)
    /// - The transport is already connected
    /// - Required resources are unavailable
    ///
    /// # Example
    ///
    /// ```no_run
    /// # use dirigent_core::connectors::acp::transport::{AcpTransport, StdioTransport};
    /// # async fn example() -> anyhow::Result<()> {
    /// let mut transport = StdioTransport::new("my-agent", &[]);
    /// transport.connect().await?;
    /// # Ok(())
    /// # }
    /// ```
    async fn connect(&mut self) -> TransportResult<()>;

    /// Send a JSON-RPC message to the agent
    ///
    /// The message is serialized and transmitted according to the transport's
    /// protocol (line-delimited JSON for stdio, HTTP POST for HTTP transport).
    ///
    /// # Arguments
    ///
    /// * `message` - JSON-RPC 2.0 message (request or response)
    ///
    /// # Errors
    ///
    /// Returns an error if:
    /// - The transport is not connected
    /// - The message cannot be serialized
    /// - The underlying I/O operation fails
    ///
    /// # Example
    ///
    /// ```no_run
    /// # use dirigent_core::connectors::acp::transport::{AcpTransport, StdioTransport};
    /// # use serde_json::json;
    /// # async fn example(mut transport: StdioTransport) -> anyhow::Result<()> {
    /// let request = json!({
    ///     "jsonrpc": "2.0",
    ///     "method": "session/new",
    ///     "id": 1,
    ///     "params": {}
    /// });
    /// transport.send(request).await?;
    /// # Ok(())
    /// # }
    /// ```
    async fn send(&mut self, message: Value) -> TransportResult<()>;

    /// Receive a JSON-RPC message from the agent
    ///
    /// This method blocks until a message is available or the connection is closed.
    /// Returns `None` if the connection is closed gracefully (EOF on stdio, SSE
    /// stream ended, etc.).
    ///
    /// # Returns
    ///
    /// - `Ok(Some(message))` - A message was received
    /// - `Ok(None)` - The connection was closed gracefully (no more messages)
    /// - `Err(e)` - An error occurred during receive
    ///
    /// # Errors
    ///
    /// Returns an error if:
    /// - The transport is not connected
    /// - The received data cannot be parsed as JSON
    /// - The underlying I/O operation fails
    ///
    /// # Example
    ///
    /// ```no_run
    /// # use dirigent_core::connectors::acp::transport::{AcpTransport, StdioTransport};
    /// # async fn example(mut transport: StdioTransport) -> anyhow::Result<()> {
    /// while let Some(message) = transport.recv().await? {
    ///     println!("Received: {:?}", message);
    /// }
    /// # Ok(())
    /// # }
    /// ```
    async fn recv(&mut self) -> TransportResult<Option<Value>>;

    /// Close the connection gracefully
    ///
    /// This method cleans up resources and closes the connection. After calling
    /// `close()`, no further `send()` or `recv()` operations are allowed.
    ///
    /// For stdio transports, this terminates the child process. For network
    /// transports, this closes the socket/connection.
    ///
    /// # Errors
    ///
    /// Returns an error if the cleanup operation fails. This is typically
    /// non-fatal and can be ignored in most cases.
    ///
    /// # Example
    ///
    /// ```no_run
    /// # use dirigent_core::connectors::acp::transport::{AcpTransport, StdioTransport};
    /// # async fn example(mut transport: StdioTransport) -> anyhow::Result<()> {
    /// // ... use transport ...
    /// transport.close().await?;
    /// # Ok(())
    /// # }
    /// ```
    async fn close(&mut self) -> TransportResult<()>;

    /// Get crash context if the transport detected a child process crash.
    ///
    /// Only meaningful for transports that manage child processes (e.g., StdioTransport).
    /// Returns None by default.
    async fn get_crash_context(&self) -> Option<CrashContext> {
        None
    }

    /// Get the process ID, if applicable (e.g., for stdio transports that spawn a child process).
    /// Returns None for transports that don't manage OS processes.
    async fn pid(&self) -> Option<u32> {
        None
    }
}
