//! 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 async_trait::async_trait;
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()
    }
}

#[async_trait]
impl Transport for StdioTransport {
    async fn read_message(&mut self) -> Result<JsonRpcRequest, TransportError> {
        // 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
            }
        }
    }

    async fn read_incoming(&mut self) -> Result<IncomingMessage, TransportError> {
        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)
    }

    async fn write_message(&mut self, response: JsonRpcResponse) -> Result<(), TransportError> {
        // 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(())
    }

    async fn send_notification(
        &mut self,
        notification: JsonRpcNotification,
    ) -> Result<(), TransportError> {
        // 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(())
    }

    async fn send_request(&mut self, request: JsonRpcClientRequest) -> Result<(), TransportError> {
        // 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
    }

    async fn shutdown(&mut self) -> Result<(), TransportError> {
        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();
    }
}
