import unittest
from argparse import Namespace
from collections import OrderedDict
from typing import Any

import pytest

import confuse


class BuildDictTests(unittest.TestCase):
    def test_pure_dicts(self):
        config = {"foo": {"bar": 1}}
        result = confuse.util.build_dict(config)
        assert 1 == result["foo"]["bar"]

    def test_namespaces(self):
        config = Namespace(foo=Namespace(bar=2), another=1)
        result = confuse.util.build_dict(config)
        assert 2 == result["foo"]["bar"]
        assert 1 == result["another"]

    def test_dot_sep_keys(self):
        config: dict[str, Any] = {"foo.bar": 1}
        result = confuse.util.build_dict(config.copy())
        assert 1 == result["foo.bar"]

        result = confuse.util.build_dict(config.copy(), sep=".")
        assert 1 == result["foo"]["bar"]

    def test_dot_sep_keys_clobber(self):
        args: list[tuple[str, Any]] = [("foo.bar", 1), ("foo.bar.zar", 2)]
        config = OrderedDict(args)
        result = confuse.util.build_dict(config.copy(), sep=".")
        assert {"zar": 2} == result["foo"]["bar"]
        assert 2 == result["foo"]["bar"]["zar"]

        # Reverse and do it again! (should be stable)
        args.reverse()
        config = OrderedDict(args)
        result = confuse.util.build_dict(config.copy(), sep=".")
        assert {"zar": 2} == result["foo"]["bar"]
        assert 2 == result["foo"]["bar"]["zar"]

    def test_dot_sep_keys_no_clobber(self):
        args: list[tuple[str, Any]] = [
            ("foo.bar", 1),
            ("foo.far", 2),
            ("foo.zar.dar", 4),
        ]
        config = OrderedDict(args)
        result = confuse.util.build_dict(config.copy(), sep=".")
        assert 1 == result["foo"]["bar"]
        assert 2 == result["foo"]["far"]
        assert 4 == result["foo"]["zar"]["dar"]

    def test_adjacent_underscores_sep_keys(self):
        config: dict[str, Any] = {"foo__bar_baz": 1}
        result = confuse.util.build_dict(config.copy())
        assert 1 == result["foo__bar_baz"]

        result = confuse.util.build_dict(config.copy(), sep="_")
        assert 1 == result["foo"][""]["bar"]["baz"]

        result = confuse.util.build_dict(config.copy(), sep="__")
        assert 1 == result["foo"]["bar_baz"]

    def test_keep_none(self):
        config = {"foo": None}
        result = confuse.util.build_dict(config.copy())
        with pytest.raises(KeyError):
            result["foo"]

        result = confuse.util.build_dict(config.copy(), keep_none=True)
        assert None is result["foo"]

    def test_keep_none_with_nested(self):
        config = {"foo": {"bar": None}}
        result = confuse.util.build_dict(config.copy())
        assert {} == result["foo"]

        result = confuse.util.build_dict(config.copy(), keep_none=True)
        assert None is result["foo"]["bar"]
