"""Domain normalization utilities for Scout checks."""

from __future__ import annotations

import re
from typing import Optional

try:
    import tldextract  # type: ignore

    _tld_extractor = tldextract.TLDExtract(cache_dir=False, suffix_list_urls=())
    _HAS_TLDEXTRACT = True
except Exception:  # pragma: no cover - optional dependency
    tldextract = None  # type: ignore
    _tld_extractor = None
    _HAS_TLDEXTRACT = False


_FREE_DOMAINS = {
    "gmail.com",
    "yahoo.com",
    "hotmail.com",
    "outlook.com",
    "aol.com",
    "icloud.com",
    "mail.com",
    "gmx.com",
    "proton.me",
    "protonmail.com",
    "zoho.com",
    "yahoo.co.uk",
    "yahoo.ca",
    "yahoo.fr",
    "yahoo.de",
    "hotmail.co.uk",
    "hotmail.fr",
    "hotmail.de",
    "live.com",
    "msn.com",
    "comcast.net",
    "me.com",
    "mac.com",
    "pm.me",
    "protonmail.ch",
    "icloud.com",
    "outlook.co.uk",
    "outlook.de",
    "outlook.fr",
    "fastmail.com",
}

_COMMON_2LEVEL_TLDS = {
    "co.uk",
    "com.au",
    "com.br",
    "co.jp",
    "co.in",
    "co.nz",
    "com.mx",
}


def normalize_domain(raw: Optional[str]) -> Optional[str]:
    if not raw:
        return None
    text = str(raw).strip().lower()
    if not text:
        return None
    if text.startswith("mailto:"):
        text = text[len("mailto:"):]
    if "@" in text and "//" not in text:
        text = text.split("@")[-1]
    text = re.sub(r"^https?://", "", text)
    text = re.sub(r"^ftp://", "", text)
    text = text.split("/")[0]
    text = text.split("?")[0]
    text = text.split("#")[0]
    if ":" in text:
        text = text.split(":")[0]
    if text.startswith("www."):
        text = text[4:]
    text = text.strip(".")
    return text or None


def _fallback_registrable_domain(domain: str) -> Optional[str]:
    parts = [p for p in domain.split(".") if p]
    if len(parts) < 2:
        return domain
    last_two = ".".join(parts[-2:])
    last_three = ".".join(parts[-3:]) if len(parts) >= 3 else last_two
    if last_two in _COMMON_2LEVEL_TLDS:
        return last_three
    return last_two


def extract_registrable_domain(raw: Optional[str]) -> Optional[str]:
    domain = normalize_domain(raw)
    if not domain:
        return None
    if _HAS_TLDEXTRACT and _tld_extractor is not None:
        try:
            ext = _tld_extractor(domain)
            if not ext.suffix:
                return domain
            if not ext.domain:
                return ext.suffix
            return f"{ext.domain}.{ext.suffix}"
        except Exception:
            return _fallback_registrable_domain(domain)
    return _fallback_registrable_domain(domain)


def extract_domain_root(raw: Optional[str]) -> Optional[str]:
    domain = normalize_domain(raw)
    if not domain:
        return None
    if _HAS_TLDEXTRACT and _tld_extractor is not None:
        try:
            ext = _tld_extractor(domain)
            return ext.domain or None
        except Exception:
            pass
    parts = [p for p in domain.split(".") if p]
    return parts[-2] if len(parts) >= 2 else (parts[0] if parts else None)


def is_free_domain(domain: Optional[str]) -> bool:
    if not domain:
        return False
    normalized = extract_registrable_domain(domain) or normalize_domain(domain) or ""
    return normalized in _FREE_DOMAINS


__all__ = [
    "extract_registrable_domain",
    "extract_domain_root",
    "normalize_domain",
    "is_free_domain",
]
