use axum::{
    extract::{Request, State},
    http::{HeaderMap, HeaderName, Method, StatusCode, Uri},
    middleware::Next,
    response::Response,
};
use std::time::Instant;
// Note: Use tracing::info! macro directly instead of importing
use crate::AppState;
use uuid::Uuid;

pub async fn request_logging_middleware(
    State(state): State<AppState>,
    mut request: Request,
    next: Next,
) -> Response {
    let start = Instant::now();
    let _method = request.method().clone();
    let uri = request.uri().clone();
    let headers = request.headers().clone();
    
    // FIX #23: Extraire le request_id depuis les headers (propagé depuis le backend Go)
    // Si X-Request-ID est présent, l'utiliser, sinon générer un nouveau UUID
    let request_id = extract_request_id_from_headers(&headers);
    
    // Extraire aussi le trace_id si présent
    let trace_id = extract_trace_id_from_headers(&headers);

    // Extraire l'IP du client
    let client_ip = extract_client_ip(&headers);

    // Ajouter l'ID de requête aux headers de réponse
    request.headers_mut().insert(
        HeaderName::from_static("x-request-id"),
        request_id.to_string().parse().unwrap(),
    );
    
    // Créer un span avec le request_id pour la corrélation
    let span = tracing::span!(
        tracing::Level::INFO,
        "request",
        request_id = %request_id,
        trace_id = ?trace_id,
        method = %_method,
        uri = %uri,
    );
    let _guard = span.enter();

    // Enregistrer le début de la requête
    tracing::info!(
        request_id = %request_id,
        method = %_method,
        uri = %uri,
        client_ip = %client_ip,
        user_agent = ?headers.get("user-agent"),
        "Début de requête"
    );

    // Traiter la requête
    let response = next.run(request).await;
    let status = response.status();
    let duration = start.elapsed();

    // Incrémenter les métriques
    state.metrics.increment_requests();
    if status.is_success() {
        state.metrics.increment_successful_requests();
    } else {
        state.metrics.increment_failed_requests();
    }

    // Détecter les requêtes suspectes
    if is_suspicious_request(&_method, &uri, &headers, status) {
        tracing::warn!(
            request_id = %request_id,
            method = %_method,
            uri = %uri,
            client_ip = %client_ip,
            status = %status,
            duration_ms = duration.as_millis(),
            "Requête suspecte détectée"
        );
    }

    // Enregistrer la réponse
    let log_level = if status.is_server_error() {
        tracing::Level::ERROR
    } else if status.is_client_error() {
        tracing::Level::WARN
    } else {
        tracing::Level::INFO
    };

    match log_level {
        tracing::Level::ERROR => tracing::error!(
            request_id = %request_id,
            method = %_method,
            uri = %uri,
            client_ip = %client_ip,
            status = %status,
            duration_ms = duration.as_millis(),
            "Requête terminée avec erreur serveur"
        ),
        tracing::Level::WARN => tracing::warn!(
            request_id = %request_id,
            method = %_method,
            uri = %uri,
            client_ip = %client_ip,
            status = %status,
            duration_ms = duration.as_millis(),
            "Requête terminée avec erreur client"
        ),
        _ => tracing::info!(
            request_id = %request_id,
            method = %_method,
            uri = %uri,
            client_ip = %client_ip,
            status = %status,
            duration_ms = duration.as_millis(),
            "Requête terminée avec succès"
        ),
    }

    response
}

fn extract_client_ip(headers: &HeaderMap) -> String {
    // Vérifier les headers de proxy dans l'ordre de priorité
    if let Some(forwarded_for) = headers.get("x-forwarded-for") {
        if let Ok(forwarded_str) = forwarded_for.to_str() {
            // Prendre la première IP de la liste
            if let Some(first_ip) = forwarded_str.split(',').next() {
                return first_ip.trim().to_string();
            }
        }
    }

    if let Some(real_ip) = headers.get("x-real-ip") {
        if let Ok(ip_str) = real_ip.to_str() {
            return ip_str.to_string();
        }
    }

    if let Some(forwarded) = headers.get("forwarded") {
        if let Ok(forwarded_str) = forwarded.to_str() {
            // Parser le header Forwarded (RFC 7239)
            for directive in forwarded_str.split(';') {
                if let Some(for_part) = directive.strip_prefix("for=") {
                    return for_part.trim_matches('"').to_string();
                }
            }
        }
    }

    // Fallback
    "unknown".to_string()
}

fn is_suspicious_request(
    _method: &Method,
    uri: &Uri,
    headers: &HeaderMap,
    status: StatusCode,
) -> bool {
    let path = uri.path();
    let query = uri.query().unwrap_or("");

    // Détecter les tentatives d'injection SQL
    if contains_injection_patterns(path) || contains_injection_patterns(query) {
        return true;
    }

    // Détecter les tentatives de traversée de répertoire
    if contains_dangerous_patterns(path) {
        return true;
    }

    // Détecter les tentatives de scan de ports ou de vulnérabilités
    if path.contains("/.well-known/")
        || path.contains("/admin")
        || path.contains("/wp-admin")
        || path.contains("/phpmyadmin")
    {
        return true;
    }

    // Détecter les User-Agents suspects
    if let Some(user_agent) = headers.get("user-agent") {
        if let Ok(ua_str) = user_agent.to_str() {
            let ua_lower = ua_str.to_lowercase();
            if ua_lower.contains("bot")
                && !ua_lower.contains("googlebot")
                && !ua_lower.contains("bingbot")
            {
                return true;
            }
        }
    }

    // Détecter les erreurs 404 répétées sur des chemins sensibles
    if status == StatusCode::NOT_FOUND
        && (path.contains("admin") || path.contains("config") || path.contains("backup"))
    {
        return true;
    }

    false
}

fn contains_dangerous_patterns(input: &str) -> bool {
    let dangerous_patterns = [
        "../",
        "..\\",
        "..%2f",
        "..%5c",
        "%2e%2e%2f",
        "%2e%2e%5c",
        "etc/passwd",
        "windows/system32",
        "/proc/",
        "/sys/",
    ];

    let input_lower = input.to_lowercase();
    dangerous_patterns
        .iter()
        .any(|&pattern| input_lower.contains(pattern))
}

fn contains_injection_patterns(input: &str) -> bool {
    let injection_patterns = [
        "union select",
        "drop table",
        "insert into",
        "delete from",
        "update set",
        "create table",
        "<script",
        "javascript:",
        "onload=",
        "onerror=",
        "eval(",
        "alert(",
        "document.cookie",
        "window.location",
    ];

    let input_lower = input.to_lowercase();
    injection_patterns
        .iter()
        .any(|&pattern| input_lower.contains(pattern))
}

// FIX #23: Extraire le request_id depuis les headers HTTP
fn extract_request_id_from_headers(headers: &HeaderMap) -> Uuid {
    // Chercher X-Request-ID dans les headers
    if let Some(request_id_header) = headers.get("x-request-id") {
        if let Ok(request_id_str) = request_id_header.to_str() {
            if let Ok(uuid) = Uuid::parse_str(request_id_str) {
                return uuid;
            }
        }
    }
    
    // Si aucun request_id valide n'est trouvé, générer un nouveau UUID
    Uuid::new_v4()
}

// FIX #23: Extraire le trace_id depuis les headers HTTP
fn extract_trace_id_from_headers(headers: &HeaderMap) -> Option<String> {
    // Chercher X-Trace-ID dans les headers
    if let Some(trace_id_header) = headers.get("x-trace-id") {
        if let Ok(trace_id_str) = trace_id_header.to_str() {
            return Some(trace_id_str.to_string());
        }
    }
    
    None
}

#[cfg(test)]
mod tests {
    use super::*;
    use axum::http::{Method, Uri};

    #[test]
    fn test_extract_client_ip() {
        let mut headers = HeaderMap::new();

        // Test avec X-Forwarded-For
        headers.insert("x-forwarded-for", "192.168.1.1, 10.0.0.1".parse().unwrap());
        assert_eq!(extract_client_ip(&headers), "192.168.1.1");

        // Test avec X-Real-IP
        headers.clear();
        headers.insert("x-real-ip", "203.0.113.1".parse().unwrap());
        assert_eq!(extract_client_ip(&headers), "203.0.113.1");

        // Test sans headers
        headers.clear();
        assert_eq!(extract_client_ip(&headers), "unknown");
    }

    #[test]
    fn test_suspicious_request_detection() {
        let headers = HeaderMap::new();

        // Test avec chemin suspect (admin)
        let method = Method::POST;
        let uri: Uri = "/admin/config".parse().unwrap();
        assert!(is_suspicious_request(
            &method,
            &uri,
            &headers,
            StatusCode::OK
        ));

        // Test avec un autre chemin suspect
        let method = Method::GET;
        let uri: Uri = "/phpmyadmin".parse().unwrap();
        assert!(is_suspicious_request(
            &method,
            &uri,
            &headers,
            StatusCode::OK
        ));

        // Test avec requête normale
        let method = Method::GET;
        let uri: Uri = "/stream/music.mp3".parse().unwrap();
        assert!(!is_suspicious_request(
            &method,
            &uri,
            &headers,
            StatusCode::OK
        ));
    }
}
