"""Tests for LLMHosts configuration loading, env overrides, and serialisation."""

from __future__ import annotations

from typing import TYPE_CHECKING

import pytest
import toml

from llmhosts.config import LLMHostsConfig, load_config, save_default_config

if TYPE_CHECKING:
    from pathlib import Path

# -------------------------------------------------------------------
# Default config loading
# -------------------------------------------------------------------


@pytest.mark.unit
def test_default_config_loads_without_errors() -> None:
    """Creating a default LLMHostsConfig should succeed with sensible defaults."""
    config = LLMHostsConfig()
    assert config.server.host == "127.0.0.1"
    assert config.server.port == 4000
    assert config.cache.enabled is True
    assert config.router.prefer_local is True


@pytest.mark.unit
def test_load_config_missing_file(tmp_path: Path) -> None:
    """Loading from a non-existent file should return defaults, not raise."""
    config = load_config(tmp_path / "nonexistent.toml")
    assert isinstance(config, LLMHostsConfig)
    assert config.server.port == 4000


@pytest.mark.unit
def test_load_config_from_toml(tmp_path: Path) -> None:
    """A valid TOML file should be loaded and parsed correctly."""
    cfg_file = tmp_path / "config.toml"
    cfg_file.write_text(
        "[server]\nport = 9999\n\n[cache]\nenabled = false\n",
        encoding="utf-8",
    )
    config = load_config(cfg_file)
    assert config.server.port == 9999
    assert config.cache.enabled is False
    # Non-overridden fields keep their defaults
    assert config.server.host == "127.0.0.1"


@pytest.mark.unit
def test_load_config_with_engine(tmp_path: Path) -> None:
    """Config with [engine] section should load correctly."""
    cfg_file = tmp_path / "config.toml"
    cfg_file.write_text(
        '[server]\nport = 4000\n\n[engine]\ntype = "vllm"\nvllm_host = "http://custom:8000"\n',
        encoding="utf-8",
    )
    config = load_config(cfg_file)
    assert config.engine.type == "vllm"
    assert config.engine.vllm_host == "http://custom:8000"


@pytest.mark.unit
def test_load_config_bad_toml(tmp_path: Path) -> None:
    """Malformed TOML should fall back to defaults, not crash."""
    cfg_file = tmp_path / "bad.toml"
    cfg_file.write_text("this is not valid toml [[[", encoding="utf-8")
    config = load_config(cfg_file)
    assert isinstance(config, LLMHostsConfig)


# -------------------------------------------------------------------
# Env var overrides
# -------------------------------------------------------------------


@pytest.mark.unit
def test_env_override_port(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    monkeypatch.setenv("LLMHOSTS_SERVER_PORT", "7777")
    config = load_config(tmp_path / "nope.toml")
    assert config.server.port == 7777


@pytest.mark.unit
def test_env_override_bool(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    monkeypatch.setenv("LLMHOSTS_CACHE_ENABLED", "false")
    config = load_config(tmp_path / "nope.toml")
    assert config.cache.enabled is False


@pytest.mark.unit
def test_env_override_string(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    monkeypatch.setenv("LLMHOSTS_SERVER_HOST", "0.0.0.0")
    config = load_config(tmp_path / "nope.toml")
    assert config.server.host == "0.0.0.0"


@pytest.mark.unit
def test_env_overrides_take_priority_over_file(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    """Env vars should win over file values."""
    cfg_file = tmp_path / "config.toml"
    cfg_file.write_text("[server]\nport = 5555\n", encoding="utf-8")
    monkeypatch.setenv("LLMHOSTS_SERVER_PORT", "6666")
    config = load_config(cfg_file)
    assert config.server.port == 6666


# -------------------------------------------------------------------
# Config serialisation round-trip
# -------------------------------------------------------------------


@pytest.mark.unit
def test_config_round_trip() -> None:
    """Config -> dict -> Config should produce identical values."""
    original = LLMHostsConfig()
    data = original.model_dump()
    reconstructed = LLMHostsConfig.model_validate(data)
    assert original == reconstructed


@pytest.mark.unit
def test_config_json_round_trip() -> None:
    """Config -> JSON -> Config should preserve all fields."""
    original = LLMHostsConfig(
        server={"host": "0.0.0.0", "port": 8080},  # type: ignore[arg-type]
        cache={"enabled": False, "ttl_seconds": 7200},  # type: ignore[arg-type]
    )
    json_str = original.model_dump_json()
    reconstructed = LLMHostsConfig.model_validate_json(json_str)
    assert reconstructed.server.port == 8080
    assert reconstructed.cache.enabled is False
    assert reconstructed.cache.ttl_seconds == 7200


# -------------------------------------------------------------------
# save_default_config
# -------------------------------------------------------------------


@pytest.mark.unit
def test_save_default_config(tmp_path: Path) -> None:
    path = tmp_path / "config.toml"
    save_default_config(path)
    assert path.exists()
    # Should be valid TOML
    data = toml.load(path)
    assert "server" in data
    assert "cache" in data


@pytest.mark.unit
def test_save_default_config_no_overwrite(tmp_path: Path) -> None:
    path = tmp_path / "config.toml"
    path.write_text("existing", encoding="utf-8")
    with pytest.raises(FileExistsError):
        save_default_config(path)
