"""Unit tests for config/env.py"""

import pytest
from gmi_ieops.config.env import EnvVar, AppEnv, Env


class TestEnvVar:

    @pytest.mark.parametrize("raw,type_,expected", [
        ("hello", str, "hello"),
        ("42", int, 42),
        ("3.14", float, 3.14),
        ("true", bool, True), ("1", bool, True), ("yes", bool, True),
        ("false", bool, False), ("0", bool, False), ("off", bool, False),
    ])
    def test_type_conversion(self, monkeypatch, raw, type_, expected):
        monkeypatch.setenv("T", raw)
        v = EnvVar("T", None, type_)
        assert v.get() == pytest.approx(expected) if type_ == float else v.get() == expected

    def test_list_conversion(self, monkeypatch):
        monkeypatch.setenv("T", " a , , b , c ")
        assert EnvVar("T", [], list).get() == ["a", "b", "c"]

    def test_default_when_unset(self, monkeypatch):
        monkeypatch.delenv("MISSING_X", raising=False)
        assert EnvVar("MISSING_X", "fb", str).get() == "fb"
        assert EnvVar("MISSING_X", 99, int).get() == 99

    def test_lazy_loading_caches(self, monkeypatch):
        monkeypatch.setenv("T", "first")
        v = EnvVar("T", "", str)
        assert v.get() == "first"
        monkeypatch.setenv("T", "second")
        assert v.get() == "first"  # cached

    def test_reload(self, monkeypatch):
        monkeypatch.setenv("T", "old")
        v = EnvVar("T", "", str)
        assert v.get() == "old"
        monkeypatch.setenv("T", "new")
        assert v.reload() == "new"

    def test_is_set(self, monkeypatch):
        monkeypatch.setenv("T", "x")
        v = EnvVar("T", "", str)
        assert v.is_set is True  # triggers loading

        monkeypatch.delenv("U", raising=False)
        assert EnvVar("U", "d", str).is_set is False


class TestEnvGroup:

    def test_access_reload_to_dict(self, monkeypatch):
        monkeypatch.setenv("APP_NAME", "test-1")
        g = AppEnv()
        g.reload()
        assert g.name == "test-1"

        monkeypatch.setenv("APP_NAME", "test-2")
        g.reload()
        assert g.name == "test-2"
        assert g.to_dict()["name"] == "test-2"

    def test_is_set_and_get_var(self, monkeypatch):
        monkeypatch.setenv("APP_NAME", "x")
        g = AppEnv()
        g.reload()
        assert g.is_set("name") is True
        assert g.is_set("nonexistent") is False
        assert g.get_var("name") is not None
        assert g.get_var("missing") is None


class TestEnv:

    @pytest.mark.parametrize("method,env_val,default,expected", [
        ("get", "hello", None, "hello"),
        ("get_int", "123", 0, 123),
        ("get_int", "abc", 42, 42),
        ("get_float", "2.718", 0.0, 2.718),
        ("get_float", "xyz", 1.0, 1.0),
        ("get_bool", "true", False, True),
    ])
    def test_direct_getters(self, monkeypatch, method, env_val, default, expected):
        monkeypatch.setenv("T", env_val)
        e = Env()
        result = getattr(e, method)("T", default) if default is not None else getattr(e, method)("T")
        if isinstance(expected, float):
            assert result == pytest.approx(expected)
        else:
            assert result == expected

    def test_get_list(self, monkeypatch):
        monkeypatch.setenv("L", "a|b|c")
        e = Env()
        assert e.get_list("L", sep="|") == ["a", "b", "c"]
        monkeypatch.delenv("M", raising=False)
        assert e.get_list("M") == []

    def test_to_dict_and_groups(self):
        e = Env()
        d = e.to_dict()
        for g in ["app", "log", "server", "model", "device", "api",
                   "storage", "register", "ssl", "model_sync", "metrics"]:
            assert g in d

    def test_reload_all(self, monkeypatch):
        monkeypatch.setenv("APP_NAME", "before")
        e = Env()
        _ = e.app.name
        monkeypatch.setenv("APP_NAME", "after")
        e.reload()
        assert e.app.name == "after"
