//! HTTP Response building and serialization

use crate::http::Body;
use http::{HeaderMap, StatusCode};

#[derive(Debug)]
pub struct HttpResponse {
    pub status: StatusCode,
    pub headers: HeaderMap,
    pub body: Body,
    pub version: http::Version,
}

//####
//##        HTTTP RESPONSE IMPLEMENTATION
//#####
impl HttpResponse {
    pub fn new() -> Self {
        Self {
            status: StatusCode::OK,
            headers: HeaderMap::new(),
            body: Body::empty(),
            version: http::Version::HTTP_11,
        }
    }

    /// Get the HTTP version string
    pub fn version(&self) -> &'static str {
        match self.version {
            http::Version::HTTP_10 => "HTTP/1.0",
            http::Version::HTTP_11 => "HTTP/1.1",
            http::Version::HTTP_2 => "HTTP/2.0",
            http::Version::HTTP_3 => "HTTP/3.0",
            _ => "HTTP/1.1",
        }
    }

    /// Serialize response to bytes
    pub fn to_bytes(&self) -> Vec<u8> {
        let mut response = Vec::new();

        // Status line
        response.extend_from_slice(
            format!(
                "{} {} {}\r\n",
                self.version(),
                self.status.as_u16(),
                self.status.canonical_reason().unwrap_or("Unknown")
            )
            .as_bytes(),
        );

        // Headers
        for (name, value) in &self.headers {
            response.extend_from_slice(name.as_str().as_bytes());
            response.extend_from_slice(b": ");
            response.extend_from_slice(value.as_bytes());
            response.extend_from_slice(b"\r\n");
        }

        // Body (buffered only)
        response.extend_from_slice(b"\r\n");
        if let Some(bytes) = self.body.as_bytes() {
            response.extend_from_slice(bytes);
        }

        response
    }

    /// Serialize response with a forced HTTP version (e.g. to match request)
    pub fn to_bytes_with_version(&self, version: http::Version) -> Vec<u8> {
        let mut tmp = HttpResponse {
            status: self.status,
            headers: self.headers.clone(),
            body: Body::from_bytes(self.body.as_bytes().cloned().unwrap_or_default()),
            version,
        };
        tmp.to_bytes()
    }

    /// Known body length (buffered only)
    pub fn body_len(&self) -> Option<usize> {
        self.body.len()
    }
}

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

    #[test]
    fn test_new_defaults() {
        let response = HttpResponse::new();
        assert_eq!(response.status, StatusCode::OK);
        assert_eq!(response.version, http::Version::HTTP_11);
        assert!(response.headers.is_empty());
        assert!(response.body.is_empty());
    }

    #[test]
    fn test_to_bytes_serialization() {
        let mut response = HttpResponse::new();
        response.status = StatusCode::CREATED;
        response
            .headers
            .insert("content-type", "text/plain".parse().expect("header value"));
        response.body = Body::from_vec(b"hello".to_vec());

        let raw = String::from_utf8(response.to_bytes()).expect("valid utf8");
        assert!(raw.starts_with("HTTP/1.1 201 Created\r\n"));
        assert!(raw.contains("content-type: text/plain\r\n"));
        assert!(raw.ends_with("\r\nhello"));
    }

    #[test]
    fn test_to_bytes_with_version_override() {
        let response = HttpResponse::new();
        let raw = String::from_utf8(response.to_bytes_with_version(http::Version::HTTP_10))
            .expect("valid utf8");
        assert!(raw.starts_with("HTTP/1.0 200 OK\r\n"));
    }
}
