//! Response domain model
//!
//! This module defines the `Response` type, which represents an HTTP response
//! in a protocol-agnostic way. The response uses a builder pattern for
//! ergonomic construction.
//!
//! # Design
//!
//! - **Builder pattern**: Fluent API for constructing responses
//! - **HTTP-aware**: Uses `http::StatusCode` and `http::HeaderMap`
//! - **Flexible body**: Supports empty, bytes, and streaming bodies
//! - **Convenience methods**: `text()`, `json()`, `html()`, `redirect()`
//!
//! # Examples
//!
//! ```rust,no_run
//! use ruxno::Response;
//!
//! // Text response
//! let res = Response::text("Hello, World!");
//!
//! // JSON response
//! let res = Response::json(&serde_json::json!({ "status": "ok" }));
//!
//! // Custom response with builder pattern
//! let res = Response::new()
//!     .with_status(201)
//!     .with_header("x-custom", "value")
//!     .with_body("Created");
//!
//! // Redirect
//! let res = Response::redirect("/login");
//! ```

use crate::core::{CoreError, StatusCode};
use crate::domain::Headers;
use bytes::Bytes;
use futures_util::Stream;
use std::pin::Pin;

/// Response body type
///
/// Represents different types of response bodies:
/// - `Empty`: No body content (e.g., 204 No Content)
/// - `Bytes`: Static bytes (most common case)
/// - `Stream`: Streaming body for large responses or SSE
/// - `Upgrade`: Raw hyper response for protocol upgrades (WebSocket, etc.)
pub enum ResponseBody {
    /// Empty body (no content)
    Empty,

    /// Static bytes (buffered in memory)
    Bytes(Bytes),

    /// Streaming body (for large responses, SSE, etc.)
    ///
    /// Note: Streams are not cloneable. If you need to clone a response,
    /// use `Empty` or `Bytes` variants.
    Stream(Pin<Box<dyn Stream<Item = Result<Bytes, std::io::Error>> + Send + Sync>>),

    /// Raw hyper response for protocol upgrades (WebSocket, SSE upgrades, etc.)
    ///
    /// This variant bypasses the normal response conversion pipeline and returns
    /// the hyper response directly. Required for WebSocket upgrades because
    /// hyper stores upgrade state in response extensions that must not be lost.
    Upgrade(hyper::Response<http_body_util::Full<Bytes>>),
}

impl ResponseBody {
    /// Check if body is empty
    pub fn is_empty(&self) -> bool {
        matches!(self, ResponseBody::Empty)
    }

    /// Get body size if known (None for streams and upgrades)
    pub fn size(&self) -> Option<usize> {
        match self {
            ResponseBody::Empty => Some(0),
            ResponseBody::Bytes(bytes) => Some(bytes.len()),
            ResponseBody::Stream(_) => None,
            ResponseBody::Upgrade(_) => None,
        }
    }
}

impl std::fmt::Debug for ResponseBody {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            ResponseBody::Empty => write!(f, "Empty"),
            ResponseBody::Bytes(bytes) => {
                // Show size and preview of content (up to 100 bytes)
                let preview_len = bytes.len().min(100);
                let preview = &bytes[..preview_len];

                // Try to display as UTF-8 string if valid
                if let Ok(s) = std::str::from_utf8(preview) {
                    write!(
                        f,
                        "Bytes({} bytes, preview: {:?}{})",
                        bytes.len(),
                        s,
                        if bytes.len() > 100 { "..." } else { "" }
                    )
                } else {
                    // Display as hex for binary data
                    write!(
                        f,
                        "Bytes({} bytes, binary data: {:02x?}{})",
                        bytes.len(),
                        &bytes[..preview_len.min(20)],
                        if bytes.len() > 20 { "..." } else { "" }
                    )
                }
            }
            ResponseBody::Stream(_) => write!(f, "Stream(<streaming>)"),
            ResponseBody::Upgrade(_) => write!(f, "Upgrade(<hyper response>)"),
        }
    }
}

/// Response domain model
///
/// Represents an HTTP response with status, headers, and body.
/// Uses builder pattern for ergonomic construction.
///
/// # Examples
///
/// ```rust,no_run
/// use ruxno::Response;
///
/// // Simple text response
/// let res = Response::text("Hello");
///
/// // JSON response
/// let res = Response::json(&serde_json::json!({ "id": 123 }));
///
/// // Custom response
/// let res = Response::new()
///     .with_status(201)
///     .with_header("location", "/users/123")
///     .with_body("Created");
/// ```
#[derive(Debug)]
pub struct Response {
    /// HTTP status code
    status: StatusCode,

    /// Response headers (HTTP-aware, case-insensitive)
    headers: Headers,

    /// Response body
    body: ResponseBody,
}

impl Response {
    /// Create a new empty response with 200 OK status
    ///
    /// # Examples
    ///
    /// ```rust,ignore
    /// let res = Response::new();
    /// assert_eq!(res.status(), StatusCode::OK);
    /// ```
    pub fn new() -> Self {
        Self {
            status: StatusCode::OK,
            headers: Headers::new(),
            body: ResponseBody::Empty,
        }
    }

    /// Create a text response with 200 OK status
    ///
    /// Sets `Content-Type: text/plain; charset=utf-8` header.
    ///
    /// # Examples
    ///
    /// ```rust,ignore
    /// let res = Response::text("Hello, World!");
    /// ```
    pub fn text(text: impl Into<String>) -> Self {
        let mut headers = Headers::new();
        headers.set_static("content-type", "text/plain; charset=utf-8");

        Self {
            status: StatusCode::OK,
            headers,
            body: ResponseBody::Bytes(Bytes::from(text.into())),
        }
    }

    /// Create a JSON response with 200 OK status
    ///
    /// Sets `Content-Type: application/json` header.
    /// Returns `Err(CoreError::Internal)` if serialization fails.
    ///
    /// # Examples
    ///
    /// ```rust,ignore
    /// let res = Response::json(&serde_json::json!({ "status": "ok" }))?;
    /// ```
    pub fn json<T: serde::Serialize>(value: &T) -> Result<Self, CoreError> {
        let body = serde_json::to_string(value)
            .map_err(|e| CoreError::internal(format!("JSON serialization failed: {}", e)))?;
        let mut headers = Headers::new();
        headers.set_static("content-type", "application/json");
        Ok(Self {
            status: StatusCode::OK,
            headers,
            body: ResponseBody::Bytes(Bytes::from(body)),
        })
    }

    /// Create an HTML response with 200 OK status
    ///
    /// Sets `Content-Type: text/html; charset=utf-8` header.
    ///
    /// # Examples
    ///
    /// ```rust,ignore
    /// let res = Response::html("<h1>Hello</h1>");
    /// ```
    pub fn html(html: impl Into<String>) -> Self {
        let mut headers = Headers::new();
        headers.set_static("content-type", "text/html; charset=utf-8");

        Self {
            status: StatusCode::OK,
            headers,
            body: ResponseBody::Bytes(Bytes::from(html.into())),
        }
    }

    /// Create a redirect response
    ///
    /// Returns 302 Found with `Location` header.
    /// Use `with_status()` to change to 301 (permanent) or 307/308 (preserve method).
    ///
    /// # Examples
    ///
    /// ```rust,ignore
    /// // Temporary redirect (302)
    /// let res = Response::redirect("/login");
    ///
    /// // Permanent redirect (301)
    /// let res = Response::redirect("/new-url").with_status_code(StatusCode::MOVED_PERMANENTLY);
    /// ```
    pub fn redirect(location: impl Into<String>) -> Self {
        let mut headers = Headers::new();
        headers.set("location", &location.into()).ok();

        Self {
            status: StatusCode::FOUND, // 302
            headers,
            body: ResponseBody::Empty,
        }
    }

    /// Create a protocol upgrade response (WebSocket, etc.)
    ///
    /// Wraps a raw hyper response so it can be returned through the domain layer
    /// without losing the upgrade extensions that hyper needs to complete the handshake.
    ///
    /// This is an internal constructor used by `WebSocketUpgrade::upgrade()`.
    /// The `to_hyper_response` conversion will return this response unchanged.
    pub fn upgrade(hyper_response: hyper::Response<http_body_util::Full<bytes::Bytes>>) -> Self {
        Self {
            status: hyper_response.status(),
            headers: Headers::new(), // headers are in the raw response
            body: ResponseBody::Upgrade(hyper_response),
        }
    }

    /// Build an error response from a `CoreError`
    ///
    /// The status code is derived from `CoreError::status_code()`. The body is a
    /// JSON envelope of the form:
    ///
    /// ```json
    /// { "error": "...", "status": 500, "error_id": "ERR-..." }
    /// ```
    ///
    /// In production mode, 5xx error messages are masked to `"Internal Server Error"`
    /// and the full error is logged to stderr with the correlation ID. Client errors
    /// (4xx) always include details in both modes.
    ///
    /// This is the canonical place to convert framework errors into HTTP responses
    /// — both the per-request handler path and the connection-limit 503 path go
    /// through here so clients see one error envelope shape.
    pub fn from_error(err: CoreError, production_mode: bool) -> Self {
        let status =
            StatusCode::from_u16(err.status_code()).unwrap_or(StatusCode::INTERNAL_SERVER_ERROR);
        Self::from_error_with_status(err, status, production_mode)
    }

    /// Build an error response with an explicit status code.
    ///
    /// Same JSON envelope and production-masking rules as [`Response::from_error`],
    /// but the caller picks the status code. Useful for connection-level rejections
    /// (503 Service Unavailable) where the error variant alone doesn't determine
    /// the right status.
    pub fn from_error_with_status(
        err: CoreError,
        status: StatusCode,
        production_mode: bool,
    ) -> Self {
        let error_id = generate_error_id();
        let is_server_error = err.is_server_error();

        let error_message = if production_mode && is_server_error {
            // Log full error server-side; return generic message to client.
            eprintln!("🔴 [{}] Internal error: {}", error_id, err);
            "Internal Server Error".to_string()
        } else {
            if is_server_error {
                // Log server errors even in development mode for visibility.
                eprintln!("🔴 [{}] Internal error: {}", error_id, err);
            }
            err.to_string()
        };

        let body = serde_json::json!({
            "error": error_message,
            "status": status.as_u16(),
            "error_id": error_id,
        });

        Self::json(&body)
            .unwrap_or_else(|_| {
                // Fallback: plain text if JSON serialization somehow fails
                Self::new()
                    .with_status_code(status)
                    .with_body("Internal Server Error")
            })
            .with_status_code(status)
    }

    /// Set status code from u16 (builder pattern)
    ///
    /// Convenience method for setting status code using numeric values.
    /// Falls back to 500 Internal Server Error if the status code is invalid.
    ///
    /// # Examples
    ///
    /// ```rust,ignore
    /// let res = Response::new().with_status(201);
    /// let res = Response::new().with_status(404);
    /// ```
    pub fn with_status(mut self, status: u16) -> Self {
        self.status = StatusCode::from_u16(status).unwrap_or(StatusCode::INTERNAL_SERVER_ERROR);
        self
    }

    /// Set status code using StatusCode enum (builder pattern)
    ///
    /// # Examples
    ///
    /// ```rust,ignore
    /// let res = Response::new().with_status_code(StatusCode::CREATED);
    /// ```
    pub fn with_status_code(mut self, status: StatusCode) -> Self {
        self.status = status;
        self
    }

    /// Add a header (builder pattern)
    ///
    /// # Examples
    ///
    /// ```rust,ignore
    /// let res = Response::new()
    ///     .with_header("x-request-id", "123")
    ///     .with_header("cache-control", "no-cache");
    /// ```
    pub fn with_header(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
        self.headers.set(&name.into(), &value.into()).ok();
        self
    }

    /// Set body (builder pattern)
    ///
    /// # Examples
    ///
    /// ```rust,ignore
    /// let res = Response::new().with_body("Hello, World!");
    /// ```
    pub fn with_body(mut self, body: impl Into<String>) -> Self {
        self.body = ResponseBody::Bytes(Bytes::from(body.into()));
        self
    }

    /// Set body as bytes (builder pattern)
    ///
    /// # Examples
    ///
    /// ```rust,ignore
    /// let res = Response::new().with_bytes(Bytes::from("data"));
    /// ```
    pub fn with_bytes(mut self, bytes: Bytes) -> Self {
        self.body = ResponseBody::Bytes(bytes);
        self
    }

    /// Set body with a stream (for SSE, large responses, etc.)
    ///
    /// # Examples
    ///
    /// ```rust,ignore
    /// let stream = futures_util::stream::iter(vec![
    ///     Ok(Bytes::from("chunk1")),
    ///     Ok(Bytes::from("chunk2")),
    /// ]);
    /// let res = Response::new().with_stream(stream);
    /// ```
    pub fn with_stream<S>(mut self, stream: S) -> Self
    where
        S: Stream<Item = Result<Bytes, std::io::Error>> + Send + Sync + 'static,
    {
        self.body = ResponseBody::Stream(Box::pin(stream));
        self
    }

    /// Get status code
    ///
    /// # Examples
    ///
    /// ```rust,ignore
    /// assert_eq!(res.status(), StatusCode::OK);
    /// ```
    pub fn status(&self) -> StatusCode {
        self.status
    }

    /// Get headers
    ///
    /// # Examples
    ///
    /// ```rust,ignore
    /// let headers = res.headers();
    /// ```
    pub fn headers(&self) -> &Headers {
        &self.headers
    }

    /// Get mutable headers
    ///
    /// # Examples
    ///
    /// ```rust,ignore
    /// res.headers_mut().set("x-custom", "value").unwrap();
    /// ```
    pub fn headers_mut(&mut self) -> &mut Headers {
        &mut self.headers
    }

    /// Get body
    ///
    /// # Examples
    ///
    /// ```rust,ignore
    /// let body = res.body();
    /// ```
    pub fn body(&self) -> &ResponseBody {
        &self.body
    }

    /// Take body (consumes self)
    ///
    /// Useful for converting to HTTP response.
    ///
    /// # Examples
    ///
    /// ```rust,ignore
    /// let body = res.into_body();
    /// ```
    pub fn into_body(self) -> ResponseBody {
        self.body
    }
}

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

// Convenience conversions

impl From<String> for Response {
    fn from(text: String) -> Self {
        Response::text(text)
    }
}

impl From<&str> for Response {
    fn from(text: &str) -> Self {
        Response::text(text)
    }
}

impl From<Bytes> for Response {
    fn from(bytes: Bytes) -> Self {
        Response::new().with_bytes(bytes)
    }
}

/// Generate a unique error ID for log/response correlation.
///
/// Format: `ERR-{uuid_simple}`. Used by [`Response::from_error`]
/// and [`Response::from_error_with_status`] so server-side logs and the JSON body
/// returned to the client share an identifier without exposing internal details.
fn generate_error_id() -> String {
    format!("ERR-{}", uuid::Uuid::new_v4().simple())
}

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

    #[test]
    fn test_response_new() {
        let res = Response::new();
        assert_eq!(res.status(), StatusCode::OK);
        assert!(res.body().is_empty());
        assert_eq!(res.headers().len(), 0);
    }

    #[test]
    fn test_response_text() {
        let res = Response::text("Hello, World!");
        assert_eq!(res.status(), StatusCode::OK);
        assert_eq!(
            res.headers().get("content-type").unwrap(),
            "text/plain; charset=utf-8"
        );
        match res.body() {
            ResponseBody::Bytes(bytes) => {
                assert_eq!(bytes, &Bytes::from("Hello, World!"));
            }
            _ => panic!("Expected Bytes body"),
        }
    }

    #[test]
    fn test_response_json() {
        let data = serde_json::json!({ "name": "John", "age": 30 });
        let res = Response::json(&data).unwrap();
        assert_eq!(res.status(), StatusCode::OK);
        assert_eq!(
            res.headers().get("content-type").unwrap(),
            "application/json"
        );
        match res.body() {
            ResponseBody::Bytes(bytes) => {
                let parsed: serde_json::Value = serde_json::from_slice(bytes).unwrap();
                assert_eq!(parsed["name"], "John");
                assert_eq!(parsed["age"], 30);
            }
            _ => panic!("Expected Bytes body"),
        }
    }

    #[test]
    fn test_response_html() {
        let res = Response::html("<h1>Hello</h1>");
        assert_eq!(res.status(), StatusCode::OK);
        assert_eq!(
            res.headers().get("content-type").unwrap(),
            "text/html; charset=utf-8"
        );
        match res.body() {
            ResponseBody::Bytes(bytes) => {
                assert_eq!(bytes, &Bytes::from("<h1>Hello</h1>"));
            }
            _ => panic!("Expected Bytes body"),
        }
    }

    #[test]
    fn test_response_redirect() {
        let res = Response::redirect("/login");
        assert_eq!(res.status(), StatusCode::FOUND);
        assert_eq!(res.headers().get("location").unwrap(), "/login");
        assert!(res.body().is_empty());
    }

    #[test]
    fn test_response_redirect_permanent() {
        let res = Response::redirect("/new-url").with_status_code(StatusCode::MOVED_PERMANENTLY);
        assert_eq!(res.status(), StatusCode::MOVED_PERMANENTLY);
        assert_eq!(res.headers().get("location").unwrap(), "/new-url");
    }

    #[test]
    fn test_response_with_status() {
        let res = Response::new().with_status(201);
        assert_eq!(res.status(), StatusCode::CREATED);
    }

    #[test]
    fn test_response_with_status_code() {
        let res = Response::new().with_status_code(StatusCode::CREATED);
        assert_eq!(res.status(), StatusCode::CREATED);

        let res = Response::new().with_status_code(StatusCode::NOT_FOUND);
        assert_eq!(res.status(), StatusCode::NOT_FOUND);

        let res = Response::new().with_status_code(StatusCode::INTERNAL_SERVER_ERROR);
        assert_eq!(res.status(), StatusCode::INTERNAL_SERVER_ERROR);
    }

    #[test]
    fn test_response_with_status_invalid() {
        // Status codes outside 100-999 are invalid per the http crate.
        // with_status() falls back to 500 for those.
        // Note: 999 is accepted by the http crate (range is 100-999 inclusive),
        // so we use 1000 which is truly out of range.
        let res = Response::new().with_status(1000);
        assert_eq!(res.status(), StatusCode::INTERNAL_SERVER_ERROR);
    }

    #[test]
    fn test_response_with_header() {
        let res = Response::new()
            .with_header("x-request-id", "123")
            .with_header("cache-control", "no-cache");

        assert_eq!(res.headers().get("x-request-id").unwrap(), "123");
        assert_eq!(res.headers().get("cache-control").unwrap(), "no-cache");
    }

    #[test]
    fn test_response_with_body() {
        let res = Response::new().with_body("Custom body");
        match res.body() {
            ResponseBody::Bytes(bytes) => {
                assert_eq!(bytes, &Bytes::from("Custom body"));
            }
            _ => panic!("Expected Bytes body"),
        }
    }

    #[test]
    fn test_response_with_bytes() {
        let data = Bytes::from("binary data");
        let res = Response::new().with_bytes(data.clone());
        match res.body() {
            ResponseBody::Bytes(bytes) => {
                assert_eq!(bytes, &data);
            }
            _ => panic!("Expected Bytes body"),
        }
    }

    #[test]
    fn test_response_builder_pattern() {
        let res = Response::new()
            .with_status_code(StatusCode::CREATED)
            .with_header("location", "/users/123")
            .with_header("x-custom", "value")
            .with_body("User created");

        assert_eq!(res.status(), StatusCode::CREATED);
        assert_eq!(res.headers().get("location").unwrap(), "/users/123");
        assert_eq!(res.headers().get("x-custom").unwrap(), "value");
        match res.body() {
            ResponseBody::Bytes(bytes) => {
                assert_eq!(bytes, &Bytes::from("User created"));
            }
            _ => panic!("Expected Bytes body"),
        }
    }

    #[test]
    fn test_response_from_string() {
        let res: Response = "Hello".to_string().into();
        assert_eq!(res.status(), StatusCode::OK);
        match res.body() {
            ResponseBody::Bytes(bytes) => {
                assert_eq!(bytes, &Bytes::from("Hello"));
            }
            _ => panic!("Expected Bytes body"),
        }
    }

    #[test]
    fn test_response_from_str() {
        let res: Response = "Hello".into();
        assert_eq!(res.status(), StatusCode::OK);
        match res.body() {
            ResponseBody::Bytes(bytes) => {
                assert_eq!(bytes, &Bytes::from("Hello"));
            }
            _ => panic!("Expected Bytes body"),
        }
    }

    #[test]
    fn test_response_from_bytes() {
        let data = Bytes::from("data");
        let res: Response = data.clone().into();
        match res.body() {
            ResponseBody::Bytes(bytes) => {
                assert_eq!(bytes, &data);
            }
            _ => panic!("Expected Bytes body"),
        }
    }

    #[test]
    fn test_response_body_is_empty() {
        assert!(ResponseBody::Empty.is_empty());
        assert!(!ResponseBody::Bytes(Bytes::from("data")).is_empty());
    }

    #[test]
    fn test_response_body_size() {
        assert_eq!(ResponseBody::Empty.size(), Some(0));
        assert_eq!(ResponseBody::Bytes(Bytes::from("hello")).size(), Some(5));
    }

    #[test]
    fn test_response_headers_mut() {
        let mut res = Response::new();
        res.headers_mut().set("x-custom", "value").unwrap();
        assert_eq!(res.headers().get("x-custom"), Some("value"));
    }

    #[test]
    fn test_response_into_body() {
        let res = Response::text("Hello");
        let body = res.into_body();
        match body {
            ResponseBody::Bytes(bytes) => {
                assert_eq!(bytes, Bytes::from("Hello"));
            }
            _ => panic!("Expected Bytes body"),
        }
    }

    #[test]
    fn test_response_default() {
        let res = Response::default();
        assert_eq!(res.status(), StatusCode::OK);
        assert!(res.body().is_empty());
    }
}
