//! Response building utilities for server operations

use crate::http::{Body, HttpResponse};
use http::{HeaderMap, StatusCode};
use std::path::PathBuf;

/// Builder for HTTP responses with common server patterns
pub struct ResponseBuilder {
    status: StatusCode,
    headers: HeaderMap,
    body: Body,
}

impl ResponseBuilder {
    /// Create a new response builder
    pub fn new() -> Self {
        Self {
            status: StatusCode::OK,
            headers: HeaderMap::new(),
            body: Body::empty(),
        }
    }

    /// Set response status
    pub fn status(mut self, status: StatusCode) -> Self {
        self.status = status;
        self
    }

    /// Add a header
    pub fn header(mut self, name: &str, value: &str) -> Self {
        if let (Ok(n), Ok(v)) = (
            http::header::HeaderName::try_from(name),
            http::header::HeaderValue::try_from(value),
        ) {
            self.headers.insert(n, v);
        }
        self
    }

    /// Set Heeaders
    pub fn set_headers(mut self, headers: HeaderMap) -> Self {
        self.headers = headers;
        self
    }

    /// Set content type
    pub fn content_type(self, mime: &str) -> Self {
        self.header("content-type", mime)
    }

    /// Set response body
    pub fn body(mut self, body: Vec<u8>) -> Self {
        let len = body.len().to_string();
        self.body = Body::from_vec(body);
        self.header("content-length", &len)
    }

    /// Set response body from a file path (streamed).
    pub fn file(mut self, path: impl Into<PathBuf>) -> Self {
        self.body = Body::from_file(path);
        self
    }

    /// Build with JSON body
    pub fn json(self, json: &str) -> Self {
        self.content_type("application/json")
            .body(json.as_bytes().to_vec())
    }

    /// Build with text body
    pub fn text(self, text: &str) -> Self {
        self.content_type("text/plain; charset=utf-8")
            .body(text.as_bytes().to_vec())
    }

    /// Build with HTML body
    pub fn html(self, html: &str) -> Self {
        self.content_type("text/html; charset=utf-8")
            .body(html.as_bytes().to_vec())
    }

    /// Build the final response
    pub fn build(self) -> HttpResponse {
        HttpResponse {
            status: self.status,
            headers: self.headers,
            body: self.body,
            version: http::Version::HTTP_11,
        }
    }

    /// Create a 200 OK response
    pub fn ok() -> Self {
        Self::new().status(StatusCode::OK)
    }

    /// Create a 404 Not Found response
    pub fn not_found() -> Self {
        Self::new().status(StatusCode::NOT_FOUND).text("Not Found")
    }

    /// Create a 500 Internal Server Error response
    pub fn internal_error() -> Self {
        Self::new()
            .status(StatusCode::INTERNAL_SERVER_ERROR)
            .text("Internal Server Error")
    }

    /// Create a 400 Bad Request response
    pub fn bad_request() -> Self {
        Self::new()
            .status(StatusCode::BAD_REQUEST)
            .text("Bad Request")
    }

    /// Create a 503 Service Unavailable response
    pub fn service_unavailable() -> Self {
        Self::new()
            .status(StatusCode::SERVICE_UNAVAILABLE)
            .text("Service Unavailable")
    }
}

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

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

    #[test]
    fn test_response_builder() {
        let response = ResponseBuilder::new()
            .status(StatusCode::OK)
            .json(r#"{"status":"ok"}"#)
            .build();

        assert_eq!(response.status, StatusCode::OK);
        assert!(!response.body.is_empty());
    }

    #[test]
    fn test_not_found() {
        let response = ResponseBuilder::not_found().build();
        assert_eq!(response.status, StatusCode::NOT_FOUND);
    }

    #[test]
    fn test_body_sets_content_length() {
        let response = ResponseBuilder::new().body(b"hello".to_vec()).build();
        assert_eq!(
            response
                .headers
                .get("content-length")
                .and_then(|v| v.to_str().ok()),
            Some("5")
        );
        assert_eq!(response.body.as_bytes().unwrap().as_ref(), b"hello");
    }

    #[test]
    fn test_content_type_helper() {
        let response = ResponseBuilder::new()
            .content_type("application/octet-stream")
            .body(vec![1, 2, 3])
            .build();
        assert_eq!(
            response
                .headers
                .get("content-type")
                .and_then(|v| v.to_str().ok()),
            Some("application/octet-stream")
        );
    }

    #[test]
    fn test_json_text_html_helpers() {
        let json = ResponseBuilder::new().json(r#"{"ok":true}"#).build();
        assert_eq!(
            json.headers
                .get("content-type")
                .and_then(|v| v.to_str().ok()),
            Some("application/json")
        );

        let text = ResponseBuilder::new().text("hello").build();
        assert_eq!(
            text.headers
                .get("content-type")
                .and_then(|v| v.to_str().ok()),
            Some("text/plain; charset=utf-8")
        );

        let html = ResponseBuilder::new().html("<h1>x</h1>").build();
        assert_eq!(
            html.headers
                .get("content-type")
                .and_then(|v| v.to_str().ok()),
            Some("text/html; charset=utf-8")
        );
    }

    #[test]
    fn test_set_headers_replaces_existing_map() {
        let mut headers = HeaderMap::new();
        headers.insert("x-test", "yes".parse().expect("header value"));

        let response = ResponseBuilder::new().set_headers(headers).build();
        assert_eq!(
            response.headers.get("x-test").and_then(|v| v.to_str().ok()),
            Some("yes")
        );
    }

    #[test]
    fn test_status_helpers() {
        assert_eq!(ResponseBuilder::ok().build().status, StatusCode::OK);
        assert_eq!(
            ResponseBuilder::bad_request().build().status,
            StatusCode::BAD_REQUEST
        );
        assert_eq!(
            ResponseBuilder::internal_error().build().status,
            StatusCode::INTERNAL_SERVER_ERROR
        );
        assert_eq!(
            ResponseBuilder::service_unavailable().build().status,
            StatusCode::SERVICE_UNAVAILABLE
        );
    }
}
