# src/fmatch/preprocessing/rules/domain_utils.py
from __future__ import annotations
import re
import tldextract
import idna
import os

# Configure tldextract with cache and fallback for production hardening
_tld_cache_dir = os.path.expanduser("~/.tldextract_cache")
_tld_extractor = tldextract.TLDExtract(
    cache_dir=_tld_cache_dir,
    fallback_to_snapshot=True,  # Use bundled snapshot if network fails
)


def registrable_domain(s: str, *, key_ascii: bool = False) -> str:
    """
    Extract the registrable domain (eTLD+1) from URL or email with IDN support.

    Examples:
        - "https://careers.acme.com/jobs" -> "acme.com"
        - "user@mail.example.co.jp" -> "example.co.jp"
        - "xn--e1afmkfd.xn--p1ai" -> "пример.рф" (IDN decoded, or punycode if key_ascii=True)
        - "www.sub.domain.co.uk" -> "domain.co.uk"

    Args:
        s: URL, email, or domain string
        key_ascii: If True, return ASCII-safe key (punycode for IDN) for blocking

    Returns:
        Registrable domain (base domain + TLD)
    """
    if not s:
        return ""

    # Clean input
    s = s.strip().lower()

    # Handle email addresses
    if "@" in s:
        parts = s.split("@")
        if len(parts) == 2:
            s = parts[1]

    # Remove protocol and path from URLs
    s = re.sub(r"^https?://", "", s)
    s = s.split("/", 1)[0]

    # Remove www prefix
    s = s.lstrip("www.")

    # Handle IDN (Internationalized Domain Names)
    try:
        if s.startswith("xn--") or ".xn--" in s:
            # Decode IDN to Unicode for logic
            host_u = idna.decode(s.encode("ascii"))
        else:
            host_u = s
    except Exception:
        # If IDN decoding fails, use original
        host_u = s

    # Extract using tldextract for proper eTLD+1
    try:
        ext = _tld_extractor.extract(host_u)

        # Build registrable domain (domain.suffix)
        if ext.suffix and ext.domain:
            base_u = f"{ext.domain}.{ext.suffix}"
        elif ext.domain:
            base_u = ext.domain
        else:
            base_u = host_u

        # Return ASCII-safe key for blocking if requested
        if key_ascii:
            try:
                return idna.encode(base_u).decode("ascii")  # punycode
            except Exception:
                # Fallback to original if encoding fails
                pass

        return base_u
    except Exception:
        # Fallback to original if extraction fails
        return host_u


def is_free_domain(domain: str, free_domains: set[str]) -> bool:
    """
    Check if domain is in the free/consumer domains list.

    Args:
        domain: Domain to check
        free_domains: Set of known free domains

    Returns:
        True if domain is a free/consumer domain
    """
    if not domain:
        return False

    # Extract registrable domain first
    reg_domain = registrable_domain(domain)

    return reg_domain in free_domains


# Global free domains (extensible by region)
GLOBAL_FREE_DOMAINS = {
    # Major global providers
    "gmail.com",
    "yahoo.com",
    "hotmail.com",
    "outlook.com",
    "aol.com",
    "icloud.com",
    "me.com",
    "mac.com",
    # Regional European
    "gmx.de",
    "web.de",
    "t-online.de",
    "freenet.de",  # Germany
    "orange.fr",
    "laposte.net",
    "free.fr",
    "sfr.fr",  # France
    "libero.it",
    "alice.it",
    "tin.it",
    "virgilio.it",  # Italy
    "terra.es",
    "ya.com",
    "telefonica.net",  # Spain
    "sapo.pt",
    "clix.pt",
    "netcabo.pt",  # Portugal
    # Eastern European
    "yandex.ru",
    "mail.ru",
    "rambler.ru",
    "list.ru",  # Russia
    "yandex.com",
    "yandex.ua",
    "i.ua",  # Ukraine/CIS
    # Asian
    "naver.com",
    "daum.net",
    "hanmail.net",
    "nate.com",  # Korea
    "yahoo.co.jp",
    "docomo.ne.jp",
    "ezweb.ne.jp",
    "softbank.ne.jp",  # Japan
    "163.com",
    "126.com",
    "qq.com",
    "sina.com",  # China
    # Other regions can be added as needed
}
