from __future__ import annotations

import ipaddress
from dataclasses import dataclass
from enum import Enum
from urllib.parse import parse_qsl, urlparse

from jarvis_gateway.redaction import redact_error_message


class LegacyLinkStatus(str, Enum):
    CONFIGURED = "configured"
    NOT_CONFIGURED = "not_configured"
    BLOCKED = "blocked"


@dataclass(frozen=True)
class LegacyLinkResult:
    status: LegacyLinkStatus
    href: str | None
    reason: str | None = None
    hostname_category: str = "not_configured"


_FORBIDDEN_URL_PARTS = {
    "token",
    "secret",
    "oauth",
    "access_token",
    "refresh_token",
    "password",
    "apikey",
    "api_key",
    "key",
}


def _is_tailnet_ip(hostname: str) -> bool:
    try:
        ip = ipaddress.ip_address(hostname)
    except ValueError:
        return False
    return ip in ipaddress.ip_network("100.64.0.0/10")


def _is_localhost(hostname: str) -> bool:
    return hostname in {"localhost", "127.0.0.1", "::1"}


def hostname_category(url: str | None) -> str:
    if not url:
        return "not_configured"
    parsed = urlparse(url)
    host = parsed.hostname or ""
    if _is_localhost(host):
        return "localhost"
    if _is_tailnet_ip(host):
        return "tailnet"
    return "blocked"


def validate_legacy_link(url: str | None, *, allow_tailnet_links: bool = False) -> LegacyLinkResult:
    if not url:
        return LegacyLinkResult(status=LegacyLinkStatus.NOT_CONFIGURED, href=None, reason="not_configured")
    try:
        parsed = urlparse(url)
        if parsed.scheme not in {"http", "https"}:
            return LegacyLinkResult(status=LegacyLinkStatus.BLOCKED, href=None, reason="invalid_scheme", hostname_category="blocked")
        if not parsed.hostname:
            return LegacyLinkResult(status=LegacyLinkStatus.BLOCKED, href=None, reason="missing_host", hostname_category="blocked")
        lowered_url = url.lower()
        if any(part in lowered_url for part in _FORBIDDEN_URL_PARTS):
            return LegacyLinkResult(status=LegacyLinkStatus.BLOCKED, href=None, reason="forbidden_url_content", hostname_category=hostname_category(url))
        query_pairs = parse_qsl(parsed.query, keep_blank_values=True)
        if query_pairs:
            suspicious = [key for key, value in query_pairs if key or value]
            if suspicious:
                return LegacyLinkResult(status=LegacyLinkStatus.BLOCKED, href=None, reason="querystring_blocked", hostname_category=hostname_category(url))
        if parsed.username or parsed.password:
            return LegacyLinkResult(status=LegacyLinkStatus.BLOCKED, href=None, reason="userinfo_blocked", hostname_category=hostname_category(url))
        if _is_localhost(parsed.hostname):
            return LegacyLinkResult(status=LegacyLinkStatus.CONFIGURED, href=url, hostname_category="localhost")
        if _is_tailnet_ip(parsed.hostname):
            if allow_tailnet_links:
                return LegacyLinkResult(status=LegacyLinkStatus.CONFIGURED, href=url, hostname_category="tailnet")
            return LegacyLinkResult(status=LegacyLinkStatus.BLOCKED, href=None, reason="tailnet_links_disabled", hostname_category="tailnet")
        return LegacyLinkResult(status=LegacyLinkStatus.BLOCKED, href=None, reason="external_host_blocked", hostname_category="blocked")
    except Exception as exc:  # noqa: BLE001 - validation must fail closed
        return LegacyLinkResult(status=LegacyLinkStatus.BLOCKED, href=None, reason=redact_error_message(str(exc)), hostname_category="blocked")
