use axum::extract::Request;
use axum::middleware::Next;
use axum::response::IntoResponse;
use axum_extra::headers::{CacheControl, Expires, HeaderMapExt};
use http::{HeaderMap, HeaderValue, header};
use std::time::{Duration, SystemTime};

// see http://nginx.org/en/docs/http/ngx_http_headers_module.html#add_header
const NGINX_SUCCESS_CODES: [u16; 10] = [200, 201, 204, 206, 301, 203, 303, 304, 307, 308];

const ONE_DAY: Duration = Duration::from_secs(24 * 60 * 60);
const ONE_YEAR: Duration = Duration::from_secs(365 * 24 * 60 * 60);

pub async fn add_common_headers(request: Request, next: Next) -> impl IntoResponse {
    let v = HeaderValue::from_static;

    let path = request.uri().path();

    const STATIC_FILES: [&str; 6] = [
        "/github-auth-loading.html",
        "/github-redirect.html",
        "/favicon.ico",
        "/robots.txt",
        "/opensearch.xml",
        "/.well-known/security.txt",
    ];
    let cache_duration = if STATIC_FILES.contains(&path) {
        Some(ONE_DAY)
    } else if path.starts_with("/_app/immutable/") {
        Some(10 * ONE_YEAR)
    } else {
        None
    };

    let response = next.run(request).await;

    let mut headers = HeaderMap::new();
    headers.insert(header::ACCESS_CONTROL_ALLOW_ORIGIN, v("*"));
    headers.insert(header::STRICT_TRANSPORT_SECURITY, v("max-age=31536000"));

    if let Some(cache_duration) = cache_duration
        && response.status().is_success()
    {
        expires(&mut headers, cache_duration);
    }

    if NGINX_SUCCESS_CODES.contains(&response.status().as_u16()) {
        headers.insert(header::X_CONTENT_TYPE_OPTIONS, v("nosniff"));
        headers.insert(header::X_FRAME_OPTIONS, v("SAMEORIGIN"));
        headers.insert(header::X_XSS_PROTECTION, v("0"));
    }

    (headers, response)
}

fn expires(headers: &mut HeaderMap, cache_duration: Duration) {
    headers.typed_insert(Expires::from(SystemTime::now() + cache_duration));
    headers.typed_insert(
        CacheControl::new()
            .with_public()
            .with_max_age(cache_duration),
    );
}
