"""Unit tests for the config framework."""

from argparse import ArgumentParser, Namespace
from dataclasses import dataclass
from pathlib import Path

import pytest

from byteb4rb1e.homeostat.util.config import (
    add_config_arguments,
    apply_cli_overrides,
    ensure_ini,
    load_ini,
)


@dataclass
class SampleConfig:
    name: str = "default"
    count: int = 10
    ratio: float = 0.5
    enabled: bool = True


class TestLoadIni:
    def test_loads_values(self, tmp_path):
        ini = tmp_path / "test.ini"
        ini.write_text(
            "[sample]\n"
            "name = custom\n"
            "count = 42\n"
            "ratio = 0.75\n"
        )
        config = load_ini(SampleConfig, ini)
        assert config.name == "custom"
        assert config.count == 42
        assert config.ratio == 0.75
        assert config.enabled is True  # default

    def test_missing_section_uses_defaults(self, tmp_path):
        ini = tmp_path / "test.ini"
        ini.write_text("[other]\nfoo = bar\n")
        config = load_ini(SampleConfig, ini)
        assert config.name == "default"
        assert config.count == 10

    def test_missing_file_uses_defaults(self, tmp_path):
        config = load_ini(
            SampleConfig, tmp_path / "missing.ini"
        )
        assert config.name == "default"

    def test_unknown_key_raises(self, tmp_path):
        ini = tmp_path / "test.ini"
        ini.write_text("[sample]\nunknown_key = bad\n")
        with pytest.raises(ValueError, match="unknown_key"):
            load_ini(SampleConfig, ini)

    def test_custom_section_name(self, tmp_path):
        ini = tmp_path / "test.ini"
        ini.write_text("[mysection]\nname = custom\n")
        config = load_ini(
            SampleConfig, ini, section="mysection"
        )
        assert config.name == "custom"

    def test_comments_ignored(self, tmp_path):
        ini = tmp_path / "test.ini"
        ini.write_text(
            "[sample]\n"
            "# this is a comment\n"
            "name = works  # inline comment\n"
        )
        config = load_ini(SampleConfig, ini)
        assert config.name == "works"


class TestAddConfigArguments:
    def test_generates_flags(self):
        parser = ArgumentParser()
        add_config_arguments(SampleConfig, parser)
        args = parser.parse_args(
            ["--name", "cli", "--count", "99"]
        )
        assert args.name == "cli"
        assert args.count == 99

    def test_defaults_are_none(self):
        parser = ArgumentParser()
        add_config_arguments(SampleConfig, parser)
        args = parser.parse_args([])
        assert args.name is None
        assert args.count is None

    def test_underscores_become_dashes(self):
        @dataclass
        class DashConfig:
            my_long_name: str = "x"

        parser = ArgumentParser()
        add_config_arguments(DashConfig, parser)
        args = parser.parse_args(
            ["--my-long-name", "val"]
        )
        assert args.my_long_name == "val"


class TestApplyCliOverrides:
    def test_overrides_set_values(self):
        config = SampleConfig()
        args = Namespace(name="override", count=None,
                         ratio=None, enabled=None)
        result = apply_cli_overrides(config, args)
        assert result.name == "override"
        assert result.count == 10  # unchanged

    def test_no_overrides_returns_same(self):
        config = SampleConfig()
        args = Namespace(name=None, count=None,
                         ratio=None, enabled=None)
        result = apply_cli_overrides(config, args)
        assert result.name == "default"
        assert result is config


class TestEnsureIni:
    def test_creates_file_if_missing(self, tmp_path):
        ini = tmp_path / "new.ini"
        assert not ini.exists()
        config = ensure_ini(SampleConfig, ini)
        assert ini.exists()
        assert config.name == "default"
        assert config.count == 10

    def test_created_file_has_all_fields(self, tmp_path):
        ini = tmp_path / "new.ini"
        ensure_ini(SampleConfig, ini)
        content = ini.read_text()
        assert "name" in content
        assert "count" in content
        assert "ratio" in content
        assert "enabled" in content

    def test_created_file_has_comments(self, tmp_path):
        ini = tmp_path / "new.ini"
        ensure_ini(SampleConfig, ini)
        content = ini.read_text()
        assert "# name (str)" in content
        assert "# count (int)" in content

    def test_reads_existing_file(self, tmp_path):
        ini = tmp_path / "existing.ini"
        ini.write_text("[sample]\ncount = 42\n")
        config = ensure_ini(SampleConfig, ini)
        assert config.count == 42

    def test_does_not_overwrite_existing(self, tmp_path):
        ini = tmp_path / "existing.ini"
        ini.write_text("[sample]\ncount = 42\n")
        ensure_ini(SampleConfig, ini)
        content = ini.read_text()
        assert content == "[sample]\ncount = 42\n"

    def test_created_file_is_loadable(self, tmp_path):
        ini = tmp_path / "new.ini"
        ensure_ini(SampleConfig, ini)
        config = load_ini(SampleConfig, ini)
        assert config.name == "default"
        assert config.count == 10
        assert config.ratio == 0.5


class TestIntegration:
    def test_ini_then_cli_override(self, tmp_path):
        ini = tmp_path / "test.ini"
        ini.write_text("[sample]\ncount = 42\n")
        config = load_ini(SampleConfig, ini)
        assert config.count == 42

        args = Namespace(name=None, count=99,
                         ratio=None, enabled=None)
        config = apply_cli_overrides(config, args)
        assert config.count == 99
        assert config.name == "default"

    def test_ensure_then_cli_override(self, tmp_path):
        ini = tmp_path / "new.ini"
        config = ensure_ini(SampleConfig, ini)
        assert config.count == 10

        args = Namespace(name=None, count=99,
                         ratio=None, enabled=None)
        config = apply_cli_overrides(config, args)
        assert config.count == 99
        assert config.name == "default"

        # Config file unchanged
        reloaded = load_ini(SampleConfig, ini)
        assert reloaded.count == 10
