"""Utility validators for promotion workflows."""

from __future__ import annotations

import http.client
import socket
import ssl
import urllib.parse
from typing import Any, Dict, List, Optional, Tuple

import idna

from ...utils_normalize import to_domain

_ACCEPTABLE_STATUS = set(range(200, 400)) | {401, 403}
_BLOCKLIST_HOST_TOKENS = {
    # Consumer email providers
    "gmail.com",
    "mail.google.com",
    "googlemail.com",
    "outlook.com",
    "hotmail.com",
    "yahoo.com",
    "aol.com",
    "icloud.com",
    "protonmail.com",
    # SSO/Identity providers
    "login.microsoftonline.com",
    "okta.com",
    "okta.net",
    "auth0.com",
    "accounts.google.com",
    "login.yahoo.com",
    "id.apple.com",
    # SaaS platforms (not official websites)
    "my.salesforce.com",
    "zoom.us",
    "dropbox.com",
    "box.com",
    "slack.com",
    "teams.microsoft.com",
    "sharepoint.com",
    # Online/hosting domains (too generic)
    "online.net",
    "onmicrosoft.com",
    "cloudapp.net",
    "azurewebsites.net",
    "herokuapp.com",
    "vercel.app",
    "netlify.app",
}


def _is_blocklisted(host: str) -> bool:
    host_lower = host.lower()
    return any(token in host_lower for token in _BLOCKLIST_HOST_TOKENS)


class DomainValidator:
    """Resolve DNS and attempt HTTPS/HTTP to confirm the domain is reachable."""

    def __init__(self, timeout_s: int = 6, max_redirects: int = 5) -> None:
        self.timeout = timeout_s
        self.max_redirects = max_redirects

    def verify(self, domain: str) -> Dict[str, Any]:
        normalized = self._normalize(domain)
        dns_ok = self._resolve_dns(normalized)
        chain: List[Tuple[str, Optional[int]]] = []
        final_url = ""
        final_status = None
        verified = False

        if dns_ok:
            for scheme in ("https", "http"):
                current_url = f"{scheme}://{normalized}"
                for _ in range(self.max_redirects):
                    status, redirect_to = self._head_or_get(current_url)
                    chain.append((current_url, status))
                    final_status = status
                    if status is None:
                        break
                    if status in _ACCEPTABLE_STATUS and not redirect_to:
                        final_url = current_url
                        verified = True
                        break
                    if 300 <= status < 400 and redirect_to:
                        current_url = urllib.parse.urljoin(current_url, redirect_to)
                        continue
                    break
                if verified:
                    break

        if not final_url and chain:
            final_url = chain[-1][0]

        parsed = urllib.parse.urlparse(final_url or f"https://{normalized}")
        host = parsed.hostname or normalized
        try:
            unicode_host = idna.decode(host.encode("ascii"))
        except Exception:
            unicode_host = host

        canonical = to_domain(host) or to_domain(unicode_host) or unicode_host
        blocklisted = _is_blocklisted(host)

        return {
            "verified": bool(
                verified and final_status in _ACCEPTABLE_STATUS and not blocklisted
            ),
            "dns": dns_ok,
            "status": final_status,
            "final_url": final_url,
            "final_host": unicode_host,
            "final_etld1": canonical,
            "redirect_chain": chain,
            "blocklisted": blocklisted,
        }

    def _resolve_dns(self, host: str) -> bool:
        try:
            socket.getaddrinfo(host, None, proto=socket.IPPROTO_TCP)
            return True
        except Exception:
            return False

    def _head_or_get(self, url: str) -> Tuple[Optional[int], Optional[str]]:
        parsed = urllib.parse.urlparse(url)
        host = parsed.hostname or ""
        path = parsed.path or "/"
        if parsed.query:
            path = f"{path}?{parsed.query}"

        connection: Optional[http.client.HTTPConnection] = None
        try:
            if parsed.scheme == "https":
                context = ssl.create_default_context()
                connection = http.client.HTTPSConnection(
                    host, timeout=self.timeout, context=context
                )
            else:
                connection = http.client.HTTPConnection(host, timeout=self.timeout)

            connection.request(
                "HEAD", path, headers={"User-Agent": "FoundryMatch Validator/1.0"}
            )
            response = connection.getresponse()
            status = response.status
            if status == http.client.METHOD_NOT_ALLOWED:
                response.close()
                connection.request(
                    "GET", path, headers={"User-Agent": "FoundryMatch Validator/1.0"}
                )
                response = connection.getresponse()
                status = response.status
            location = response.getheader("Location")
            response.close()
            return status, location
        except Exception:
            return None, None
        finally:
            if connection:
                try:
                    connection.close()
                except Exception:
                    pass

    @staticmethod
    def _normalize(domain: str) -> str:
        candidate = domain.strip()
        parsed = urllib.parse.urlparse(
            candidate if "://" in candidate else f"https://{candidate}"
        )
        host = parsed.hostname or candidate
        return host.lower().strip(".")
