//! Request handling and processing logic

use crate::config::ProtocolType;
use crate::errors::{FalcornError, Result};
use crate::http::{HttpRequest, HttpResponse};
use crate::python::app::ApplicationInstance;
use http::StatusCode;
use std::sync::Arc;

/// Handles HTTP request processing
#[derive(Clone)]
pub struct RequestHandler {
    app: Arc<ApplicationInstance>,
    protocol: ProtocolType,
    strict_http: bool,
}

impl RequestHandler {
    /// Create a new request handler
    pub fn new(app: Arc<ApplicationInstance>, protocol: ProtocolType, strict_http: bool) -> Self {
        Self {
            app,
            protocol,
            strict_http,
        }
    }

    /// Handle a request synchronously
    #[inline(always)]
    pub fn handle_sync(&self, request: &HttpRequest) -> Result<HttpResponse> {
        // Validate request
        self.validate_request(request)?;

        let response = match self.protocol {
            ProtocolType::Wsgi => self.app.call_wsgi(request)?,
            ProtocolType::Asgi => {
                return Err(FalcornError::runtime(
                    "ASGI requires async worker (sync handler used)",
                ));
            }
        };

        Ok(response)
    }

    /// Handle a request asynchronously
    #[inline(always)]
    pub async fn handle_async(&self, request: &HttpRequest) -> Result<HttpResponse> {
        // Validate request
        self.validate_request(request)?;

        let response = match self.protocol {
            ProtocolType::Wsgi => {
                // call_wsgi_async has issues for now; sync as workaround
                self.app.call_wsgi(request)?
            }
            ProtocolType::Asgi => self.app.call_asgi(request).await?,
        };

        Ok(response)
    }

    /// Validate incoming request
    #[inline(always)]
    fn validate_request(&self, request: &HttpRequest) -> Result<()> {
        // Check required headers
        if request.method.as_str() == "POST" || request.method.as_str() == "PUT" {
            if request.content_length().is_none() && request.body.is_some() {
                let is_chunked = request
                    .get_header_str("transfer-encoding")
                    .map(|v| v.to_ascii_lowercase().contains("chunked"))
                    .unwrap_or(false);
                if !is_chunked {
                    return Err(FalcornError::MissingHeader {
                        header: "Content-Length".to_string(),
                    });
                }
            }
        }

        // Validate HTTP version (allow HTTP/1.x, HTTP/2, HTTP/3)
        let ver = request.version_str();
        if !(ver.starts_with("HTTP/1.") || ver == "HTTP/2.0" || ver == "HTTP/3.0") {
            return Err(FalcornError::InvalidHttpVersion {
                version: ver.to_string(),
            });
        }

        if self.strict_http {
            if request.version == http::Version::HTTP_11 && request.get_header("host").is_none() {
                return Err(FalcornError::MissingHeader {
                    header: "Host".to_string(),
                });
            }

            let te_has_chunked = request
                .get_header("transfer-encoding")
                .map(|v| header_contains_token_case_insensitive(v, b"chunked"))
                .unwrap_or(false);

            let cl_raw = request.get_header("content-length");
            if let Some(raw) = cl_raw {
                let cl_str = std::str::from_utf8(raw)
                    .map_err(|_| FalcornError::http_parse("Invalid Content-Length encoding"))?;
                if cl_str.parse::<u64>().is_err() {
                    return Err(FalcornError::http_parse("Invalid Content-Length"));
                }
            }

            if cl_raw.is_some() && te_has_chunked {
                return Err(FalcornError::http_parse(
                    "Conflicting Content-Length and Transfer-Encoding",
                ));
            }
        }

        Ok(())
    }

    /// Handle request error
    pub fn handle_error(&self, error: FalcornError) -> HttpResponse {
        self.handle_error_with_request(error, None)
    }

    pub fn handle_error_with_request(
        &self,
        error: FalcornError,
        request: Option<&HttpRequest>,
    ) -> HttpResponse {
        use crate::http::response;

        let status_code = error.status_code();
        let prefer_json = request
            .and_then(|r| r.get_header("accept"))
            .map(|v| {
                header_contains_token_case_insensitive(v, b"application/json")
                    || header_contains_token_case_insensitive(v, b"+json")
            })
            .unwrap_or(true);

        if prefer_json {
            let error_message = format!("{{\"error\": \"{}\"}}", error);
            response()
                .status(StatusCode::from_u16(status_code).unwrap())
                .content_type("application/json")
                .body(error_message.into_bytes())
                .build()
        } else {
            let error_message = format!("Error: {}", error);
            response()
                .status(StatusCode::from_u16(status_code).unwrap())
                .content_type("text/plain; charset=utf-8")
                .body(error_message.into_bytes())
                .build()
        }
    }
}

#[inline(always)]
fn header_contains_token_case_insensitive(haystack: &[u8], needle: &[u8]) -> bool {
    if needle.is_empty() || haystack.len() < needle.len() {
        return false;
    }
    let n = needle.len();
    for i in 0..=haystack.len() - n {
        let mut matched = true;
        for j in 0..n {
            let a = haystack[i + j];
            let b = needle[j];
            if a.to_ascii_lowercase() != b.to_ascii_lowercase() {
                matched = false;
                break;
            }
        }
        if matched {
            return true;
        }
    }
    false
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::http::HttpMethod;
    use std::net::SocketAddr;

    #[test]
    fn test_validate_request() {
        let _request = HttpRequest {
            raw: bytes::Bytes::new(),
            method: HttpMethod::Get,
            path: bytes::Bytes::from_static(b"/test"),
            query_string: None,
            version: http::Version::HTTP_11,
            headers: Vec::new(),
            body: None,
            peer_addr: "127.0.0.1:8000".parse::<SocketAddr>().unwrap(),
        };

        // This would need a mock app
        // let handler = RequestHandler::new(app);
        // assert!(handler.validate_request(&request).is_ok());
    }
}
