//! Compound email security verification.
//!
//! Runs DKIM, SPF, and DMARC verification in a single call, avoiding multiple
//! IPC round-trips and handling the internal `mail-auth` types that DMARC needs.

use mail_auth::spf::verify::SpfParameters;
use mail_auth::{AuthenticatedMessage, MessageAuthenticator};
use serde::{Deserialize, Serialize};
use std::net::IpAddr;

use crate::dkim::DkimVerificationResult;
use crate::dmarc::{check_dmarc, DmarcResult};
use crate::error::{Result, SecurityError};
use crate::spf::SpfResult;

/// Combined result of DKIM + SPF + DMARC verification.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EmailSecurityResult {
    pub dkim: Vec<DkimVerificationResult>,
    pub spf: Option<SpfResult>,
    pub dmarc: Option<DmarcResult>,
}

/// Run all email security checks (DKIM, SPF, DMARC) in one call.
///
/// This is the preferred entry point for inbound email verification because:
/// 1. DMARC requires raw `mail-auth` DKIM/SPF outputs (not our serialized types).
/// 2. A single call avoids 3 sequential IPC round-trips.
///
/// # Arguments
/// * `raw_message` - The raw RFC 5322 message bytes
/// * `ip` - The connecting client's IP address
/// * `helo_domain` - The domain from the SMTP EHLO/HELO command
/// * `host_domain` - Your receiving server's hostname
/// * `mail_from` - The full MAIL FROM address (e.g. "sender@example.com")
/// * `authenticator` - The `MessageAuthenticator` for DNS lookups
pub async fn verify_email_security(
    raw_message: &[u8],
    ip: IpAddr,
    helo_domain: &str,
    host_domain: &str,
    mail_from: &str,
    authenticator: &MessageAuthenticator,
) -> Result<EmailSecurityResult> {
    // Parse the message once for all checks
    let message = AuthenticatedMessage::parse(raw_message)
        .ok_or_else(|| SecurityError::Parse("Failed to parse email message".into()))?;

    // --- DKIM verification ---
    let dkim_outputs = authenticator.verify_dkim(&message).await;
    let dkim_results = crate::dkim::dkim_outputs_to_results(&dkim_outputs);

    // --- SPF verification ---
    let spf_output = authenticator
        .verify_spf(SpfParameters::verify_mail_from(
            ip,
            helo_domain,
            host_domain,
            mail_from,
        ))
        .await;

    let spf_result = SpfResult::from_output(&spf_output, ip);

    // --- DMARC verification (needs raw dkim_outputs + spf_output) ---
    let mail_from_domain = mail_from
        .rsplit_once('@')
        .map(|(_, d)| d)
        .unwrap_or(helo_domain);

    let dmarc_result = check_dmarc(
        raw_message,
        &dkim_outputs,
        &spf_output,
        mail_from_domain,
        authenticator,
    )
    .await
    .ok(); // DMARC failure is non-fatal; we still return DKIM + SPF results

    Ok(EmailSecurityResult {
        dkim: dkim_results,
        spf: Some(spf_result),
        dmarc: dmarc_result,
    })
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_email_security_result_serialization() {
        let result = EmailSecurityResult {
            dkim: vec![DkimVerificationResult {
                is_valid: false,
                domain: None,
                selector: None,
                status: "none".to_string(),
                details: Some("No DKIM signatures".to_string()),
            }],
            spf: Some(SpfResult {
                result: "none".to_string(),
                domain: "example.com".to_string(),
                ip: "1.2.3.4".to_string(),
                explanation: None,
            }),
            dmarc: None,
        };
        let json = serde_json::to_string(&result).unwrap();
        assert!(json.contains("\"dkim\""));
        assert!(json.contains("\"spf\""));
        assert!(json.contains("\"dmarc\":null"));
    }
}
