use regex::Regex;
use std::sync::LazyLock;

/// Basic email format regex — covers the vast majority of valid email addresses.
/// Does NOT attempt to match the full RFC 5321 grammar (which is impractical via regex).
static EMAIL_REGEX: LazyLock<Regex> = LazyLock::new(|| {
    Regex::new(
        r"(?i)^[a-z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)+$",
    )
    .expect("invalid email regex")
});

/// Check whether an email address has valid syntax.
pub fn is_valid_email_format(email: &str) -> bool {
    let email = email.trim();
    if email.is_empty() || email.len() > 254 {
        return false;
    }

    let parts: Vec<&str> = email.rsplitn(2, '@').collect();
    if parts.len() != 2 {
        return false;
    }
    let local = parts[1];
    let domain = parts[0];

    // Local part max 64 chars
    if local.is_empty() || local.len() > 64 {
        return false;
    }

    // Domain must have at least one dot (TLD only not valid for email)
    if !domain.contains('.') {
        return false;
    }

    EMAIL_REGEX.is_match(email)
}

/// Email validation result with scoring.
#[derive(Debug, Clone)]
pub struct EmailValidationResult {
    pub is_valid: bool,
    pub format_valid: bool,
    pub score: f64,
    pub error_message: Option<String>,
}

/// Validate an email address (synchronous, format-only).
/// DNS-based validation (MX records, disposable domains) would require async and is
/// intended for the N-API bridge layer where the TypeScript side already has DNS access.
pub fn validate_email(email: &str) -> EmailValidationResult {
    let format_valid = is_valid_email_format(email);

    if !format_valid {
        return EmailValidationResult {
            is_valid: false,
            format_valid: false,
            score: 0.0,
            error_message: Some(format!("Invalid email format: {}", email)),
        };
    }

    // Role account detection (weight 0.1 penalty)
    let local = email.split('@').next().unwrap_or("");
    let is_role = is_role_account(local);

    // Score: format (0.4) + assumed-mx (0.3) + assumed-not-disposable (0.2) + role (0.1)
    let mut score = 0.4 + 0.3 + 0.2; // format + mx + not-disposable
    if !is_role {
        score += 0.1;
    }

    EmailValidationResult {
        is_valid: score >= 0.7,
        format_valid: true,
        score,
        error_message: None,
    }
}

/// Check if a local part is a common role account.
fn is_role_account(local: &str) -> bool {
    const ROLE_ACCOUNTS: &[&str] = &[
        "abuse",
        "admin",
        "administrator",
        "billing",
        "compliance",
        "devnull",
        "dns",
        "ftp",
        "hostmaster",
        "info",
        "inoc",
        "ispfeedback",
        "ispsupport",
        "list",
        "list-request",
        "maildaemon",
        "mailer-daemon",
        "mailerdaemon",
        "marketing",
        "noc",
        "no-reply",
        "noreply",
        "null",
        "phish",
        "phishing",
        "postmaster",
        "privacy",
        "registrar",
        "root",
        "sales",
        "security",
        "spam",
        "support",
        "sysadmin",
        "tech",
        "undisclosed-recipients",
        "unsubscribe",
        "usenet",
        "uucp",
        "webmaster",
        "www",
    ];
    let lower = local.to_lowercase();
    ROLE_ACCOUNTS.contains(&lower.as_str())
}

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

    #[test]
    fn test_valid_emails() {
        assert!(is_valid_email_format("user@example.com"));
        assert!(is_valid_email_format("first.last@example.com"));
        assert!(is_valid_email_format("user+tag@example.com"));
        assert!(is_valid_email_format("user@sub.domain.example.com"));
    }

    #[test]
    fn test_invalid_emails() {
        assert!(!is_valid_email_format(""));
        assert!(!is_valid_email_format("@"));
        assert!(!is_valid_email_format("user@"));
        assert!(!is_valid_email_format("@domain.com"));
        assert!(!is_valid_email_format("user@domain")); // no TLD
        assert!(!is_valid_email_format("user @domain.com")); // space
        assert!(!is_valid_email_format("user@.com")); // leading dot
    }

    #[test]
    fn test_validate_email_scoring() {
        let result = validate_email("user@example.com");
        assert!(result.is_valid);
        assert!(result.score >= 0.9);

        let result = validate_email("postmaster@example.com");
        assert!(result.is_valid);
        assert!(result.score >= 0.7);
        assert!(result.score < 1.0); // role account penalty

        let result = validate_email("not-an-email");
        assert!(!result.is_valid);
        assert_eq!(result.score, 0.0);
    }

    #[test]
    fn test_role_accounts() {
        assert!(is_role_account("postmaster"));
        assert!(is_role_account("abuse"));
        assert!(is_role_account("noreply"));
        assert!(!is_role_account("john"));
        assert!(!is_role_account("alice"));
    }
}
