"""agent / llm 采样字段 YAML 往返。"""

from __future__ import annotations

from pathlib import Path

from aicd.config import (
    AppConfig,
    apply_agent_heartbeat_defaults_to_config_file,
    apply_missing_config_defaults_to_file,
    load_config,
    save_config,
)
from aicd.config_settings import apply_nested_settings_patch, format_settings_summary


def test_yaml_agent_lifecycle_coalesce_roundtrip(tmp_path: Path) -> None:
    p = tmp_path / "c.yaml"
    p.write_text(
        "agent:\n  lifecycleCoalesceSec: 0.5\n",
        encoding="utf-8",
    )
    cfg = load_config(p)
    assert cfg.agent.lifecycle_coalesce_sec == 0.5


def test_yaml_roundtrip_agent_llm(tmp_path: Path) -> None:
    p = tmp_path / "c.yaml"
    cfg = AppConfig()
    cfg.agent.max_tool_rounds = 40
    cfg.agent.sub_agent_max_tool_rounds = 8
    cfg.llm.temperature = 0.2
    cfg.llm.max_output_tokens = 2048
    cfg.llm.frequency_penalty = 0.1
    cfg.llm.extra_body = {"repeat_penalty": 1.05}
    cfg.llm.model_context_tokens = {"gpt-4o": 128_000, "*": 4096}
    save_config(p, cfg)
    cfg2 = load_config(p)
    assert cfg2.agent.max_tool_rounds == 40
    assert cfg2.agent.sub_agent_max_tool_rounds == 8
    assert cfg2.llm.temperature == 0.2
    assert cfg2.llm.max_output_tokens == 2048
    assert cfg2.llm.frequency_penalty == 0.1
    assert cfg2.llm.extra_body.get("repeat_penalty") == 1.05
    assert cfg2.llm.model_context_tokens["gpt-4o"] == 128_000
    assert cfg2.llm.model_context_tokens["*"] == 4096


def test_apply_patch_tool() -> None:
    cfg = AppConfig()
    ok, msg = apply_nested_settings_patch(
        cfg,
        {"llm": {"temperature": 0.5}, "agent": {"maxToolRounds": 10}},
    )
    assert ok and msg == "ok"
    assert cfg.llm.temperature == 0.5
    assert cfg.agent.max_tool_rounds == 10
    assert "max_tool_rounds=10" in format_settings_summary(cfg)


def test_apply_patch_model_context_tokens() -> None:
    cfg = AppConfig()
    ok, _ = apply_nested_settings_patch(
        cfg,
        {"llm": {"modelContextTokens": {"gpt-4o": 128_000, "default": 8192}}},
    )
    assert ok
    assert cfg.llm.model_context_tokens["gpt-4o"] == 128_000
    assert cfg.llm.model_context_tokens["default"] == 8192
    ok2, _ = apply_nested_settings_patch(cfg, {"llm": {"modelContextTokens": None}})
    assert ok2
    assert cfg.llm.model_context_tokens == {}


def test_apply_patch_invalid_restores() -> None:
    cfg = AppConfig()
    cfg.llm.temperature = 0.1
    ok, _ = apply_nested_settings_patch(cfg, {"llm": {"temperature": 99}})
    assert not ok
    assert cfg.llm.temperature == 0.1


def test_tool_rounds_clamped_to_max(tmp_path: Path) -> None:
    p = tmp_path / "c.yaml"
    p.write_text(
        "agent:\n  maxToolRounds: 800\n  subAgentMaxToolRounds: 1200\n",
        encoding="utf-8",
    )
    cfg = load_config(p)
    assert cfg.agent.max_tool_rounds == 500
    assert cfg.agent.sub_agent_max_tool_rounds == 500


def test_apply_patch_tool_rounds_clamped() -> None:
    cfg = AppConfig()
    ok, msg = apply_nested_settings_patch(cfg, {"agent": {"maxToolRounds": 500}})
    assert ok and msg == "ok"
    assert cfg.agent.max_tool_rounds == 500
    ok2, _ = apply_nested_settings_patch(cfg, {"agent": {"maxToolRounds": 600}})
    assert ok2
    assert cfg.agent.max_tool_rounds == 500


def test_apply_agent_heartbeat_defaults_persists_missing_keys(tmp_path: Path) -> None:
    p = tmp_path / "c.yaml"
    p.write_text("llm:\n  modelId: gpt-4o-mini\n", encoding="utf-8")
    cfg = load_config(p)
    cfg2 = apply_agent_heartbeat_defaults_to_config_file(p, cfg)
    assert cfg2.agent.idle_heartbeat_sec == 60
    assert cfg2.agent.sub_agent_idle_heartbeat_sec == 30
    assert cfg2.agent.lifecycle_coalesce_sec == 0.35
    raw = p.read_text(encoding="utf-8")
    assert "idleHeartbeatSec" in raw
    assert "subAgentIdleHeartbeatSec" in raw
    assert "lifecycleCoalesceSec" in raw
    cfg3 = load_config(p)
    assert cfg3.agent.idle_heartbeat_sec == 60
    assert cfg3.agent.sub_agent_idle_heartbeat_sec == 30
    assert cfg3.agent.lifecycle_coalesce_sec == 0.35


def test_apply_agent_heartbeat_defaults_respects_explicit_zero(tmp_path: Path) -> None:
    p = tmp_path / "c.yaml"
    p.write_text(
        "agent:\n  idleHeartbeatSec: 0\n  subAgentIdleHeartbeatSec: 0\n",
        encoding="utf-8",
    )
    cfg = load_config(p)
    cfg2 = apply_agent_heartbeat_defaults_to_config_file(p, cfg)
    assert cfg2.agent.idle_heartbeat_sec == 0
    assert cfg2.agent.sub_agent_idle_heartbeat_sec == 0


def test_apply_missing_config_adds_integrations(tmp_path: Path) -> None:
    p = tmp_path / "c.yaml"
    p.write_text("llm:\n  modelId: gpt-4o-mini\n", encoding="utf-8")
    cfg = apply_missing_config_defaults_to_file(p)
    assert cfg.integrations.web_fetch.max_response_bytes >= 4096
    assert "api.github.com" in cfg.integrations.github.api_base_url
    raw = p.read_text(encoding="utf-8")
    assert "integrations:" in raw
    assert "webFetch:" in raw


def test_apply_patch_integrations(tmp_path: Path) -> None:
    p = tmp_path / "c.yaml"
    save_config(p, AppConfig())
    cfg = load_config(p)
    ok, _ = apply_nested_settings_patch(
        cfg,
        {
            "integrations": {
                "webFetch": {"defaultTimeoutSec": 120},
                "github": {"token": "ghp_test_placeholder"},
            },
        },
    )
    assert ok
    assert cfg.integrations.web_fetch.default_timeout_sec == 120.0
    assert cfg.integrations.github.token == "ghp_test_placeholder"


def test_yaml_llm_retry_roundtrip(tmp_path: Path) -> None:
    p = tmp_path / "c.yaml"
    p.write_text(
        "llm:\n  maxRetries: 5\n  retryBaseDelaySec: 2.5\n",
        encoding="utf-8",
    )
    cfg = load_config(p)
    assert cfg.llm.max_retries == 5
    assert cfg.llm.retry_base_delay_sec == 2.5
