import pytest
import re
from typing import Any, Tuple
from dataclasses import dataclass
from app_settings_dict import Settings


def test_simple_settings() -> None:
    settings = Settings(
        settings_file_path="C:/Users/chris/Documents/sample_settings_file_name.json",
        default_factories={
            "key1": lambda: "value1",
        },
        data={
            "key1": "hello",
            "key2": "world",
        },
    )
    assert settings["key1"] == "hello"
    assert settings["key2"] == "world"
    del settings["key1"]
    del settings["key2"]
    assert "key1" not in settings
    assert "key2" not in settings
    assert settings["key1"] == "value1"
    with pytest.raises(KeyError):
        settings["key2"]


def test_default_settings() -> None:
    settings = Settings(
        settings_file_path="sample settings file name.json",
        default_factories={
            "key1": lambda: "value1",
            "key2": lambda: "value2",
            "key3": lambda: "value3",
        },
        default_settings={
            "key3": [],
        },
        data={
            "key1": "hello",
            "key2": "world",
        },
    )
    assert settings["key1"] == "hello"
    assert settings["key2"] == "world"
    assert settings["key3"] == "value3"
    del settings["key3"]
    assert settings["key3"] == "value3"
    settings.reset("key3")
    assert settings["key3"] == []
    settings["key3"] = "something"
    assert settings["key3"] == "something"
    settings.reset_all()
    assert settings["key1"] == "hello"
    assert settings["key2"] == "world"
    assert settings["key3"] == []


def test_load_without_file() -> None:
    def sample_prompt_function(settings: Settings) -> Settings:
        # s = input("Enter the settings: ")
        return settings.update({"key1": "a", "key2": "b"})

    settings = Settings(
        settings_file_path="not a real file.yaml",
        prompt_user_for_all_settings=sample_prompt_function,
        default_factories={
            "key1": lambda: "value1",
            "key2": lambda: "value2",
            "key3": lambda: "value3",
        },
        default_settings={
            "key3": [],
            "key4": "value4",
        },
        data={
            "key1": "hello",
            "key2": "world",
        },
    )
    assert settings["key1"] == "hello"
    assert settings["key2"] == "world"
    assert settings["key3"] == "value3"
    settings.load(fallback_option="prompt user")
    assert settings["key1"] == "a"
    assert settings["key2"] == "b"
    assert settings["key3"] == "value3"
    with pytest.raises(KeyError):
        settings["key4"]
    settings.load(fallback_option="default settings")
    assert settings["key1"] == "a"
    assert settings["key2"] == "b"
    assert settings["key3"] == "value3"
    assert settings["key4"] == "value4"
    settings.clear()
    settings.load(fallback_option="default settings")
    assert settings["key1"] == "hello"
    assert settings["key2"] == "world"
    assert settings["key3"] == []
    assert settings["key4"] == "value4"
    with pytest.raises(ValueError):
        settings.load(fallback_option="invalid option")


def test_load_after_empty() -> None:
    settings = Settings(
        settings_file_path="sample settings file name.json",
        prompt_user_for_all_settings=lambda: 1 / 0,
        default_factories={
            "key1": lambda: "value1",
        },
        default_settings={
            "key1": [],
        },
        data={
            "key1": "hello",
        },
    )
    assert settings["key1"] == "hello"
    settings.clear()
    assert settings["key1"] == "value1"


def test_prompt() -> None:
    def sample_prompt_function() -> Any:
        # s = input("Enter a setting: ")
        return "a"

    settings = Settings(
        settings_file_path="sample settings file name.json",
        prompt_user_for_all_settings=lambda: {"key1": "a", "key2": "b"},
        default_factories={
            "key1": sample_prompt_function,
            "key2": lambda: "value2",
            "key3": lambda: "value3",
        },
        default_settings={
            "key3": [],
        },
        data={
            "key1": "hello",
            "key2": "world",
        },
    )
    assert settings["key1"] == "hello"
    settings.prompt("key1")
    assert settings["key1"] == "a"


def test_changing_settings_before_load() -> None:
    settings = Settings(
        settings_file_path="sample settings file name.json",
        default_factories={
            "key1": lambda: "value1",
        },
        default_settings={
            "key1": [],
        },
        data={
            "key1": "hello",
        },
    )
    assert settings["key1"] == "hello"
    settings.load(fallback_option="default settings")
    assert settings["key1"] == "hello"
    settings["key1"] = "a"
    settings.load(fallback_option="default settings")
    assert settings["key1"] == "a"


def test_update() -> None:
    settings = Settings(
        settings_file_path="sample settings file name.json",
        default_factories={
            "key1": lambda: "value1",
        },
        default_settings={
            "key1": [],
        },
        data={
            "key1": "hello",
        },
    )
    assert settings["key1"] == "hello"
    settings.update({"key1": "a"})
    assert settings["key1"] == "a"
    settings.update({"key2": "b"})
    assert settings["key2"] == "b"


def test_Settings__is_using_json() -> None:
    settings = Settings(
        settings_file_path="sample_settings_file_name.json",
        default_factories={
            "key1": lambda: "value1",
        },
        data={
            "key1": "hello",
            "key2": "world",
        },
    )
    assert settings._Settings__is_using_json()
    settings.settings_file_path = "sample_settings_file_name.yaml"
    assert not settings._Settings__is_using_json()


def test_load_from_dict() -> None:
    settings = Settings()
    settings.load_from_dict(
        {
            "key1": "hello",
            "key2": "world",
        }
    )
    assert len(settings.data) == 0
    settings = Settings(
        data={
            "key1": "a",
            "key2": "b",
        }
    )
    settings.load_from_dict(
        {
            "key1": "c",
            "key2": "d",
        }
    )
    assert settings.data["key1"] == "c"
    assert settings.data["key2"] == "d"


def test_dump_to_dict() -> None:
    settings = Settings(
        settings_file_path="sample_settings_file_name.json",
        data={
            "key1": "hello",
            "key2": "world",
        },
    )
    assert settings.dump_to_dict() == {
        "key1": "hello",
        "key2": "world",
    }


def test_nested_Settings() -> None:
    settings = Settings(
        settings_file_path="sample_settings_file_name.json",
        default_settings={
            "key6": [],
            "key7": Settings(
                data={
                    "key8": "value8",
                }
            ),
        },
        data={
            "key1": "hello",
            "key2": "world",
            "key3": "value3",
            "key4": Settings(
                settings_file_path="why would anyone want an inner file though.yaml",
                data={
                    "key5": "value5",
                },
            ),
        },
    )
    assert settings.dump_to_dict() == {
        "key1": "hello",
        "key2": "world",
        "key3": "value3",
        "key4": {
            "key5": "value5",
        },
    }


def test_creating_setting_after_init() -> None:
    settings = Settings(
        settings_file_path="sample_settings_file_name.json",
        default_settings={
            "key1": [],
            "key2": "value2",
        },
    )
    with pytest.raises(KeyError):
        settings["key3"] = "value3"


def test_prompt_error() -> None:
    settings = Settings(
        settings_file_path="nonexistent file.json",
        default_settings={
            "key1": [],
            "key2": "value2",
        },
    )
    with pytest.raises(ValueError):
        settings.load(fallback_option="prompt user")


def test_nested_setting_loaders_and_dumpers() -> None:
    @dataclass
    class Coords:
        x: int
        y: int

        def __init__(self, x_and_y: Tuple[int, int]) -> None:
            self.x = x_and_y[0]
            self.y = x_and_y[1]

    settings = Settings(
        setting_loader=Coords,
        setting_dumper=lambda obj: (obj.x, obj.y),
        data={
            "location 1": Coords(x_and_y=(1, 2)),
            "location 2": Coords(x_and_y=(3, 4)),
            "patterns": Settings(
                setting_loader=re.compile,
                setting_dumper=lambda x: x.pattern,
                data={
                    "phone number pattern": re.compile(r"\d{3}-?\d{3}-?\d{4}"),
                    "email address pattern": re.compile(
                        r"[\w\d.+-]+@[\w\d.-]+\.[\w\d]+"
                    ),
                },
            ),
        },
    )
    settings_dict = settings.dump_to_dict()
    assert settings_dict["location 1"] == (1, 2)
    assert settings_dict["location 2"] == (3, 4)
    assert settings_dict["patterns"]["phone number pattern"] == r"\d{3}-?\d{3}-?\d{4}"
    assert (
        settings_dict["patterns"]["email address pattern"]
        == r"[\w\d.+-]+@[\w\d.-]+\.[\w\d]+"
    )
    settings.load_from_dict(settings_dict)
    assert settings["location 1"] == Coords(x_and_y=(1, 2))
    assert settings["location 2"] == Coords(x_and_y=(3, 4))
    assert settings["patterns"]["phone number pattern"] == re.compile(
        r"\d{3}-?\d{3}-?\d{4}"
    )
    assert settings["patterns"]["email address pattern"] == re.compile(
        r"[\w\d.+-]+@[\w\d.-]+\.[\w\d]+"
    )


def test_init_without_keywords() -> None:
    with pytest.raises(TypeError):
        Settings("sample settings file path.json")

[((180, 367), 'app_settings_dict.Settings', 'Settings', ([], {'settings_file_path': '"""C:/Users/chris/Documents/sample_settings_file_name.json"""', 'default_factories': "{'key1': lambda : 'value1'}", 'data': "{'key1': 'hello', 'key2': 'world'}"}), "(settings_file_path=\n    'C:/Users/chris/Documents/sample_settings_file_name.json',\n    default_factories={'key1': lambda : 'value1'}, data={'key1': 'hello',\n    'key2': 'world'})\n", (188, 367), False, 'from app_settings_dict import Settings\n'), ((792, 1038), 'app_settings_dict.Settings', 'Settings', ([], {'settings_file_path': '"""sample settings file name.json"""', 'default_factories': "{'key1': lambda : 'value1', 'key2': lambda : 'value2', 'key3': lambda :\n    'value3'}", 'default_settings': "{'key3': []}", 'data': "{'key1': 'hello', 'key2': 'world'}"}), "(settings_file_path='sample settings file name.json',\n    default_factories={'key1': lambda : 'value1', 'key2': lambda : 'value2',\n    'key3': lambda : 'value3'}, default_settings={'key3': []}, data={'key1':\n    'hello', 'key2': 'world'})\n", (800, 1038), False, 'from app_settings_dict import Settings\n'), ((1850, 2163), 'app_settings_dict.Settings', 'Settings', ([], {'settings_file_path': '"""not a real file.yaml"""', 'prompt_user_for_all_settings': 'sample_prompt_function', 'default_factories': "{'key1': lambda : 'value1', 'key2': lambda : 'value2', 'key3': lambda :\n    'value3'}", 'default_settings': "{'key3': [], 'key4': 'value4'}", 'data': "{'key1': 'hello', 'key2': 'world'}"}), "(settings_file_path='not a real file.yaml',\n    prompt_user_for_all_settings=sample_prompt_function, default_factories=\n    {'key1': lambda : 'value1', 'key2': lambda : 'value2', 'key3': lambda :\n    'value3'}, default_settings={'key3': [], 'key4': 'value4'}, data={\n    'key1': 'hello', 'key2': 'world'})\n", (1858, 2163), False, 'from app_settings_dict import Settings\n'), ((3220, 3436), 'app_settings_dict.Settings', 'Settings', ([], {'settings_file_path': '"""sample settings file name.json"""', 'prompt_user_for_all_settings': '(lambda : 1 / 0)', 'default_factories': "{'key1': lambda : 'value1'}", 'default_settings': "{'key1': []}", 'data': "{'key1': 'hello'}"}), "(settings_file_path='sample settings file name.json',\n    prompt_user_for_all_settings=lambda : 1 / 0, default_factories={'key1':\n    lambda : 'value1'}, default_settings={'key1': []}, data={'key1': 'hello'})\n", (3228, 3436), False, 'from app_settings_dict import Settings\n'), ((3789, 4110), 'app_settings_dict.Settings', 'Settings', ([], {'settings_file_path': '"""sample settings file name.json"""', 'prompt_user_for_all_settings': "(lambda : {'key1': 'a', 'key2': 'b'})", 'default_factories': "{'key1': sample_prompt_function, 'key2': lambda : 'value2', 'key3': lambda :\n    'value3'}", 'default_settings': "{'key3': []}", 'data': "{'key1': 'hello', 'key2': 'world'}"}), "(settings_file_path='sample settings file name.json',\n    prompt_user_for_all_settings=lambda : {'key1': 'a', 'key2': 'b'},\n    default_factories={'key1': sample_prompt_function, 'key2': lambda :\n    'value2', 'key3': lambda : 'value3'}, default_settings={'key3': []},\n    data={'key1': 'hello', 'key2': 'world'})\n", (3797, 4110), False, 'from app_settings_dict import Settings\n'), ((4413, 4584), 'app_settings_dict.Settings', 'Settings', ([], {'settings_file_path': '"""sample settings file name.json"""', 'default_factories': "{'key1': lambda : 'value1'}", 'default_settings': "{'key1': []}", 'data': "{'key1': 'hello'}"}), "(settings_file_path='sample settings file name.json',\n    default_factories={'key1': lambda : 'value1'}, default_settings={'key1':\n    []}, data={'key1': 'hello'})\n", (4421, 4584), False, 'from app_settings_dict import Settings\n'), ((4976, 5147), 'app_settings_dict.Settings', 'Settings', ([], {'settings_file_path': '"""sample settings file name.json"""', 'default_factories': "{'key1': lambda : 'value1'}", 'default_settings': "{'key1': []}", 'data': "{'key1': 'hello'}"}), "(settings_file_path='sample settings file name.json',\n    default_factories={'key1': lambda : 'value1'}, default_settings={'key1':\n    []}, data={'key1': 'hello'})\n", (4984, 5147), False, 'from app_settings_dict import Settings\n'), ((5487, 5644), 'app_settings_dict.Settings', 'Settings', ([], {'settings_file_path': '"""sample_settings_file_name.json"""', 'default_factories': "{'key1': lambda : 'value1'}", 'data': "{'key1': 'hello', 'key2': 'world'}"}), "(settings_file_path='sample_settings_file_name.json',\n    default_factories={'key1': lambda : 'value1'}, data={'key1': 'hello',\n    'key2': 'world'})\n", (5495, 5644), False, 'from app_settings_dict import Settings\n'), ((5942, 5952), 'app_settings_dict.Settings', 'Settings', ([], {}), '()\n', (5950, 5952), False, 'from app_settings_dict import Settings\n'), ((6116, 6157), 'app_settings_dict.Settings', 'Settings', ([], {'data': "{'key1': 'a', 'key2': 'b'}"}), "(data={'key1': 'a', 'key2': 'b'})\n", (6124, 6157), False, 'from app_settings_dict import Settings\n'), ((6442, 6548), 'app_settings_dict.Settings', 'Settings', ([], {'settings_file_path': '"""sample_settings_file_name.json"""', 'data': "{'key1': 'hello', 'key2': 'world'}"}), "(settings_file_path='sample_settings_file_name.json', data={'key1':\n    'hello', 'key2': 'world'})\n", (6450, 6548), False, 'from app_settings_dict import Settings\n'), ((7586, 7700), 'app_settings_dict.Settings', 'Settings', ([], {'settings_file_path': '"""sample_settings_file_name.json"""', 'default_settings': "{'key1': [], 'key2': 'value2'}"}), "(settings_file_path='sample_settings_file_name.json',\n    default_settings={'key1': [], 'key2': 'value2'})\n", (7594, 7700), False, 'from app_settings_dict import Settings\n'), ((7875, 7981), 'app_settings_dict.Settings', 'Settings', ([], {'settings_file_path': '"""nonexistent file.json"""', 'default_settings': "{'key1': [], 'key2': 'value2'}"}), "(settings_file_path='nonexistent file.json', default_settings={\n    'key1': [], 'key2': 'value2'})\n", (7883, 7981), False, 'from app_settings_dict import Settings\n'), ((688, 711), 'pytest.raises', 'pytest.raises', (['KeyError'], {}), '(KeyError)\n', (701, 711), False, 'import pytest\n'), ((2593, 2616), 'pytest.raises', 'pytest.raises', (['KeyError'], {}), '(KeyError)\n', (2606, 2616), False, 'import pytest\n'), ((3083, 3108), 'pytest.raises', 'pytest.raises', (['ValueError'], {}), '(ValueError)\n', (3096, 3108), False, 'import pytest\n'), ((7764, 7787), 'pytest.raises', 'pytest.raises', (['KeyError'], {}), '(KeyError)\n', (7777, 7787), False, 'import pytest\n'), ((8044, 8069), 'pytest.raises', 'pytest.raises', (['ValueError'], {}), '(ValueError)\n', (8057, 8069), False, 'import pytest\n'), ((9587, 9623), 're.compile', 're.compile', (['"""\\\\d{3}-?\\\\d{3}-?\\\\d{4}"""'], {}), "('\\\\d{3}-?\\\\d{3}-?\\\\d{4}')\n", (9597, 9623), False, 'import re\n'), ((9696, 9746), 're.compile', 're.compile', (['"""[\\\\w\\\\d.+-]+@[\\\\w\\\\d.-]+\\\\.[\\\\w\\\\d]+"""'], {}), "('[\\\\w\\\\d.+-]+@[\\\\w\\\\d.-]+\\\\.[\\\\w\\\\d]+')\n", (9706, 9746), False, 'import re\n'), ((9808, 9832), 'pytest.raises', 'pytest.raises', (['TypeError'], {}), '(TypeError)\n', (9821, 9832), False, 'import pytest\n'), ((9842, 9884), 'app_settings_dict.Settings', 'Settings', (['"""sample settings file path.json"""'], {}), "('sample settings file path.json')\n", (9850, 9884), False, 'from app_settings_dict import Settings\n'), ((6894, 6927), 'app_settings_dict.Settings', 'Settings', ([], {'data': "{'key8': 'value8'}"}), "(data={'key8': 'value8'})\n", (6902, 6927), False, 'from app_settings_dict import Settings\n'), ((7132, 7240), 'app_settings_dict.Settings', 'Settings', ([], {'settings_file_path': '"""why would anyone want an inner file though.yaml"""', 'data': "{'key5': 'value5'}"}), "(settings_file_path=\n    'why would anyone want an inner file though.yaml', data={'key5': 'value5'})\n", (7140, 7240), False, 'from app_settings_dict import Settings\n'), ((8790, 8826), 're.compile', 're.compile', (['"""\\\\d{3}-?\\\\d{3}-?\\\\d{4}"""'], {}), "('\\\\d{3}-?\\\\d{3}-?\\\\d{4}')\n", (8800, 8826), False, 'import re\n'), ((8871, 8921), 're.compile', 're.compile', (['"""[\\\\w\\\\d.+-]+@[\\\\w\\\\d.-]+\\\\.[\\\\w\\\\d]+"""'], {}), "('[\\\\w\\\\d.+-]+@[\\\\w\\\\d.-]+\\\\.[\\\\w\\\\d]+')\n", (8881, 8921), False, 'import re\n')]