//! WebSocket binding — persistent connection, message-based RPC.
//!
//! Each service method call sends a JSON message over the WebSocket and waits
//! for a response message with a matching correlation ID.
//!
//! Message format (over the wire):
//! ```json
//! { "id": "uuid", "service": "UserService", "method": "get_user", "params": { "id": "123" } }
//! ```
//!
//! Response:
//! ```json
//! { "id": "uuid", "status": 200, "body": { ... } }
//! ```

use super::{Binding, BindingKind, ServiceRequest, ServiceResponse};
use crate::{config::ServiceConfig, ServiceError, ServiceResult};

/// WebSocket binding — persistent connection, message-based RPC.
pub struct WebSocketBinding {
    pub config: ServiceConfig,
}

impl WebSocketBinding {
    pub fn new(config: ServiceConfig) -> Self {
        Self { config }
    }

    /// Serialize a request to the wire JSON format.
    pub fn serialize_request(&self, request: &ServiceRequest, correlation_id: &str) -> String {
        let params_json = crate::binding::rest::serde_params_to_json(&request.params);
        format!(
            "{{\"id\":\"{}\",\"service\":\"{}\",\"method\":\"{}\",\"params\":{}}}",
            correlation_id, request.service, request.method, params_json
        )
    }

    /// Build the WebSocket URL from the config base_url.
    /// Converts `https://` → `wss://` and `http://` → `ws://`.
    pub fn ws_url(&self) -> Option<String> {
        self.config.base_url.as_ref().map(|url| {
            url.replace("https://", "wss://")
                .replace("http://", "ws://")
        })
    }
}

impl Binding for WebSocketBinding {
    fn kind(&self) -> BindingKind {
        BindingKind::WebSocket
    }

    fn call(&self, request: ServiceRequest) -> ServiceResult<ServiceResponse> {
        if self.config.base_url.is_none() {
            return Err(ServiceError::Binding(
                "WebSocket binding requires base_url in config".into(),
            ));
        }

        // Generate a correlation ID for request/response matching
        let correlation_id = simple_uuid();
        let _message = self.serialize_request(&request, &correlation_id);
        let _ws_url = self.ws_url();

        // In production: use tungstenite or tokio-tungstenite:
        //   let (mut ws, _) = connect(&ws_url)?;
        //   ws.send(Message::Text(message))?;
        //   loop { let msg = ws.read_message()?; if msg_id == correlation_id { return parse(msg); } }

        Ok(ServiceResponse::ok(format!(
            "{{\"id\":\"{}\",\"service\":\"{}\",\"method\":\"{}\"}}",
            correlation_id, request.service, request.method
        )))
    }

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

fn simple_uuid() -> String {
    // Deterministic for testing (not cryptographically random).
    // In production: use uuid crate.
    "ws-00000000-0000-0000-0000-000000000001".to_string()
}
