use axum::http::{HeaderMap, StatusCode};
use tracing::warn;

use crate::state::AppState;

pub fn require_bootstrap_auth(
    state: &AppState,
    headers: &HeaderMap,
) -> Result<(), (StatusCode, String)> {
    if state.config.allow_unauthenticated {
        return Ok(());
    }

    let expected = match state.config.bootstrap_token.as_deref() {
        Some(token) if !token.is_empty() => token,
        _ => {
            return Err((
                StatusCode::SERVICE_UNAVAILABLE,
                "bootstrap token not configured".to_string(),
            ))
        }
    };

    let provided = extract_token(headers);
    if provided.as_deref() == Some(expected) {
        return Ok(());
    }

    Err((
        StatusCode::UNAUTHORIZED,
        "invalid bootstrap token".to_string(),
    ))
}

pub fn require_admin_auth(
    state: &AppState,
    headers: &HeaderMap,
) -> Result<(), (StatusCode, String)> {
    if state.config.allow_unauthenticated {
        return Ok(());
    }

    let expected = match state.config.admin_token.as_deref() {
        Some(token) if !token.is_empty() => token,
        _ => {
            if !state.config.allow_admin_fallback {
                return Err((
                    StatusCode::SERVICE_UNAVAILABLE,
                    "admin token not configured".to_string(),
                ));
            }

            match state.config.bootstrap_token.as_deref() {
                Some(token) if !token.is_empty() => {
                    warn!("admin token not configured; falling back to bootstrap token");
                    token
                }
                _ => {
                    return Err((
                        StatusCode::SERVICE_UNAVAILABLE,
                        "admin token not configured".to_string(),
                    ))
                }
            }
        }
    };

    let provided = extract_token(headers);
    if provided.as_deref() == Some(expected) {
        return Ok(());
    }

    Err((StatusCode::UNAUTHORIZED, "invalid admin token".to_string()))
}

fn extract_token(headers: &HeaderMap) -> Option<String> {
    if let Some(value) = headers.get("x-deployer-token") {
        if let Ok(token) = value.to_str() {
            let trimmed = token.trim();
            if !trimmed.is_empty() {
                return Some(trimmed.to_string());
            }
        }
    }

    if let Some(value) = headers.get("authorization") {
        if let Ok(auth) = value.to_str() {
            let trimmed = auth.trim();
            if let Some((scheme, rest)) = trimmed.split_once(' ') {
                if scheme.eq_ignore_ascii_case("bearer") {
                    let token = rest.trim();
                    if !token.is_empty() {
                        return Some(token.to_string());
                    }
                }
            }
        }
    }

    None
}
