//! Header-driven wildcard host inference policy.

use actix_web::http::header::{FORWARDED, HOST, HeaderMap};

use super::config::{wildcard_host_pattern, wildcard_host_routing_enabled};
use super::host_pattern::{extract_prefix_from_host, parse_public_host_route};

pub fn resolve_public_host_route(headers: &HeaderMap) -> Option<athena_dns::PublicHostRoute> {
    let wildcard_pattern = wildcard_host_pattern();
    candidate_host_headers(headers)
        .into_iter()
        .find_map(|host| parse_public_host_route(&host, &wildcard_pattern))
}

/// Returns a wildcard host prefix candidate when host routing should be applied.
pub fn infer_wildcard_host_prefix(headers: &HeaderMap) -> Option<String> {
    if !wildcard_host_routing_enabled() || should_skip_wildcard_host_inference(headers) {
        return None;
    }

    let wildcard_pattern = wildcard_host_pattern();
    candidate_host_headers(headers)
        .into_iter()
        .find_map(|host| extract_prefix_from_host(&host, &wildcard_pattern))
}

fn should_skip_wildcard_host_inference(headers: &HeaderMap) -> bool {
    has_non_empty_header(headers, "X-Athena-Client")
        || has_non_empty_header(headers, "X-JDBC-URL")
        || has_non_empty_header(headers, "x-jdbc-url")
        || has_non_empty_header(headers, "x-athena-jdbc-url")
        || has_non_empty_header(headers, "x-pg-uri")
}

fn has_non_empty_header(headers: &HeaderMap, name: &str) -> bool {
    header_value_as_trimmed_str(headers, name).is_some()
}

fn header_value_as_trimmed_str(headers: &HeaderMap, name: &str) -> Option<String> {
    headers
        .get(name)
        .and_then(|value| value.to_str().ok())
        .map(str::trim)
        .filter(|value| !value.is_empty())
        .map(str::to_string)
}

fn candidate_host_headers(headers: &HeaderMap) -> Vec<String> {
    let mut candidates = Vec::with_capacity(3);
    if let Some(host) = header_value_as_trimmed_str(headers, HOST.as_str()) {
        candidates.push(host);
    }
    if let Some(host) = first_forwarded_host(headers, "x-forwarded-host") {
        candidates.push(host);
    }
    if let Some(host) = forwarded_header_host(headers) {
        candidates.push(host);
    }
    candidates
}

fn first_forwarded_host(headers: &HeaderMap, name: &str) -> Option<String> {
    header_value_as_trimmed_str(headers, name)
        .and_then(|value| value.split(',').next().map(str::trim).map(str::to_string))
        .filter(|value| !value.is_empty())
}

fn forwarded_header_host(headers: &HeaderMap) -> Option<String> {
    let forwarded = header_value_as_trimmed_str(headers, FORWARDED.as_str())?;
    forwarded
        .split(',')
        .flat_map(|entry| entry.split(';'))
        .filter_map(|part| part.trim().split_once('='))
        .find_map(|(key, value)| {
            if !key.trim().eq_ignore_ascii_case("host") {
                return None;
            }
            let host = value.trim().trim_matches('"').trim();
            if host.is_empty() {
                None
            } else {
                Some(host.to_string())
            }
        })
}
