//! Standard input/output transport for MCP
//!
//! Implements line-delimited JSON over stdin/stdout
//!
//! # Bidirectional Communication
//!
//! Stdio transport supports bidirectional communication where both client
//! and server can initiate requests. Messages are distinguished by:
//! - Requests: have `method` field
//! - Responses: have `result` or `error` field (no `method`)

use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader, BufWriter};
use tokio::io::{Stdin, Stdout, stdin, stdout};

use super::traits::{IncomingMessage, JsonRpcNotification, Transport, TransportError, sealed};
use crate::protocol::types::{JsonRpcRequest, JsonRpcResponse};
use crate::server::multiplexer::JsonRpcClientRequest;

/// Stdio transport using line-delimited JSON
pub struct StdioTransport {
    reader: BufReader<Stdin>,
    writer: BufWriter<Stdout>,
}

impl StdioTransport {
    /// Create new stdio transport
    pub fn new() -> Self {
        Self {
            reader: BufReader::new(stdin()),
            writer: BufWriter::new(stdout()),
        }
    }

    /// Create stdio transport with existing buffered reader
    ///
    /// Useful when stdin has already been wrapped (e.g., for protocol detection)
    pub fn with_reader(reader: BufReader<Stdin>) -> Self {
        Self {
            reader,
            writer: BufWriter::new(stdout()),
        }
    }
}

impl sealed::Sealed for StdioTransport {}

impl Default for StdioTransport {
    fn default() -> Self {
        Self::new()
    }
}

impl Transport for StdioTransport {
    fn read_message<'a>(&'a mut self) -> super::traits::TransportFuture<'a, JsonRpcRequest> {
        Box::pin(async move {
            // For backward compatibility, use read_incoming and expect a request
            match self.read_incoming().await? {
                IncomingMessage::Request(request) => Ok(request),
                IncomingMessage::Response(_) => {
                    // Unexpected response - likely a client error
                    tracing::warn!("Received response when expecting request");
                    // Try again
                    self.read_message().await
                }
            }
        })
    }

    fn read_incoming<'a>(&'a mut self) -> super::traits::TransportFuture<'a, IncomingMessage> {
        Box::pin(async move {
            let mut line = String::new();

            // Read line from stdin
            let bytes_read = self.reader.read_line(&mut line).await?;

            // Check for EOF
            if bytes_read == 0 {
                return Err(TransportError::Closed);
            }

            // Trim whitespace
            let line = line.trim();
            if line.is_empty() {
                // Skip empty lines and try again
                return self.read_incoming().await;
            }

            // Parse and route the message
            let message = IncomingMessage::parse(line)?;

            match &message {
                IncomingMessage::Request(request) => {
                    tracing::debug!(
                        method = %request.method,
                        id = ?request.id,
                        "Received request"
                    );
                }
                IncomingMessage::Response(response) => {
                    tracing::debug!(
                        id = ?response.id,
                        has_result = response.result.is_some(),
                        has_error = response.error.is_some(),
                        "Received response to server request"
                    );
                }
            }

            Ok(message)
        })
    }

    fn write_message<'a>(
        &'a mut self,
        response: JsonRpcResponse,
    ) -> super::traits::TransportFuture<'a, ()> {
        Box::pin(async move {
            // Serialize to JSON
            let json = serde_json::to_string(&response)?;

            tracing::debug!(
                id = ?response.id,
                has_result = response.result.is_some(),
                has_error = response.error.is_some(),
                "Sending response"
            );

            // Write JSON line to stdout
            self.writer.write_all(json.as_bytes()).await?;
            self.writer.write_all(b"\n").await?;
            self.writer.flush().await?;

            Ok(())
        })
    }

    fn send_notification<'a>(
        &'a mut self,
        notification: JsonRpcNotification,
    ) -> super::traits::TransportFuture<'a, ()> {
        Box::pin(async move {
            // Serialize to JSON
            let json = serde_json::to_string(&notification)?;

            tracing::debug!(
                method = %notification.method,
                "Sending notification"
            );

            // Write JSON line to stdout
            self.writer.write_all(json.as_bytes()).await?;
            self.writer.write_all(b"\n").await?;
            self.writer.flush().await?;

            Ok(())
        })
    }

    fn send_request<'a>(
        &'a mut self,
        request: JsonRpcClientRequest,
    ) -> super::traits::TransportFuture<'a, ()> {
        Box::pin(async move {
            // Serialize to JSON
            let json = serde_json::to_string(&request)?;

            tracing::debug!(
                method = %request.method,
                id = %request.id,
                "Sending request to client"
            );

            // Write JSON line to stdout
            self.writer.write_all(json.as_bytes()).await?;
            self.writer.write_all(b"\n").await?;
            self.writer.flush().await?;

            Ok(())
        })
    }

    fn supports_bidirectional(&self) -> bool {
        true
    }

    fn shutdown<'a>(&'a mut self) -> super::traits::TransportFuture<'a, ()> {
        Box::pin(async move {
            self.writer.flush().await?;
            Ok(())
        })
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_stdio_transport_creation() {
        let _transport = StdioTransport::new();
    }

    #[test]
    fn test_stdio_transport_default() {
        let _transport = StdioTransport::default();
    }
}
