//! Request domain model
//!
//! This module defines the `Request` type, which represents an HTTP request
//! in a protocol-agnostic way. The request uses Arc-wrapped internals for
//! cheap cloning and lazy body parsing with caching.
//!
//! # Design
//!
//! - **Arc-wrapped**: All data is wrapped in `Arc` for cheap cloning
//! - **Lazy parsing**: Body is parsed on-demand and cached
//! - **Immutable**: Request is immutable after construction (params set via `with_params`)
//! - **HTTP-aware**: Uses `http::Uri` for proper URI parsing and `http::HeaderMap` for headers
//!
//! # Examples
//!
//! ```rust,no_run
//! use ruxno::Request;
//!
//! async fn handler(req: Request) {
//!     // Access request properties
//!     let method = req.method();
//!     let path = req.path();
//!     
//!     // Get path parameters
//!     let id = req.param("id").unwrap();
//!     
//!     // Get query parameters
//!     if let Some(page) = req.query("page") {
//!         println!("Page: {}", page);
//!     }
//!     
//!     // Parse body (lazy, cached)
//!     let body: MyStruct = req.json().await.unwrap();
//! }
//! ```

use crate::core::{CoreError, Method};
use crate::domain::{Headers, Params, Query};
use bytes::Bytes;
use http::{Uri, Version};
use std::collections::HashMap;
use std::sync::Arc;

/// Request domain model
///
/// Represents an HTTP request in a protocol-agnostic way. Uses Arc-wrapped
/// internals for cheap cloning and lazy body parsing with caching.
///
/// # Cloning
///
/// Cloning a `Request` is cheap (just increments Arc reference counts).
/// This allows passing requests through middleware chains efficiently.
///
/// # Body Parsing
///
/// Body parsing is lazy and cached. The first call to `json()`, `text()`,
/// or `form()` parses the body and caches the result. Subsequent calls
/// return the cached value.
///
/// # Examples
///
/// ```rust,no_run
/// use ruxno::Request;
///
/// async fn handler(req: Request) {
///     // Cheap clone
///     let req2 = req.clone();
///     
///     // Access properties
///     println!("Method: {}", req.method());
///     println!("Path: {}", req.path());
///     
///     // Get parameters
///     if let Ok(id) = req.param("id") {
///         println!("ID: {}", id);
///     }
/// }
/// ```
#[derive(Clone)]
pub struct Request {
    /// Immutable request data shared via Arc
    inner: Arc<RequestInner>,
    /// Path parameters set by the router — kept outside Arc so `with_params`
    /// is O(1) (no inner clone needed).
    params: Params,
}

/// Inner request data (immutable after construction)
struct RequestInner {
    /// HTTP method
    method: Method,

    /// Request URI (parsed HTTP URI with path, query, etc.)
    uri: Uri,

    /// HTTP version
    version: Version,

    /// Query parameters (parsed from URI, shared via Arc to avoid clone in with_params)
    query: Arc<Query>,

    /// Headers (HTTP-aware, case-insensitive, multi-value support)
    headers: Headers,

    /// Raw body bytes
    body: Bytes,

    /// Cached parsed text body (lazy, set at most once)
    text_cache: std::sync::OnceLock<String>,

    /// Original hyper request (for WebSocket upgrades and streaming)
    /// Using Mutex for interior mutability - can be taken once
    raw: Arc<tokio::sync::Mutex<Option<hyper::Request<hyper::body::Incoming>>>>,
}

impl Request {
    /// Create a new request
    ///
    /// # Arguments
    ///
    /// - `method`: HTTP method
    /// - `uri`: HTTP URI (includes path and query string)
    /// - `version`: HTTP version
    /// - `query`: Parsed query parameters
    /// - `headers`: Request headers (Headers wrapper)
    /// - `body`: Raw body bytes
    ///
    /// # Examples
    ///
    /// ```rust,ignore
    /// let uri = "/api/users?page=1".parse().unwrap();
    /// let req = Request::new(
    ///     Method::GET,
    ///     uri,
    ///     Version::HTTP_11,
    ///     HashMap::from([("page".to_string(), "1".to_string())]),
    ///     Headers::new(),
    ///     Bytes::new(),
    /// );
    /// ```
    pub fn new(
        method: Method,
        uri: Uri,
        version: Version,
        query: Query,
        headers: Headers,
        body: Bytes,
    ) -> Self {
        Self {
            inner: Arc::new(RequestInner {
                method,
                uri,
                version,
                query: Arc::new(query),
                headers,
                body,
                text_cache: std::sync::OnceLock::new(),
                raw: Arc::new(tokio::sync::Mutex::new(None)),
            }),
            params: Params::new(),
        }
    }

    /// Create a request with path parameters (used by router)
    ///
    /// This is O(1) — `params` lives outside the `Arc` so no inner clone is needed.
    ///
    /// # Arguments
    ///
    /// - `params`: Path parameters extracted from route
    ///
    /// # Examples
    ///
    /// ```rust,ignore
    /// // Router extracts params from route pattern
    /// let params = Params::from(vec![("id".to_string(), "123".to_string())]);
    /// let req = base_req.with_params(params);
    /// ```
    pub fn with_params(self, params: Params) -> Self {
        Self {
            inner: self.inner,
            params,
        }
    }

    /// Get HTTP method
    ///
    /// # Examples
    ///
    /// ```rust,ignore
    /// assert_eq!(req.method(), &Method::GET);
    /// ```
    pub fn method(&self) -> &Method {
        &self.inner.method
    }

    /// Get full URI (including query string)
    ///
    /// Returns the parsed HTTP URI which includes path, query, and optionally
    /// scheme, authority, etc.
    ///
    /// # Examples
    ///
    /// ```rust,ignore
    /// assert_eq!(req.uri().path(), "/api/users");
    /// assert_eq!(req.uri().query(), Some("page=1"));
    /// ```
    pub fn uri(&self) -> &Uri {
        &self.inner.uri
    }

    /// Get HTTP version
    ///
    /// Returns the HTTP version used for this request (e.g., HTTP/1.1, HTTP/2).
    ///
    /// # Examples
    ///
    /// ```rust,ignore
    /// assert_eq!(req.version(), Version::HTTP_11);
    /// ```
    pub fn version(&self) -> Version {
        self.inner.version
    }

    /// Get request path (without query string)
    ///
    /// This is a convenience method that extracts the path from the URI.
    ///
    /// # Examples
    ///
    /// ```rust,ignore
    /// assert_eq!(req.path(), "/api/users");
    /// ```
    pub fn path(&self) -> &str {
        self.inner.uri.path()
    }

    /// Get a query parameter by key
    pub fn query(&self, key: &str) -> Option<&str> {
        self.inner.query.get(key)
    }

    /// Get all query parameters
    pub fn query_all(&self) -> &Query {
        &self.inner.query
    }

    /// Get a header by key (case-insensitive)
    ///
    /// Returns the first value if multiple values exist for the header.
    /// Use `header_all()` to get all values.
    ///
    /// # Examples
    ///
    /// ```rust,ignore
    /// if let Some(content_type) = req.header("content-type") {
    ///     println!("Content-Type: {}", content_type);
    /// }
    /// ```
    pub fn header(&self, key: &str) -> Option<&str> {
        self.inner.headers.get(key)
    }

    /// Get all values for a header (case-insensitive)
    ///
    /// Returns an iterator over all values for the given header key.
    /// Useful for headers that can have multiple values (e.g., Set-Cookie).
    ///
    /// # Examples
    ///
    /// ```rust,ignore
    /// for cookie in req.header_all("set-cookie") {
    ///     println!("Cookie: {}", cookie);
    /// }
    /// ```
    pub fn header_all(&self, key: &str) -> Vec<&str> {
        self.inner.headers.get_all(key).collect()
    }

    /// Get all headers
    ///
    /// # Examples
    ///
    /// ```rust,ignore
    /// let headers = req.headers();
    /// for (key, value) in headers.iter() {
    ///     println!("{}: {}", key, value);
    /// }
    /// ```
    pub fn headers(&self) -> &Headers {
        &self.inner.headers
    }

    /// Get a path parameter by key
    ///
    /// Path parameters are extracted from the route pattern by the router.
    /// For example, a route `/users/:id` would extract `id` as a parameter.
    ///
    /// # Errors
    ///
    /// Returns `CoreError::MissingParameter` if the parameter doesn't exist.
    ///
    /// # Examples
    ///
    /// ```rust,ignore
    /// // Route: /users/:id
    /// // Request: /users/123
    /// let id = req.param("id")?; // "123"
    /// ```
    pub fn param(&self, key: &str) -> Result<&str, CoreError> {
        self.params
            .get(key)
            .ok_or_else(|| CoreError::missing_parameter(key))
    }

    /// Get all path parameters
    ///
    /// # Examples
    ///
    /// ```rust,ignore
    /// let params = req.params();
    /// for (key, value) in params {
    ///     println!("{} = {}", key, value);
    /// }
    /// ```
    pub fn params(&self) -> &Params {
        &self.params
    }

    /// Get raw body bytes
    ///
    /// # Examples
    ///
    /// ```rust,ignore
    /// let bytes = req.body();
    /// println!("Body size: {} bytes", bytes.len());
    /// ```
    pub fn body(&self) -> &Bytes {
        &self.inner.body
    }

    /// Parse body as JSON
    ///
    /// Uses the `JsonParser` with default size limits (2MB).
    /// The body is parsed from raw bytes for optimal performance.
    ///
    /// # Errors
    ///
    /// Returns `CoreError::BodyParseError` if the body is not valid JSON.
    /// Returns `CoreError::BadRequest` if body exceeds size limit.
    ///
    /// # Examples
    ///
    /// ```rust,ignore
    /// #[derive(Deserialize)]
    /// struct User {
    ///     name: String,
    ///     email: String,
    /// }
    ///
    /// let user: User = req.json().await?;
    /// println!("User: {}", user.name);
    /// ```
    pub async fn json<T>(&self) -> Result<T, CoreError>
    where
        T: serde::de::DeserializeOwned,
    {
        // Use JsonParser for consistent parsing with size limits
        crate::body::JsonParser::parse_as(&self.inner.body).await
    }

    /// Parse body as plain text
    ///
    /// The body is parsed lazily and the result is cached.
    ///
    /// # Errors
    ///
    /// Returns `CoreError::BodyParseError` if the body is not valid UTF-8.
    ///
    /// # Examples
    ///
    /// ```rust,ignore
    /// let text = req.text().await?;
    /// println!("Body: {}", text);
    /// ```
    pub async fn text(&self) -> Result<String, CoreError> {
        if let Some(cached) = self.inner.text_cache.get() {
            return Ok(cached.clone());
        }
        let text = String::from_utf8(self.inner.body.to_vec())
            .map_err(|e| CoreError::body_parse_error(format!("Invalid UTF-8: {}", e)))?;
        Ok(self.inner.text_cache.get_or_init(|| text).clone())
    }

    /// Parse body as URL-encoded form data
    ///
    /// Uses the `FormParser` with default size limits (1MB).
    /// The body is parsed from raw bytes for optimal performance.
    ///
    /// # Errors
    ///
    /// Returns `CoreError::BodyParseError` if the body is not valid form data.
    /// Returns `CoreError::BadRequest` if body exceeds size limit.
    ///
    /// # Examples
    ///
    /// ```rust,ignore
    /// let form = req.form().await?;
    /// if let Some(email) = form.get("email") {
    ///     println!("Email: {}", email);
    /// }
    /// ```
    pub async fn form(&self) -> Result<HashMap<String, String>, CoreError> {
        // Use FormParser for consistent parsing with size limits
        crate::body::FormParser::parse_as(&self.inner.body).await
    }

    /// Get body as raw bytes (alias for `body()`)
    ///
    /// # Examples
    ///
    /// ```rust,ignore
    /// let bytes = req.bytes();
    /// ```
    pub fn bytes(&self) -> &Bytes {
        self.body()
    }

    /// Get access to the raw hyper request (for WebSocket upgrades)
    ///
    /// This provides access to the original hyper request, which can be taken
    /// once for protocol upgrades like WebSocket. After taking, the Option
    /// will be None.
    ///
    /// # Examples
    ///
    /// ```rust,ignore
    /// let raw_guard = req.raw_request().lock().await;
    /// if let Some(hyper_req) = raw_guard.take() {
    ///     // Use hyper_req for upgrade
    /// }
    /// ```
    pub(crate) fn raw_request(
        &self,
    ) -> &Arc<tokio::sync::Mutex<Option<hyper::Request<hyper::body::Incoming>>>> {
        &self.inner.raw
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::core::Method;
    use crate::domain::Query;

    fn create_test_request() -> Request {
        let mut headers = Headers::new();
        headers.set("content-type", "application/json").unwrap();
        headers.set("authorization", "Bearer token123").unwrap();

        Request::new(
            Method::GET,
            "/api/users?page=1&limit=10".parse().unwrap(),
            Version::HTTP_11,
            Query::from(vec![
                ("page".to_string(), "1".to_string()),
                ("limit".to_string(), "10".to_string()),
            ]),
            headers,
            Bytes::from(r#"{"name":"John","email":"john@example.com"}"#),
        )
    }

    #[test]
    fn test_request_method() {
        let req = create_test_request();
        assert_eq!(req.method(), &Method::GET);
    }

    #[test]
    fn test_request_uri() {
        let req = create_test_request();
        assert_eq!(req.uri().path(), "/api/users");
        assert_eq!(req.uri().query(), Some("page=1&limit=10"));
    }

    #[test]
    fn test_request_version() {
        let req = create_test_request();
        assert_eq!(req.version(), Version::HTTP_11);
    }

    #[test]
    fn test_request_path() {
        let req = create_test_request();
        assert_eq!(req.path(), "/api/users");
    }

    #[test]
    fn test_request_query() {
        let req = create_test_request();
        assert_eq!(req.query("page"), Some("1"));
        assert_eq!(req.query("limit"), Some("10"));
        assert_eq!(req.query("nonexistent"), None);
    }

    #[test]
    fn test_request_query_all() {
        let req = create_test_request();
        let query = req.query_all();
        assert_eq!(query.len(), 2);
        assert_eq!(query.get("page"), Some("1"));
        assert_eq!(query.get("limit"), Some("10"));
    }

    #[test]
    fn test_request_header() {
        let req = create_test_request();
        assert_eq!(req.header("content-type"), Some("application/json"));
        assert_eq!(req.header("Content-Type"), Some("application/json")); // Case-insensitive
        assert_eq!(req.header("authorization"), Some("Bearer token123"));
        assert_eq!(req.header("nonexistent"), None);
    }

    #[test]
    fn test_request_header_all() {
        let mut headers = Headers::new();
        headers.append("set-cookie", "cookie1=value1").unwrap();
        headers.append("set-cookie", "cookie2=value2").unwrap();

        let req = Request::new(
            Method::GET,
            "/".parse().unwrap(),
            Version::HTTP_11,
            Query::new(),
            headers,
            Bytes::new(),
        );

        let cookies = req.header_all("set-cookie");
        assert_eq!(cookies.len(), 2);
        assert!(cookies.contains(&"cookie1=value1"));
        assert!(cookies.contains(&"cookie2=value2"));
    }

    #[test]
    fn test_request_headers() {
        let req = create_test_request();
        let headers = req.headers();
        assert_eq!(headers.len(), 2);
        assert!(headers.contains("content-type"));
        assert!(headers.contains("authorization"));
    }

    #[test]
    fn test_request_param() {
        let req = create_test_request();

        // Set params using with_params
        let req = req.with_params(Params::from(vec![
            ("id".to_string(), "123".to_string()),
            ("name".to_string(), "john".to_string()),
        ]));

        assert_eq!(req.param("id").unwrap(), "123");
        assert_eq!(req.param("name").unwrap(), "john");
        assert!(req.param("nonexistent").is_err());
    }

    #[test]
    fn test_request_params() {
        let req = create_test_request();

        let req = req.with_params(Params::from(vec![("id".to_string(), "123".to_string())]));

        let params = req.params();
        assert_eq!(params.len(), 1);
        assert_eq!(params.get("id"), Some("123"));
    }

    #[test]
    fn test_request_with_params_immutable() {
        let req1 = create_test_request();
        let req2 = req1.clone();

        // Set params on req2
        let req2 = req2.with_params(Params::from(vec![("id".to_string(), "123".to_string())]));

        // req1 should not have params (params are per-instance, not shared via Arc)
        assert!(req1.param("id").is_err());
        assert_eq!(req2.param("id").unwrap(), "123");
    }

    #[test]
    fn test_request_body() {
        let req = create_test_request();
        let body = req.body();
        assert!(!body.is_empty());
        assert_eq!(
            body,
            &Bytes::from(r#"{"name":"John","email":"john@example.com"}"#)
        );
    }

    #[test]
    fn test_request_bytes() {
        let req = create_test_request();
        let bytes = req.bytes();
        assert_eq!(bytes, req.body());
    }

    #[tokio::test]
    async fn test_request_json() {
        use serde::Deserialize;

        #[derive(Deserialize, Debug, PartialEq)]
        struct User {
            name: String,
            email: String,
        }

        let req = create_test_request();
        let user: User = req.json().await.unwrap();

        assert_eq!(user.name, "John");
        assert_eq!(user.email, "john@example.com");
    }

    #[tokio::test]
    async fn test_request_json_invalid() {
        let req = Request::new(
            Method::POST,
            "/api/users".parse().unwrap(),
            Version::HTTP_11,
            Query::new(),
            Headers::new(),
            Bytes::from("invalid json"),
        );

        let result: Result<serde_json::Value, _> = req.json().await;
        assert!(result.is_err());
    }

    #[tokio::test]
    async fn test_request_text() {
        let req = Request::new(
            Method::POST,
            "/api/echo".parse().unwrap(),
            Version::HTTP_11,
            Query::new(),
            Headers::new(),
            Bytes::from("Hello, World!"),
        );

        let text = req.text().await.unwrap();
        assert_eq!(text, "Hello, World!");

        // Test caching - second call should return cached value
        let text2 = req.text().await.unwrap();
        assert_eq!(text2, "Hello, World!");
    }

    #[tokio::test]
    async fn test_request_form() {
        let req = Request::new(
            Method::POST,
            "/api/login".parse().unwrap(),
            Version::HTTP_11,
            Query::new(),
            Headers::new(),
            Bytes::from("email=john%40example.com&password=secret123"),
        );

        let form = req.form().await.unwrap();
        assert_eq!(form.get("email"), Some(&"john@example.com".to_string()));
        assert_eq!(form.get("password"), Some(&"secret123".to_string()));

        // Test caching
        let form2 = req.form().await.unwrap();
        assert_eq!(form2, form);
    }

    #[test]
    fn test_request_clone() {
        let req = create_test_request();
        let req2 = req.clone();

        // Both should have same data
        assert_eq!(req.method(), req2.method());
        assert_eq!(req.path(), req2.path());
        assert_eq!(req.uri(), req2.uri());

        // Arc reference count should be 2
        assert_eq!(Arc::strong_count(&req.inner), 2);
    }
}
