# -*- coding: utf-8 -*-
"""
回归测试：工具参数归一化守卫。

针对 LLM 偶尔把 list/dict/int/bool 参数序列化为 JSON 字符串的场景，
coerce 系列函数应安全还原为原生类型，避免下游迭代字符串字符或类型错误。
"""
import pytest

from abyss.tools.base import (
    coerce_int,
    coerce_bool,
    coerce_list_of_dicts,
    coerce_list_of_str,
)


# ---------- coerce_int ----------

class TestCoerceInt:
    """验证 int 参数归一化。"""

    @pytest.mark.parametrize("value, expected", [
        (10, 10),
        ("10", 10),
        ("0", 0),
        ("-5", -5),
        (10.0, 10),
        ("10.5", 10),  # float 字符串截断
    ])
    def test_valid_inputs(self, value, expected):
        assert coerce_int(value, default=5) == expected

    @pytest.mark.parametrize("value", [
        None,
        "",
        "abc",
        [],
        {},
        "1.2.3",
    ])
    def test_invalid_returns_default(self, value):
        assert coerce_int(value, default=42) == 42

    def test_none_with_no_default_returns_zero(self):
        assert coerce_int(None) == 0


# ---------- coerce_bool ----------

class TestCoerceBool:
    """验证 bool 参数归一化，特别防御字符串 'false' 被判 truthy。"""

    @pytest.mark.parametrize("value, expected", [
        (True, True),
        (False, False),
        ("true", True),
        ("false", False),
        ("True", True),
        ("False", False),
        ("TRUE", True),
        ("FALSE", False),
        (1, True),
        (0, False),
        ("1", True),
        ("0", False),
        ("yes", True),
        ("no", False),
        ("on", True),
        ("off", False),
    ])
    def test_truthy_falsy_strings(self, value, expected):
        assert coerce_bool(value) is expected

    @pytest.mark.parametrize("value", [
        None,
        "",
        [],
        {},
        "abc",
    ])
    def test_invalid_returns_default(self, value):
        assert coerce_bool(value, default=False) is False
        assert coerce_bool(value, default=True) is True


# ---------- coerce_list_of_dicts ----------

class TestCoerceListOfDicts:
    """验证 list[dict] 参数归一化（todo_write / spawn_subagents 场景）。"""

    def test_native_list_of_dicts(self):
        todos = [{"id": "1"}, {"id": "2"}]
        assert coerce_list_of_dicts(todos) == todos

    def test_json_string_of_list(self):
        s = '[{"id": "1"}, {"id": "2"}]'
        assert coerce_list_of_dicts(s) == [{"id": "1"}, {"id": "2"}]

    def test_json_string_filters_non_dict_items(self):
        s = '[{"id": "1"}, "junk", 42, null]'
        assert coerce_list_of_dicts(s) == [{"id": "1"}]

    def test_invalid_json_string_returns_empty(self):
        assert coerce_list_of_dicts("not json") == []

    @pytest.mark.parametrize("value", [
        None,
        "",
        [],
        {},
        42,
        True,
    ])
    def test_non_list_returns_empty(self, value):
        assert coerce_list_of_dicts(value) == []

    def test_filters_non_dict_elements_in_native_list(self):
        items = [{"ok": 1}, "bad", 42, None, {"ok": 2}]
        assert coerce_list_of_dicts(items) == [{"ok": 1}, {"ok": 2}]


# ---------- coerce_list_of_str ----------

class TestCoerceListOfStr:
    """验证 list[str] 参数归一化（knowledge_save tags 场景）。"""

    def test_native_list_of_str(self):
        tags = ["a", "b"]
        assert coerce_list_of_str(tags) == ["a", "b"]

    def test_json_string_of_list(self):
        s = '["a", "b"]'
        assert coerce_list_of_str(s) == ["a", "b"]

    def test_json_string_filters_non_str_items(self):
        s = '["a", 42, null, {"x": 1}]'
        assert coerce_list_of_str(s) == ["a"]

    def test_invalid_json_returns_empty(self):
        assert coerce_list_of_str("not json") == []

    @pytest.mark.parametrize("value", [
        None,
        "",
        [],
        {},
        42,
    ])
    def test_non_list_returns_empty(self, value):
        assert coerce_list_of_str(value) == []

    def test_filters_non_str_elements_in_native_list(self):
        items = ["a", 42, None, {"x": 1}, "b"]
        assert coerce_list_of_str(items) == ["a", "b"]
