"""NO_PROXY entries httpx cannot compile are rewritten to the bare-IPv6 form (#118159).

Clash Verge / mihomo export loopback bypasses in bracketed form (``[::1]``); httpx 0.28.1's
``get_environment_proxies`` sends any bracketed entry down its wildcard branch, producing
``all://*[::1]``, and ``URLPattern`` dies at ``Client.__init__`` with ``InvalidURL: Invalid port``.
Every bare ``httpx.Client()`` in the process then raises — codex login, doctor probes — while the
chat transport (explicit mounts) keeps working. ``utils.normalize_proxy_env_vars`` (already the
in-place env sanitizer called when chat transports are built) rewrites those entries to the bare
``::1`` literal that httpx compiles into a working ``all://[::1]`` bypass and that urllib, requests
and the repo's own ``agent.proxy_bypass`` matcher all understand.
"""

from __future__ import annotations

import pytest

import httpx

from utils import normalize_proxy_env_vars, sanitize_no_proxy_entries


@pytest.fixture
def proxy_env(monkeypatch):
    for key in ("NO_PROXY", "no_proxy", "HTTP_PROXY", "HTTPS_PROXY", "ALL_PROXY",
                "http_proxy", "https_proxy", "all_proxy"):
        monkeypatch.delenv(key, raising=False)


# The exact environment Clash Verge / mihomo writes (issue #118159).
_CLASH_NO_PROXY = "127.0.0.1,localhost,::1,[::1]"


class TestSanitizeNoProxyEntries:
    def test_bracketed_ipv6_becomes_bare_literal(self):
        assert sanitize_no_proxy_entries(_CLASH_NO_PROXY) == "127.0.0.1,localhost,::1"

    @pytest.mark.parametrize(
        "entry,expected",
        [
            ("[::1]", "::1"),
            ("[::1]:8080", "::1"),  # httpx cannot express a port-scoped v6 bypass; the literal still bypasses every port
            ("[2001:db8::1]", "2001:db8::1"),
            ("[::ffff:127.0.0.1]", "::ffff:127.0.0.1"),
            ("[::1]/128", "::1"),
            ("::1/128", "::1"),  # httpx cannot compile v6 CIDR bypasses at all; degrade to the bare literal
            ("2001:db8::/32", "2001:db8::"),
        ],
    )
    def test_bracketed_and_cidr_ipv6_entries_are_rewritten(self, entry, expected):
        assert sanitize_no_proxy_entries(f"example.com,{entry}") == f"example.com,{expected}"

    @pytest.mark.parametrize(
        "value",
        [
            "",  # nothing set: nothing invented
            "*",
            "127.0.0.1,localhost,::1",
            "10.0.0.0/8,*.internal",  # IPv4 CIDR / wildcard domains: httpx compiles these fine
            "[127.0.0.1]",  # bracketed IPv4: not the broken form, untouched
            "[not-an-ip]",  # bracketed junk: httpx's wildcard branch compiles it, untouched
            "example.com,  spaced.internal ,10.0.0.5",
            "::1:8080",  # a valid v6 address in its own right (hex), httpx compiles it
        ],
    )
    def test_unaffected_values_pass_through_byte_stable(self, value):
        assert sanitize_no_proxy_entries(value) == value


class TestNormalizeProxyEnvVars:
    def test_rewrites_both_casings_in_place(self, proxy_env, monkeypatch):
        monkeypatch.setenv("NO_PROXY", _CLASH_NO_PROXY)
        monkeypatch.setenv("no_proxy", _CLASH_NO_PROXY)
        normalize_proxy_env_vars()
        assert os_get("NO_PROXY") == "127.0.0.1,localhost,::1"
        assert os_get("no_proxy") == "127.0.0.1,localhost,::1"

    def test_clean_environment_is_left_untouched(self, proxy_env, monkeypatch):
        monkeypatch.setenv("NO_PROXY", "example.com,10.0.0.0/8")
        normalize_proxy_env_vars()
        assert os_get("NO_PROXY") == "example.com,10.0.0.0/8"

    def test_socks_scheme_normalization_keeps_working(self, proxy_env, monkeypatch):
        monkeypatch.setenv("HTTPS_PROXY", "socks://127.0.0.1:7890")
        monkeypatch.setenv("NO_PROXY", _CLASH_NO_PROXY)
        normalize_proxy_env_vars()
        assert os_get("HTTPS_PROXY") == "socks5://127.0.0.1:7890"  # pre-existing behavior intact
        assert os_get("NO_PROXY") == "127.0.0.1,localhost,::1"


class TestSanitizedEnvYieldsAWorkingHttpxClient:
    def test_client_constructs_and_loopback_v6_bypasses(self, proxy_env, monkeypatch):
        monkeypatch.setenv("NO_PROXY", _CLASH_NO_PROXY)
        monkeypatch.setenv("no_proxy", _CLASH_NO_PROXY)
        normalize_proxy_env_vars()
        # Red on base: the unsanitized Clash env raises InvalidURL at construction.
        client = httpx.Client()
        try:
            assert any(
                pattern.matches(httpx.URL("http://[::1]:8080/")) and transport is None
                for pattern, transport in client._mounts.items()
            )
        finally:
            client.close()

    def test_bypass_still_matches_after_normalization(self, proxy_env, monkeypatch):
        from agent.proxy_bypass import should_bypass_proxy

        monkeypatch.setenv("NO_PROXY", _CLASH_NO_PROXY)
        normalize_proxy_env_vars()
        assert should_bypass_proxy("http://[::1]:8080/")
        assert should_bypass_proxy("https://[::1]/")
        assert not should_bypass_proxy("https://api.example.com/")


def os_get(key: str) -> str:
    import os

    return os.environ[key]
