#!/usr/bin/env python3
import random, string, json
from typing import Dict, Tuple, List
import idna, dns.resolver

# ------------------ CONFIG ------------------
DISPOSABLE = {
    "mailinator.com","10minutemail.com","tempmail.com","guerrillamail.com",
    "sharklasers.com","yopmail.com","trashmail.com"
}
ROLE_PREFIXES = {
    "admin","info","sales","support","contact","hello","office","mail","no-reply","noreply"
}
LOCAL_PART_ALLOWED = set("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789._%+-")

# ------------------ HELPERS ------------------
def _is_valid_local_part(local: str) -> bool:
    if not (1 <= len(local) <= 64): return False
    if any(ch not in LOCAL_PART_ALLOWED for ch in local): return False
    if local[0] == "." or local[-1] == "." or ".." in local: return False
    return True

def _is_valid_domain_ascii(domain_ascii: str) -> bool:
    if not domain_ascii or len(domain_ascii) > 253 or "." not in domain_ascii: return False
    labels = domain_ascii.split(".")
    if len(labels[-1]) < 2 or not labels[-1].isalpha(): return False
    for lab in labels:
        if not (1 <= len(lab) <= 63): return False
        if any(not (ch.isalnum() or ch == "-") for ch in lab): return False
        if lab[0] == "-" or lab[-1] == "-": return False
    return True

def normalize_email(addr: str) -> Tuple[str,str,str]:
    addr = addr.strip()
    if "@" not in addr: raise ValueError("Missing '@'")
    parts = addr.split("@")
    if len(parts) != 2: raise ValueError("Multiple '@'")
    local_raw, domain_raw = parts[0], parts[1].strip()
    if not _is_valid_local_part(local_raw): raise ValueError("Invalid local-part")
    try:
        domain_ascii = idna.encode(domain_raw.lower()).decode("ascii")
    except idna.IDNAError:
        raise ValueError("Invalid IDN domain")
    if not _is_valid_domain_ascii(domain_ascii): raise ValueError("Invalid domain")
    if len(local_raw)+1+len(domain_ascii) > 254: raise ValueError("Too long")
    return f"{local_raw.lower()}@{domain_ascii}", local_raw.lower(), domain_ascii

def dns_lookup(name: str, rrtype: str) -> List[str]:
    try: return [r.to_text() for r in dns.resolver.resolve(name, rrtype)]
    except Exception: return []

def get_mail_hosts(domain: str) -> List[str]:
    """Fast version: only resolve MX/A/AAAA, no SMTP probing"""
    mx = []
    try:
        answers = dns.resolver.resolve(domain, "MX")
        for r in answers: mx.append(str(r.exchange).rstrip("."))
        if not mx:
            a = dns_lookup(domain, "A")
            aaaa = dns_lookup(domain, "AAAA")
            mx = a + aaaa
    except Exception:
        a = dns_lookup(domain, "A")
        aaaa = dns_lookup(domain, "AAAA")
        mx = a + aaaa
    return mx

def is_disposable(domain: str) -> bool: return domain in DISPOSABLE
def is_role_account(local: str) -> bool: return local.split("+",1)[0].lower() in ROLE_PREFIXES

# ------------------ HIGH LEVEL CHECK ------------------
def verify_email_strong(addr: str) -> Dict[str, object]:
    """
    Fast version: only syntax, disposable, role account, MX/A/AAAA existence.
    Skips SMTP probing + catch-all.
    """
    out = {
        "input": addr,
        "status": "unknown",
        "reasons": [],
        "domain": None,
        "mx_hosts": [],
        "has_spf": False,
        "has_dmarc": False,
        "role_account": False,
        "disposable_domain": False,
        "smtp_notes": [],   # empty in fast version
        "confidence": 0
    }

    try:
        full, local, domain = normalize_email(addr)
        out["domain"] = domain
    except ValueError as ve:
        out["status"] = "invalid"
        out["reasons"].append(str(ve))
        out["confidence"] = 0
        return out

    if is_role_account(local):
        out["role_account"] = True
        out["reasons"].append("role_account")
    if is_disposable(domain):
        out["disposable_domain"] = True
        out["reasons"].append("disposable_domain")

    mx_list = get_mail_hosts(domain)
    out["mx_hosts"] = mx_list

    # SPFs and DMARC DNS checks (fast enough)
    out["has_spf"] = any("v=spf1" in txt.lower() for txt in dns_lookup(domain, "TXT"))
    out["has_dmarc"] = any("v=dmarc1" in txt.lower() for txt in dns_lookup(f"_dmarc.{domain}", "TXT"))

    # Determine fast status & confidence
    if not mx_list:
        out["status"] = "invalid"
        out["reasons"].append("no_mx_or_a_record")
        out["confidence"] = 0
    else:
        out["status"] = "syntax_checked"
        base_conf = 70
        if out["disposable_domain"]: base_conf -= 20
        if out["role_account"]: base_conf -= 10
        out["confidence"] = max(0, min(100, base_conf))

    return out

# ------------------ CLI ------------------
if __name__ == "__main__":
    import sys
    result = verify_email_strong(sys.argv[1])
    print(json.dumps(result, indent=2))
