"""network_policy：出站 URL 判定、环境与 CLI 开关。"""

from __future__ import annotations

import asyncio
import os
from pathlib import Path

import pytest
import yaml

from aicd.config import AppConfig
from aicd.network_policy import (
    aicd_runtime_flags_from_config,
    apply_cli_block_external_network_flag,
    merge_env_block_external_network,
    outbound_tool_url_blocked_reason,
)
from aicd.env_probe import (
    CAPABILITIES_SUMMARY_FILENAME,
    RUNTIME_ENV_FILENAME,
    format_env_capabilities_for_prompt,
)
from aicd.tools.host_desktop_open import open_url_with_browser
from aicd.tools.site_bookmarks import fetch_site_summary


@pytest.mark.parametrize(
    "url",
    [
        "https://example.com/path",
        "http://example.org",
        "ws://example.com/ws",
        "wss://api.service.test/",
        "http://192.0.2.1/",
    ],
)
def test_outbound_blocked_external_hosts(url: str) -> None:
    assert outbound_tool_url_blocked_reason(url, block_external=True) is not None


@pytest.mark.parametrize(
    "url",
    [
        "http://127.0.0.1:8080/",
        "http://localhost/foo",
        "http://[::1]:3000/",
        "https://127.0.0.1/",
    ],
)
def test_outbound_allowed_loopback(url: str) -> None:
    assert outbound_tool_url_blocked_reason(url, block_external=True) is None


def test_outbound_disabled_when_flag_off() -> None:
    assert outbound_tool_url_blocked_reason("https://example.com/", block_external=False) is None


def test_file_and_about_allowed_under_block() -> None:
    assert outbound_tool_url_blocked_reason("file:///tmp/x", block_external=True) is None
    assert outbound_tool_url_blocked_reason("about:blank", block_external=True) is None


@pytest.mark.parametrize("val", ("1", "true", "yes", "on", "TRUE", "On"))
def test_merge_env_enables_block(monkeypatch: pytest.MonkeyPatch, val: str) -> None:
    cfg = AppConfig()
    monkeypatch.setenv("AICD_BLOCK_EXTERNAL_NETWORK", val)
    merge_env_block_external_network(cfg)
    assert cfg.agent.block_external_network is True


def test_merge_env_empty_keeps_false(monkeypatch: pytest.MonkeyPatch) -> None:
    cfg = AppConfig()
    cfg.agent.block_external_network = False
    monkeypatch.delenv("AICD_BLOCK_EXTERNAL_NETWORK", raising=False)
    merge_env_block_external_network(cfg)
    assert cfg.agent.block_external_network is False


def test_apply_cli_flag_sets_env(monkeypatch: pytest.MonkeyPatch) -> None:
    monkeypatch.delenv("AICD_BLOCK_EXTERNAL_NETWORK", raising=False)
    apply_cli_block_external_network_flag(True)
    assert os.environ.get("AICD_BLOCK_EXTERNAL_NETWORK") == "1"


def test_aicd_runtime_flags_from_config() -> None:
    cfg = AppConfig()
    cfg.agent.block_external_network = True
    assert aicd_runtime_flags_from_config(cfg) == {"block_external_network": True}


def test_capabilities_prompt_mentions_block_when_runtime_flag() -> None:
    data = {
        "generated_at": "t",
        "commands": {},
        "aicd_runtime": {"block_external_network": True},
    }
    text = format_env_capabilities_for_prompt(data, yaml_path="/x/y.yaml")
    assert "block_external_network=ON" in text


def test_desktop_open_url_blocks_public_http() -> None:
    r = open_url_with_browser(
        "https://example.com/",
        block_external_network=True,
        policy_check=None,
    )
    assert r.get("ok") is False
    assert r.get("network_policy") == "block_external_network"


def test_fetch_site_summary_blocked_before_http() -> None:
    async def _go() -> None:
        r = await fetch_site_summary(
            "https://example.com/",
            user_agent="test",
            timeout_sec=30.0,
            max_bytes=1000,
            block_external_network=True,
        )
        assert r.get("ok") is False
        assert r.get("network_policy") == "block_external_network"

    asyncio.run(_go())


def test_build_runtime_block_external_writes_snapshot(tmp_path: Path) -> None:
    """block_external_network=True 应写入 config 态、runtime_env.yaml 与能力摘要。"""
    from aicd.runtime import build_runtime

    home = tmp_path / "home"

    async def _go() -> None:
        ctx = await build_runtime(
            home=home,
            skip_first_setup=True,
            setup_tty_relaxed=True,
            block_external_network=True,
        )
        try:
            assert ctx.config.agent.block_external_network is True
            assert ctx.router._cfg.agent.block_external_network is True
            assert ctx.runtime_env_snapshot.get("aicd_runtime", {}).get(
                "block_external_network"
            ) is True
            yp = ctx.layout["data"] / RUNTIME_ENV_FILENAME
            assert yp.is_file()
            raw = yaml.safe_load(yp.read_text(encoding="utf-8"))
            assert raw.get("aicd_runtime", {}).get("block_external_network") is True
            cap = ctx.layout["data"] / CAPABILITIES_SUMMARY_FILENAME
            assert cap.is_file()
            assert "block_external_network=ON" in cap.read_text(encoding="utf-8")
        finally:
            await ctx.shutdown_graceful()

    asyncio.run(_go())
