"""Safety rail: compliance scenarios may only target loopback mock servers.

Fail closed: anything that is not a loopback address is refused, and the
known live Gitea instances are refused by name. There is deliberately no
environment override — requirement 6 of issue #156 forbids credentialed
destructive scenarios against live or production-like hosts.
"""

import ipaddress
from urllib.parse import urlsplit

LIVE_GITEA_HOSTS = frozenset({"gitea.dadeschools.net", "gitea.prgs.cc"})
_LOOPBACK_NAMES = frozenset({"localhost"})


class UnsafeComplianceTargetError(Exception):
    """Raised when a compliance scenario targets a non-loopback host."""


def _hostname(target):
    """Extract a lowercase hostname from a URL or bare host[:port] string."""
    if "://" in target:
        return (urlsplit(target).hostname or "").lower()
    host = target.strip()
    # Bracketed IPv6, optionally with a port: [::1] or [::1]:8080.
    if host.startswith("["):
        return host.split("]", 1)[0][1:].lower()
    # Exactly one colon means host:port; more means a bare IPv6 literal.
    if host.count(":") == 1:
        return host.rsplit(":", 1)[0].lower()
    return host.lower()


def _is_loopback(host):
    """True only for real loopback IPs (127.0.0.0/8, ::1) or 'localhost'.

    A string-prefix check like startswith('127.') would accept DNS names
    such as 127.0.0.1.evil.com — the host must parse as an IP address.
    """
    if host in _LOOPBACK_NAMES:
        return True
    try:
        return ipaddress.ip_address(host).is_loopback
    except ValueError:
        return False


def is_safe_compliance_target(target):
    """Return (ok, reason). Only loopback targets are safe."""
    host = _hostname(str(target))
    if not host:
        return False, "empty target host; fail closed"
    if host in LIVE_GITEA_HOSTS:
        return False, (
            f"'{host}' is a live Gitea instance; compliance scenarios must "
            "never run credentialed against live hosts"
        )
    if _is_loopback(host):
        return True, "loopback target"
    return False, (
        f"'{host}' is not a loopback address; compliance scenarios must "
        "target a local mock Gitea server"
    )


def assert_safe_compliance_target(target):
    """Raise UnsafeComplianceTargetError unless *target* is loopback."""
    ok, reason = is_safe_compliance_target(target)
    if not ok:
        raise UnsafeComplianceTargetError(reason)
