"""Tests for Settings."""

import json
import os
import tempfile

from brinkhaustools.common.settings import Settings


def _make_settings(initial: dict = None) -> tuple:
    """Create a Settings instance backed by a temp file."""
    fd, path = tempfile.mkstemp(suffix=".json")
    os.close(fd)
    if initial:
        with open(path, "w") as f:
            json.dump(initial, f)
    s = Settings(file_path=path)
    return s, path


def test_get_creates_missing_key():
    s, path = _make_settings()
    val = s.get(["section", "key"], "hello")
    assert val == "hello"
    # Should be persisted
    with open(path) as f:
        data = json.load(f)
    assert data["section"]["key"] == "hello"
    os.unlink(path)


def test_get_returns_existing_value():
    s, path = _make_settings({"section": {"key": "world"}})
    val = s.get(["section", "key"], "default")
    assert val == "world"
    os.unlink(path)


def test_set_and_get():
    s, path = _make_settings()
    s.set(["a", "b"], 42)
    assert s.get(["a", "b"], 0) == 42
    os.unlink(path)


def test_dot_notation():
    s, path = _make_settings({"level1": {"level2": "value"}})
    assert s.get("level1.level2", "x") == "value"
    s.set("level1.new_key", 123)
    assert s.get("level1.new_key", 0) == 123
    os.unlink(path)


def test_array_access():
    s, path = _make_settings({"items": [{"name": "a"}, {"name": "b"}]})
    assert s.get(["items", 0, "name"], "") == "a"
    assert s.get(["items", 1, "name"], "") == "b"
    os.unlink(path)


def test_exist():
    s, path = _make_settings({"a": {"b": 1}})
    assert s.exist(["a", "b"])
    assert s.exist("a.b")
    assert not s.exist(["a", "c"])
    assert not s.exist("a.c")
    os.unlink(path)


def test_length():
    s, path = _make_settings({"items": [1, 2, 3]})
    assert s.length(["items"]) == 3
    assert s.length(["nonexistent"]) == -1
    os.unlink(path)


def test_delete_key():
    s, path = _make_settings({"a": {"b": 1, "c": 2}})
    assert s.delete_key(["a", "b"])
    assert not s.exist(["a", "b"])
    assert s.exist(["a", "c"])
    os.unlink(path)


def test_move_key():
    s, path = _make_settings({"old": {"key": "val"}})
    assert s.move_key(["old", "key"], ["new", "key"])
    assert not s.exist(["old", "key"])
    assert s.get(["new", "key"], "") == "val"
    os.unlink(path)


def test_reload():
    s, path = _make_settings({"x": 1})
    # Modify file externally
    with open(path, "w") as f:
        json.dump({"x": 99}, f)
    s.reload()
    assert s.get(["x"], 0) == 99
    os.unlink(path)


def test_to_json_and_load_json():
    s, path = _make_settings({"a": 1})
    j = s.to_json()
    data = json.loads(j)
    assert data["a"] == 1

    s.load_json('{"b": 2}')
    assert s.get(["b"], 0) == 2
    assert not s.exist(["a"])
    os.unlink(path)


def test_type_compatibility():
    s, path = _make_settings({"val": 42})
    # int stored, float default → compatible
    assert s.get(["val"], 0.0) == 42
    # int stored, str default → incompatible, default wins
    assert s.get(["val"], "x") == "x"
    os.unlink(path)


def test_in_memory_only():
    s = Settings(file_path=None)
    s.set(["key"], "value")
    assert s.get(["key"], "") == "value"


def test_dot_notation_with_array_index():
    s, path = _make_settings({"items": ["a", "b", "c"]})
    assert s.get("items.1", "") == "b"
    os.unlink(path)


def test_get_value_from_env(monkeypatch):
    s, path = _make_settings()
    monkeypatch.setenv("TEST_VAR_123", "from_env")
    val = s.get_value_from_env("TEST_VAR_123", ["env_key"], "fallback")
    assert val == "from_env"
    # Falls back when env not set
    val2 = s.get_value_from_env("NONEXISTENT_VAR_XYZ", ["other_key"], "default_val")
    assert val2 == "default_val"
    os.unlink(path)
