# pyright: reportPrivateUsage=false
"""Unit tests: normalize_history and _role_to_int."""

import pytest

from seaart._internal._schemas.characters.characters_chat import HistoryMessage
from seaart._utils import _role_to_int, normalize_history


# ── _role_to_int ─────────────────────────────────────────────────────────────


class TestRoleToInt:
    @pytest.mark.parametrize("role", [1, "user", "u", "human", "client", "1"])
    def test_user_roles(self, role: int | str) -> None:
        assert _role_to_int(role) == 1

    @pytest.mark.parametrize("role", [2, "assistant", "ai", "model", "bot", "2"])
    def test_assistant_roles(self, role: int | str) -> None:
        assert _role_to_int(role) == 2

    def test_case_insensitive(self) -> None:
        assert _role_to_int("USER") == 1
        assert _role_to_int("Assistant") == 2

    def test_invalid_int(self) -> None:
        with pytest.raises(ValueError, match="must be 1 or 2"):
            _role_to_int(3)

    def test_invalid_string(self) -> None:
        with pytest.raises(ValueError, match="Unknown role"):
            _role_to_int("moderator")


# ── normalize_history ────────────────────────────────────────────────────────


class TestNormalizeHistory:
    def test_none_returns_empty(self) -> None:
        assert normalize_history(None) == []

    def test_string_becomes_user_message(self) -> None:
        result = normalize_history("hello")
        assert len(result) == 2  # user msg + auto assistant
        assert result[0].role == 1
        assert result[0].content == "hello"

    def test_string_no_ensure_assistant(self) -> None:
        result = normalize_history("hello", ensure_last_assistant=False)
        assert len(result) == 1
        assert result[0].role == 1

    def test_history_message_passthrough(self) -> None:
        msg = HistoryMessage(role=2, content="hi", msg_id="m1")
        result = normalize_history(msg, ensure_last_assistant=False)
        assert result == [msg]

    def test_tuple_2(self) -> None:
        result = normalize_history(("user", "hello"), ensure_last_assistant=False)
        assert len(result) == 1
        assert result[0].role == 1
        assert result[0].content == "hello"

    def test_tuple_3(self) -> None:
        result = normalize_history(
            ("assistant", "reply", "msg123"), ensure_last_assistant=False
        )
        assert result[0].role == 2
        assert result[0].msg_id == "msg123"

    def test_tuple_invalid_length(self) -> None:
        with pytest.raises(TypeError, match="Tuple history item must be"):
            normalize_history((1, "a", "b", "c"), ensure_last_assistant=False)  # type: ignore[arg-type]

    def test_tuple_non_string_content(self) -> None:
        with pytest.raises(TypeError, match="content must be str"):
            normalize_history((1, 123), ensure_last_assistant=False)  # type: ignore[arg-type]

    def test_dict_input(self) -> None:
        result = normalize_history(
            {"role": "ai", "content": "hi"}, ensure_last_assistant=False
        )
        assert result[0].role == 2
        assert result[0].content == "hi"

    def test_dict_defaults(self) -> None:
        result = normalize_history({}, ensure_last_assistant=False)
        assert result[0].role == 1
        assert result[0].content == ""

    def test_list_of_mixed(self) -> None:
        items: list[object] = [
            "hello",
            ("assistant", "world"),
            {"role": 1, "content": "again"},
        ]
        result = normalize_history(items, ensure_last_assistant=False)  # type: ignore[arg-type]
        assert len(result) == 3
        assert result[0].role == 1
        assert result[1].role == 2
        assert result[2].role == 1

    def test_ensure_last_assistant_appends(self) -> None:
        result = normalize_history([("user", "hi")])
        assert result[-1].role == 2
        assert result[-1].content == " "

    def test_ensure_last_assistant_no_append_if_already(self) -> None:
        result = normalize_history([("assistant", "bye")])
        assert len(result) == 1

    def test_object_with_content_attr(self) -> None:
        class FakeMsg:
            content = "from obj"
            role = "user"

        result = normalize_history([FakeMsg()], ensure_last_assistant=False)  # type: ignore[list-item]
        assert result[0].content == "from obj"
        assert result[0].role == 1

    def test_unsupported_type_raises(self) -> None:
        with pytest.raises(TypeError, match="Unsupported history item type"):
            normalize_history([42], ensure_last_assistant=False)  # type: ignore[list-item]
