use axum::{
    body::Body,
    extract::State,
    http::{header, HeaderValue, Request},
    middleware::Next,
    response::Response,
};

use crate::https::ServerState;

const PERMISSIONS_POLICY_VALUE: &str = "fullscreen=(), geolocation=()";
const X_CONTENT_TYPE_OPTIONS_VALUE: &str = "nosniff";

pub async fn security_headers_layer(
    State(state): State<ServerState>,
    request: Request<Body>,
    next: Next,
) -> Response {
    // wait for the middleware to come back
    let mut response = next.run(request).await;

    let headers = response.headers_mut();

    // add the Content-Security-Policy header, which defines how contact will be accessed/run based on the source URL
    // Only update the CSP if none is present. This allows some routes to opt into defining their own CSP
    if !headers.contains_key(header::CONTENT_SECURITY_POLICY) {
        headers.insert(header::CONTENT_SECURITY_POLICY, state.csp_header);
    }

    // X-Content-Type-Options tells the browser if it's OK to "sniff" or guess the content type of a response
    //
    // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options
    // https://scotthelme.co.uk/hardening-your-http-response-headers/#x-content-type-options
    headers.insert(
        header::X_CONTENT_TYPE_OPTIONS,
        HeaderValue::from_static(X_CONTENT_TYPE_OPTIONS_VALUE),
    );

    // Permissions policy defines access to platform services like geolocation, fullscreen etc.
    //
    // https://www.w3.org/TR/permissions-policy-1/
    headers.insert(
        "Permissions-Policy",
        HeaderValue::from_static(PERMISSIONS_POLICY_VALUE),
    );

    // Don't send a referrer header when the user is navigating to a non-HTTPS URL
    // Ref:
    // https://scotthelme.co.uk/a-new-security-header-referrer-policy/
    // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy
    headers.insert(
        header::REFERRER_POLICY,
        HeaderValue::from_static("no-referrer-when-downgrade"),
    );

    response
}

pub async fn csp_header_no_form_action_layer(
    State(state): State<ServerState>,
    request: Request<Body>,
    next: Next,
) -> Response {
    let mut response = next.run(request).await;

    let headers = response.headers_mut();

    headers.insert(
        header::CONTENT_SECURITY_POLICY,
        state.csp_header_no_form_action,
    );

    response
}
