use crate::error::RariError;
use crate::server::ServerState;
use axum::{
    body::Bytes,
    extract::State,
    http::{HeaderMap, StatusCode, header},
    response::{IntoResponse, Json, Response},
};
use cow_utils::CowUtils;
use rustc_hash::FxHashMap;
use serde::{Deserialize, Serialize};
use serde_json::Value as JsonValue;
use tracing::error;

#[cfg(test)]
mod tests;

const MAX_BOUND_ARGS: usize = 1000;
const MAX_BIGINT_DIGITS: usize = 300;

#[derive(Debug, Clone)]
pub struct ValidationConfig {
    pub max_depth: usize,
    pub max_string_length: usize,
    pub max_array_length: usize,
    pub max_object_keys: usize,
    pub allow_special_numbers: bool,
    pub max_total_elements: usize,
}

impl Default for ValidationConfig {
    fn default() -> Self {
        Self {
            max_depth: 10,
            max_string_length: 10_000,
            max_array_length: 1_000,
            max_object_keys: 100,
            allow_special_numbers: false,
            max_total_elements: 1_000_000,
        }
    }
}

impl ValidationConfig {
    pub fn development() -> Self {
        Self {
            max_depth: 20,
            max_string_length: 50_000,
            max_array_length: 5_000,
            max_object_keys: 500,
            allow_special_numbers: false,
            max_total_elements: 5_000_000,
        }
    }

    pub fn production() -> Self {
        Self::default()
    }
}

#[derive(Debug)]
struct ValidationContext {
    total_elements: usize,
    has_fork: bool,
}

impl ValidationContext {
    fn new() -> Self {
        Self { total_elements: 0, has_fork: false }
    }

    fn bump_count(&mut self, count: usize, config: &ValidationConfig) -> Result<(), RariError> {
        self.total_elements = self.total_elements.saturating_add(count);

        if self.total_elements > config.max_total_elements && self.has_fork {
            return Err(RariError::bad_request(format!(
                "Maximum array nesting exceeded: {} > {}. Large nested arrays can be dangerous. Try adding intermediate objects.",
                self.total_elements, config.max_total_elements
            )));
        }
        Ok(())
    }
}

#[derive(Debug, Deserialize)]
pub struct ServerActionRequest {
    pub id: String,
    pub export_name: String,
    pub args: Vec<JsonValue>,
}

#[derive(Debug, Serialize)]
pub struct ServerActionResponse {
    pub success: bool,
    pub result: Option<JsonValue>,
    pub error: Option<String>,
    pub redirect: Option<String>,
}

fn effective_port(url: &url::Url) -> u16 {
    url.port().unwrap_or_else(|| match url.scheme() {
        "https" => 443,
        "http" => 80,
        _ => 0,
    })
}

fn normalize_origin(url: &url::Url) -> (String, String, u16) {
    (url.scheme().to_string(), url.host_str().unwrap_or("").to_string(), effective_port(url))
}

fn check_origin(headers: &HeaderMap, allowed_origins: &[String]) -> Result<(), StatusCode> {
    if allowed_origins.is_empty() {
        let host = headers.get("host").and_then(|v| v.to_str().ok()).ok_or_else(|| {
            error!("Missing host header in server action request");
            StatusCode::BAD_REQUEST
        })?;

        let scheme = headers
            .get("x-forwarded-proto")
            .or_else(|| headers.get("x-forwarded-protocol"))
            .and_then(|v| v.to_str().ok())
            .unwrap_or_else(|| {
                tracing::debug!(
                    "No x-forwarded-proto header; defaulting to http for origin validation"
                );
                "http"
            });

        let server_origin_str = format!("{}://{}", scheme, host);
        let server_origin_url = url::Url::parse(&server_origin_str).map_err(|e| {
            error!("Failed to parse server origin: {}", e);
            StatusCode::INTERNAL_SERVER_ERROR
        })?;
        let server_origin_tuple = normalize_origin(&server_origin_url);

        if let Some(origin) = headers.get("origin").and_then(|v| v.to_str().ok()) {
            if let Ok(origin_url) = url::Url::parse(origin) {
                let origin_tuple = normalize_origin(&origin_url);

                if origin_tuple == server_origin_tuple {
                    return Ok(());
                }
            }
            error!("Origin mismatch: origin={}, server_origin={}", origin, server_origin_str);
            return Err(StatusCode::FORBIDDEN);
        }

        if let Some(referer) = headers.get("referer").and_then(|v| v.to_str().ok()) {
            if let Ok(referer_url) = url::Url::parse(referer) {
                let referer_tuple = normalize_origin(&referer_url);

                if referer_tuple == server_origin_tuple {
                    return Ok(());
                }
                error!(
                    "Referer mismatch: referer_origin={}://{}:{}, server_origin={}",
                    referer_tuple.0, referer_tuple.1, referer_tuple.2, server_origin_str
                );
            } else {
                error!("Invalid referer header: failed to parse");
            }
            return Err(StatusCode::FORBIDDEN);
        }

        error!("Missing origin and referer headers in server action request");
        return Err(StatusCode::FORBIDDEN);
    }

    if let Some(origin) = headers.get("origin").and_then(|v| v.to_str().ok()) {
        if !crate::server::utils::http_utils::is_origin_allowed(origin, allowed_origins) {
            error!("Invalid origin: {}", origin);
            return Err(StatusCode::FORBIDDEN);
        }
        return Ok(());
    }

    if let Some(referer) = headers.get("referer").and_then(|v| v.to_str().ok()) {
        if let Ok(referer_url) = url::Url::parse(referer) {
            let (scheme, host, port) = normalize_origin(&referer_url);
            let referer_origin =
                if (scheme == "http" && port == 80) || (scheme == "https" && port == 443) {
                    format!("{}://{}", scheme, host)
                } else {
                    format!("{}://{}:{}", scheme, host, port)
                };
            if crate::server::utils::http_utils::is_origin_allowed(&referer_origin, allowed_origins)
            {
                return Ok(());
            }
            error!("Invalid referer origin: {}", referer_origin);
        } else {
            error!("Invalid referer header: failed to parse origin");
        }
        return Err(StatusCode::FORBIDDEN);
    }

    error!("Missing Origin and Referer headers with non-empty allowed_origins");
    Err(StatusCode::FORBIDDEN)
}

pub async fn handle_server_action(
    State(state): State<ServerState>,
    headers: HeaderMap,
    body: Bytes,
) -> Result<Response, StatusCode> {
    let allowed_origins = state.config.action_origins();
    check_origin(&headers, &allowed_origins)?;

    let request: ServerActionRequest = match serde_json::from_slice(&body) {
        Ok(req) => req,
        Err(e) => {
            error!("Failed to parse server action request: {}", e);
            let mut response = Json(ServerActionResponse {
                success: false,
                result: None,
                error: Some("Invalid request format".to_string()),
                redirect: None,
            })
            .into_response();
            response.headers_mut().insert(
                header::CACHE_CONTROL,
                "no-store, no-cache, must-revalidate, private"
                    .parse()
                    .expect("Valid cache-control header"),
            );
            return Ok(response);
        }
    };

    if request.args.len() > MAX_BOUND_ARGS {
        error!("Too many server function arguments: {} > {}", request.args.len(), MAX_BOUND_ARGS);
        let mut response = Json(ServerActionResponse {
            success: false,
            result: None,
            error: Some(format!(
                "Server Function has too many bound arguments. Received {} but the limit is {}.",
                request.args.len(),
                MAX_BOUND_ARGS
            )),
            redirect: None,
        })
        .into_response();
        response.headers_mut().insert(
            header::CACHE_CONTROL,
            "no-store, no-cache, must-revalidate, private"
                .parse()
                .expect("Valid cache-control header"),
        );
        *response.status_mut() = StatusCode::BAD_REQUEST;
        return Ok(response);
    }

    if is_reserved_export_name(&request.export_name) {
        error!("Attempted to call reserved export name: {}", request.export_name);
        let mut response = Json(ServerActionResponse {
            success: false,
            result: None,
            error: Some(format!(
                "Invalid export name '{}': reserved for internal use",
                request.export_name
            )),
            redirect: None,
        })
        .into_response();
        response.headers_mut().insert(
            header::CACHE_CONTROL,
            "no-store, no-cache, must-revalidate, private"
                .parse()
                .expect("Valid cache-control header"),
        );
        *response.status_mut() = StatusCode::BAD_REQUEST;
        return Ok(response);
    }

    let validation_config = if state.config.is_development() {
        ValidationConfig::development()
    } else {
        ValidationConfig::production()
    };

    let sanitized_args = match validate_and_sanitize_args(&request.args, &validation_config) {
        Ok(args) => args,
        Err(e) => {
            error!("Input validation failed: {}", e);
            let mut response = Json(ServerActionResponse {
                success: false,
                result: None,
                error: Some(format!("Input validation failed: {}", e)),
                redirect: None,
            })
            .into_response();
            response.headers_mut().insert(
                header::CACHE_CONTROL,
                "no-store, no-cache, must-revalidate, private"
                    .parse()
                    .expect("Valid cache-control header"),
            );
            *response.status_mut() = StatusCode::BAD_REQUEST;
            return Ok(response);
        }
    };

    let cookie_header =
        headers.get(header::COOKIE).and_then(|v| v.to_str().ok()).map(|s| s.to_string());

    let request_context = std::sync::Arc::new(
        crate::server::middleware::request_context::RequestContext::new(
            "/_rari/action".to_string(),
        )
        .with_cookies(cookie_header),
    );

    let renderer = state.renderer.lock().await;

    let result = renderer
        .runtime
        .execute_with_request_context(request_context.clone(), async {
            renderer
                .execute_server_function(&request.id, &request.export_name, &sanitized_args)
                .await
        })
        .await;

    match result {
        Ok(value) => {
            let redirect_config = state.config.redirect_config();
            let redirect = extract_redirect_from_result(&value, &redirect_config);

            if let Some(ref redirect_url) = redirect {
                let redirect_path = if let Ok(parsed) = url::Url::parse(redirect_url) {
                    parsed.path().to_string()
                } else if redirect_url.starts_with('/') {
                    redirect_url.split('?').next().unwrap_or(redirect_url).to_string()
                } else {
                    redirect_url.clone()
                };

                state.response_cache.invalidate_by_tag(&redirect_path).await;
                state.html_cache.remove(&redirect_path);
            }

            let response =
                ServerActionResponse { success: true, result: Some(value), error: None, redirect };

            let mut response = Json(response).into_response();
            response.headers_mut().insert(
                header::CACHE_CONTROL,
                "no-store, no-cache, must-revalidate, private"
                    .parse()
                    .expect("Valid cache-control header"),
            );

            append_pending_cookies(response.headers_mut(), &request_context.pending_cookies);

            Ok(response)
        }
        Err(e) => {
            error!("Server action execution failed: {}", e);
            let mut response = Json(ServerActionResponse {
                success: false,
                result: None,
                error: Some(e.to_string()),
                redirect: None,
            })
            .into_response();
            response.headers_mut().insert(
                header::CACHE_CONTROL,
                "no-store, no-cache, must-revalidate, private"
                    .parse()
                    .expect("Valid cache-control header"),
            );
            append_pending_cookies(response.headers_mut(), &request_context.pending_cookies);
            Ok(response)
        }
    }
}

pub async fn handle_form_action(
    State(state): State<ServerState>,
    headers: HeaderMap,
    body: Bytes,
) -> Result<Response, StatusCode> {
    let allowed_origins = state.config.action_origins();
    check_origin(&headers, &allowed_origins)?;

    let form_data = match parse_form_data(&body) {
        Ok(data) => data,
        Err(e) => {
            error!("Failed to parse form data: {}", e);
            return Err(StatusCode::BAD_REQUEST);
        }
    };

    let action_id = form_data.get("__action_id").ok_or(StatusCode::BAD_REQUEST)?;
    let export_name = form_data.get("__export_name").ok_or(StatusCode::BAD_REQUEST)?;

    if is_reserved_export_name(export_name) {
        error!("Attempted to call reserved export name in form action: {}", export_name);
        return Err(StatusCode::BAD_REQUEST);
    }

    let args = convert_form_data_to_args(&form_data);

    let validation_config = if state.config.is_development() {
        ValidationConfig::development()
    } else {
        ValidationConfig::production()
    };

    let sanitized_args = match validate_and_sanitize_args(&args, &validation_config) {
        Ok(args) => args,
        Err(e) => {
            error!("Form action input validation failed: {}", e);
            return Err(StatusCode::BAD_REQUEST);
        }
    };

    let cookie_header =
        headers.get(header::COOKIE).and_then(|v| v.to_str().ok()).map(|s| s.to_string());

    let request_context = std::sync::Arc::new(
        crate::server::middleware::request_context::RequestContext::new(
            "/_rari/action".to_string(),
        )
        .with_cookies(cookie_header),
    );

    let renderer = state.renderer.lock().await;

    let result = renderer
        .runtime
        .execute_with_request_context(request_context.clone(), async {
            renderer.execute_server_function(action_id, export_name, &sanitized_args).await
        })
        .await;

    match result {
        Ok(value) => {
            let redirect_config = state.config.redirect_config();
            if let Some(redirect_url) = extract_redirect_from_result(&value, &redirect_config) {
                let redirect_path = if let Ok(parsed) = url::Url::parse(&redirect_url) {
                    parsed.path().to_string()
                } else if redirect_url.starts_with('/') {
                    redirect_url.split('?').next().unwrap_or(&redirect_url).to_string()
                } else {
                    redirect_url.clone()
                };

                state.response_cache.invalidate_by_tag(&redirect_path).await;
                state.html_cache.remove(&redirect_path);

                let mut redirect_response = Response::builder()
                    .status(StatusCode::SEE_OTHER)
                    .header("Location", redirect_url)
                    .header("Cache-Control", "no-store, no-cache, must-revalidate")
                    .body("".into())
                    .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;

                append_pending_cookies(
                    redirect_response.headers_mut(),
                    &request_context.pending_cookies,
                );

                return Ok(redirect_response);
            }

            let (redirect_url, redirect_path_opt) = if let Some(referer) =
                headers.get("referer").and_then(|h| h.to_str().ok())
            {
                if let Ok(parsed) = url::Url::parse(referer) {
                    let referer_tuple = normalize_origin(&parsed);

                    let server_origin_tuple_opt = {
                        let host = headers.get("host").and_then(|v| v.to_str().ok()).unwrap_or("");
                        let scheme = headers
                            .get("x-forwarded-proto")
                            .or_else(|| headers.get("x-forwarded-protocol"))
                            .and_then(|v| v.to_str().ok())
                            .unwrap_or("http");
                        let server_origin_str = format!("{}://{}", scheme, host);

                        url::Url::parse(&server_origin_str).ok().map(|u| normalize_origin(&u))
                    };

                    let (is_same_origin, is_allowed) = if allowed_origins.is_empty() {
                        if let Some(server_origin_tuple) = &server_origin_tuple_opt {
                            let is_same = referer_tuple == *server_origin_tuple;
                            (is_same, is_same)
                        } else {
                            (false, false)
                        }
                    } else {
                        let is_same =
                            server_origin_tuple_opt.as_ref().is_some_and(|t| referer_tuple == *t);

                        let referer_origin = format!(
                            "{}://{}:{}",
                            referer_tuple.0, referer_tuple.1, referer_tuple.2
                        );
                        let allowed = crate::server::utils::http_utils::is_origin_allowed(
                            &referer_origin,
                            &allowed_origins,
                        );
                        (is_same, allowed)
                    };

                    if is_allowed {
                        if is_same_origin {
                            let path_and_query = if let Some(query) = parsed.query() {
                                format!("{}?{}", parsed.path(), query)
                            } else {
                                parsed.path().to_string()
                            };
                            (path_and_query.clone(), Some(parsed.path().to_string()))
                        } else {
                            (referer.to_string(), None)
                        }
                    } else {
                        ("/".to_string(), None)
                    }
                } else {
                    ("/".to_string(), None)
                }
            } else {
                ("/".to_string(), None)
            };

            if let Some(redirect_path) = redirect_path_opt {
                state.response_cache.invalidate_by_tag(&redirect_path).await;
                state.html_cache.remove(&redirect_path);
            }

            let mut redirect_response = Response::builder()
                .status(StatusCode::SEE_OTHER)
                .header("Location", redirect_url)
                .header("Cache-Control", "no-store, no-cache, must-revalidate")
                .body("".into())
                .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;

            append_pending_cookies(
                redirect_response.headers_mut(),
                &request_context.pending_cookies,
            );

            Ok(redirect_response)
        }
        Err(e) => {
            error!("Form action execution failed: {}", e);
            let mut response = StatusCode::INTERNAL_SERVER_ERROR.into_response();
            append_pending_cookies(response.headers_mut(), &request_context.pending_cookies);
            Ok(response)
        }
    }
}

pub(crate) fn validate_redirect_url(
    url: &str,
    config: &crate::server::config::RedirectConfig,
) -> Result<String, RariError> {
    if config.allow_relative && url.starts_with('/') && !url.starts_with("//") {
        return Ok(url.to_string());
    }

    let parsed =
        url::Url::parse(url).map_err(|_| RariError::bad_request("Invalid redirect URL format"))?;

    if parsed.scheme() != "http" && parsed.scheme() != "https" {
        return Err(RariError::bad_request("Invalid redirect scheme: only http/https allowed"));
    }

    if let Some(host) = parsed.host_str() {
        let is_allowed = config.allowed_hosts.iter().any(|allowed| {
            if config.allow_subdomains {
                host == allowed || host.ends_with(&format!(".{}", allowed))
            } else {
                host == allowed
            }
        });

        if !is_allowed {
            return Err(RariError::bad_request("Redirect to untrusted host not allowed"));
        }
    } else {
        return Err(RariError::bad_request("Invalid redirect URL: missing host"));
    }

    Ok(url.to_string())
}

fn extract_redirect_from_result(
    result: &JsonValue,
    config: &crate::server::config::RedirectConfig,
) -> Option<String> {
    if let Some(redirect) = result.get("redirect") {
        if let Some(url) = redirect.as_str() {
            return validate_redirect_url(url, config).ok();
        }
        if let Some(obj) = redirect.as_object()
            && let Some(destination) = obj.get("destination").and_then(|d| d.as_str())
        {
            return validate_redirect_url(destination, config).ok();
        }
    }
    None
}

fn parse_form_data(body: &Bytes) -> Result<FxHashMap<String, String>, RariError> {
    let body_str = std::str::from_utf8(body)
        .map_err(|_| RariError::bad_request("Invalid UTF-8 in form data"))?;

    let mut form_data = FxHashMap::default();

    for pair in body_str.split('&') {
        if let Some((key, value)) = pair.split_once('=') {
            let key = percent_decode(key)
                .map_err(|_| RariError::bad_request("Invalid URL encoding in form key"))?;
            let value = percent_decode(value)
                .map_err(|_| RariError::bad_request("Invalid URL encoding in form value"))?;
            form_data.insert(key, value);
        }
    }

    Ok(form_data)
}

fn convert_form_data_to_args(form_data: &FxHashMap<String, String>) -> Vec<JsonValue> {
    let mut form_entries = serde_json::Map::new();

    for (key, value) in form_data {
        if key.starts_with("__") {
            continue;
        }
        form_entries.insert(key.clone(), JsonValue::String(value.clone()));
    }

    let form_data_object = JsonValue::Object(form_entries);

    vec![JsonValue::Null, form_data_object]
}

fn percent_decode(input: &str) -> Result<String, RariError> {
    let mut bytes = Vec::new();
    let mut chars = input.chars();

    while let Some(ch) = chars.next() {
        if ch == '%' {
            let hex1 =
                chars.next().ok_or_else(|| RariError::bad_request("Invalid percent encoding"))?;
            let hex2 =
                chars.next().ok_or_else(|| RariError::bad_request("Invalid percent encoding"))?;

            let hex_str = format!("{}{}", hex1, hex2);
            let byte = u8::from_str_radix(&hex_str, 16)
                .map_err(|_| RariError::bad_request("Invalid hex in percent encoding"))?;

            bytes.push(byte);
        } else if ch == '+' {
            bytes.push(b' ');
        } else {
            let mut buf = [0u8; 4];
            for b in ch.encode_utf8(&mut buf).bytes() {
                bytes.push(b);
            }
        }
    }

    String::from_utf8(bytes)
        .map_err(|_| RariError::bad_request("Invalid UTF-8 in percent-decoded data"))
}

pub(crate) fn validate_and_sanitize_args(
    args: &[JsonValue],
    config: &ValidationConfig,
) -> Result<Vec<JsonValue>, RariError> {
    let mut context = ValidationContext::new();
    args.iter().map(|arg| validate_and_sanitize_value(arg, config, 0, &mut context)).collect()
}

fn validate_and_sanitize_value(
    value: &JsonValue,
    config: &ValidationConfig,
    depth: usize,
    context: &mut ValidationContext,
) -> Result<JsonValue, RariError> {
    if depth > config.max_depth {
        return Err(RariError::bad_request(format!(
            "Maximum nesting depth exceeded: {} > {}",
            depth, config.max_depth
        )));
    }

    match value {
        JsonValue::String(s) => {
            if s.len() > config.max_string_length {
                return Err(RariError::bad_request(format!(
                    "String too long: {} > {}",
                    s.len(),
                    config.max_string_length
                )));
            }

            context.bump_count(s.len(), config)?;

            Ok(value.clone())
        }
        JsonValue::Number(n) => {
            if let Some(f) = n.as_f64()
                && !config.allow_special_numbers
                && !f.is_finite()
            {
                return Err(RariError::bad_request(
                    "Invalid number: Infinity or NaN not allowed".to_string(),
                ));
            }

            if let Some(f) = n.as_f64() {
                let abs_f = f.abs();
                if abs_f > 1e100 {
                    let estimated_digits =
                        if abs_f == 0.0 { 1 } else { (abs_f.log10().floor() as usize) + 1 };

                    if estimated_digits > MAX_BIGINT_DIGITS {
                        return Err(RariError::bad_request(format!(
                            "Number too large. Estimated {} digits but the limit is {}.",
                            estimated_digits, MAX_BIGINT_DIGITS
                        )));
                    }
                }
            }

            Ok(value.clone())
        }
        JsonValue::Array(arr) => {
            if arr.len() > config.max_array_length {
                return Err(RariError::bad_request(format!(
                    "Array too large: {} > {}",
                    arr.len(),
                    config.max_array_length
                )));
            }

            if arr.len() > 1 {
                context.has_fork = true;
            }

            context.bump_count(arr.len() + 1, config)?;

            let validated: Result<Vec<_>, _> = arr
                .iter()
                .map(|v| validate_and_sanitize_value(v, config, depth + 1, context))
                .collect();

            Ok(JsonValue::Array(validated?))
        }
        JsonValue::Object(obj) => {
            if obj.len() > config.max_object_keys {
                return Err(RariError::bad_request(format!(
                    "Too many object keys: {} > {}",
                    obj.len(),
                    config.max_object_keys
                )));
            }

            let mut sanitized = serde_json::Map::new();
            for (key, val) in obj {
                if is_dangerous_property(key) {
                    continue;
                }

                let validated_val = validate_and_sanitize_value(val, config, depth + 1, context)?;
                sanitized.insert(key.clone(), validated_val);
            }

            Ok(JsonValue::Object(sanitized))
        }
        JsonValue::Bool(_) | JsonValue::Null => Ok(value.clone()),
    }
}

pub(crate) fn is_dangerous_property(key: &str) -> bool {
    matches!(
        key,
        "__proto__"
            | "constructor"
            | "prototype"
            | "__defineGetter__"
            | "__defineSetter__"
            | "__lookupGetter__"
            | "__lookupSetter__"
    )
}

pub(crate) fn is_reserved_export_name(name: &str) -> bool {
    matches!(
        name,
        "then"
            | "catch"
            | "finally"
            | "toString"
            | "valueOf"
            | "toLocaleString"
            | "constructor"
            | "Symbol"
            | "@@iterator"
            | "@@toStringTag"
    )
}

fn append_pending_cookies(
    headers: &mut axum::http::HeaderMap,
    pending_cookies: &dashmap::DashMap<
        crate::server::middleware::request_context::PendingCookieKey,
        crate::server::middleware::request_context::PendingCookie,
    >,
) {
    for entry in pending_cookies.iter() {
        let cookie = entry.value();
        match build_set_cookie_header(cookie) {
            Ok(set_cookie_value) => match set_cookie_value.parse() {
                Ok(header_value) => {
                    headers.append(header::SET_COOKIE, header_value);
                }
                Err(_) => {
                    tracing::warn!(
                        "Failed to parse Set-Cookie header for '{}': invalid header value",
                        cookie.name
                    );
                }
            },
            Err(()) => {
                tracing::warn!("Skipped invalid cookie '{}': failed validation", cookie.name);
            }
        }
    }
}

pub(crate) fn is_valid_cookie_name(s: &str) -> bool {
    !s.is_empty() && s.bytes().all(|b| b > 32 && b < 127 && !b"()<>@,;:\\\"/[]?={} \t".contains(&b))
}

pub(crate) fn is_valid_cookie_value(s: &str) -> bool {
    s.bytes().all(|b| matches!(b, 0x21 | 0x23..=0x2B | 0x2D..=0x3A | 0x3C..=0x5B | 0x5D..=0x7E))
}

pub(crate) fn is_valid_attr_value(s: &str) -> bool {
    !s.is_empty() && s.is_ascii() && s.bytes().all(|b| b >= 32 && b != b';' && b != 127)
}

pub(crate) fn build_set_cookie_header(
    cookie: &crate::server::middleware::request_context::PendingCookie,
) -> Result<String, ()> {
    if !is_valid_cookie_name(&cookie.name) {
        return Err(());
    }
    if !is_valid_cookie_value(&cookie.value) {
        return Err(());
    }

    let path = cookie.path.as_deref().unwrap_or("/");
    if !is_valid_attr_value(path) {
        return Err(());
    }

    let mut header = format!("{}={}", cookie.name, cookie.value);
    header.push_str(&format!("; Path={}", path));

    if let Some(domain) = &cookie.domain {
        if !is_valid_attr_value(domain) {
            return Err(());
        }
        header.push_str(&format!("; Domain={}", domain));
    }
    if let Some(expires) = &cookie.expires {
        if !is_valid_attr_value(expires) {
            return Err(());
        }
        header.push_str(&format!("; Expires={}", expires));
    }
    if let Some(max_age) = cookie.max_age {
        header.push_str(&format!("; Max-Age={}", max_age));
    }
    let normalized_same_site =
        cookie.same_site.as_deref().map(|value| value.cow_to_ascii_lowercase());
    if normalized_same_site.as_deref() == Some("none") && !cookie.secure {
        return Err(());
    }
    if cookie.partitioned && !cookie.secure {
        return Err(());
    }
    if cookie.http_only {
        header.push_str("; HttpOnly");
    }
    if cookie.secure {
        header.push_str("; Secure");
    }
    if let Some(same_site) = normalized_same_site.as_deref() {
        let serialized_same_site = match same_site {
            "strict" => "Strict",
            "lax" => "Lax",
            "none" => "None",
            _ => return Err(()),
        };
        header.push_str(&format!("; SameSite={}", serialized_same_site));
    }
    if let Some(priority) = &cookie.priority {
        match priority.cow_to_ascii_lowercase().as_ref() {
            "low" => header.push_str("; Priority=Low"),
            "medium" => header.push_str("; Priority=Medium"),
            "high" => header.push_str("; Priority=High"),
            _ => return Err(()),
        }
    }
    if cookie.partitioned {
        header.push_str("; Partitioned");
    }
    Ok(header)
}
