# pyright: reportPrivateUsage=false
"""Mock tests: characters sub-resources (follow, detail, datacards, replies, settings, chat)."""

from typing import Any
from unittest.mock import AsyncMock, patch

from seaart import AsyncClient

from .conftest import make_ok as _ok


# ── Minimal mock data ────────────────────────────────────────────────────────

_COLLECT: dict[str, Any] = {"id": "col1"}

_STAT: dict[str, Any] = {
    "num_of_like": 0, "num_of_collection": 0, "num_of_task": 0,
    "num_of_view": 0, "num_of_artwork": 0, "rating": 0,
}
_COVER: dict[str, Any] = {"nsfw": 0, "url": "", "video": "", "width": 1, "height": 1}
_AUTHOR: dict[str, Any] = {
    "id": "a1", "avatar_frame": "", "name": "A", "num_of_character": 0,
    "join_stimulate": 0, "head": "", "is_follow": False, "follower_cnt": 0,
    "is_active": True,
}
_CHARACTER_DETAIL: dict[str, Any] = {
    "id": "ch1", "name": "Bot", "personal": "", "is_deep_seek": 0,
    "seo_keywords": "", "agent_simple_image": [], "releated_character": {"character_id": "", "character_name": ""},
    "apply_id": "", "default_apply_id": "", "gender": "f", "mask": "", "dialog_example": "",
    "allow_same": 0, "pop_recommend_model": 0,
    "original_vision": {"bg": {"nsfw": 0, "url": "", "video": "", "width": 0, "height": 0}},
    "audio_url": "", "evaluation": "", "gpt_model": "gpt-4", "init_mem_num": 0,
    "seo_session_keywords": "", "is_show_both_sidebar": False,
    "cover": _COVER, "scene": "", "support_first_voice": 0, "character_type": 0,
    "support_models": None, "support_voice": 1, "seo_description": "", "seo_session_title": "",
    "status": 1, "nsfw_level": 0, "timbre_id": "", "chat_people_num": 0,
    "fuzzy_start_number": 0, "create_at": 0, "first_msg": "Hi!", "comment_num": 0,
    "workflow_no": "", "act_model_setting": None, "tourist_limit_num": 5,
    "intro": "test", "publish_status": 1, "collect": None, "source_from": 0,
    "nsfw": 0, "support_reply_img": 0, "collect_num": 0, "source": "",
    "session_id": "s1", "free_chat_nums": 10, "chat_free_pattern_num": 0,
    "default_workflow_no": "", "stat": _STAT, "default_model": "gpt-4",
    "original_cover": _COVER, "vip_only": 0, "latest_session": [],
    "seo_title": "", "name_key": "", "seo_session_description": "",
    "categories": [], "vision": {"bg": {"nsfw": 0, "url": "", "video": "", "width": 0, "height": 0}},
    "config": "", "is_official_agent": 0, "video_url": "", "support_generate_img": 0,
    "author": _AUTHOR,
}

_CREATE_CHAT: dict[str, Any] = {
    "session_id": "s1", "msg": "Welcome!", "create_at": 0,
    "id": "c1", "support_voice": 1, "voice_duration": 0,
}

_TIMBRE: dict[str, Any] = {
    "id": "t1", "gender": "f", "tones": None, "name": "Voice",
    "url": "", "age": "adult", "tags": None, "langs": None,
    "cover": None, "Text": "", "author_id": "",
}
_CARD: dict[str, Any] = {
    "id": "card1", "username": "U", "age": 25, "gender": "m",
    "like": "", "dislike": "", "account_id": "a1", "extra": "",
}
_START_CHAT: dict[str, Any] = {
    "position": "", "full_screen": 0, "hide_bg": 0,
    "timbre": _TIMBRE, "free_times": 10, "enable_memory": 0,
    "card": _CARD, "is_main_card": 1, "limit_tourist_chat": 5,
    "latest_session_info": {
        "session_id": "", "x_msg_id": "", "x_response_msg_id": "", "original_msg_id": "",
        "msg_type": "", "user_msg": "", "msg_end": True, "response_msg": "",
        "response_json_msg": None, "create_at": 0, "support_voice": 0,
        "voice_duration": 0, "think_duration": 0, "status": 0, "gpt_model": "",
        "file_info": None, "is_active_upload": 0, "openai_msg": None,
        "search_results": None, "character_type": 0,
    },
    "guide_msg": {"msg_id": "", "title": "", "desc": ""},
    "free_model_guide_msg": {"msg_id": "", "title": "", "desc": ""},
    "chated_free_times": 0, "chated_num": 0, "today_free_chat_num": 0,
    "draw_lottery_condition_num": 0, "is_draw_lottery": 0,
    "reply_img_author_id": "", "is_new_business": 0,
    "chat_card_popup": {"popup_type": 0, "show_popup": 0},
}

_DATA_CARD: dict[str, Any] = {"id": "dc1"}
_MY_DATACARDS: dict[str, Any] = {"items": [{"id": "dc1"}, {"id": "dc2"}]}
_REPLY_RECOMMEND: dict[str, Any] = {"free_times": 5, "video_prompt_key": "", "items": []}
_HISTORY_CHAT: dict[str, Any] = {"items": [], "has_more": False}


# ── Characters (follow, unfollow, detail, models) ───────────────────────────


class TestCharactersFollow:
    async def test_follow_sends_character_no(self, client: AsyncClient) -> None:
        with patch.object(client, "request_route", new_callable=AsyncMock, return_value=_ok(_COLLECT)) as mock:
            await client.characters.follow("ch1")
            body: dict[str, Any] = mock.call_args.kwargs["json"]
            assert body["character_no"] == "ch1"

    async def test_follow_with_collection_no(self, client: AsyncClient) -> None:
        with patch.object(client, "request_route", new_callable=AsyncMock, return_value=_ok(_COLLECT)) as mock:
            await client.characters.follow("ch1", collection_no="col99")
            body: dict[str, Any] = mock.call_args.kwargs["json"]
            assert body["collection_no"] == "col99"

    async def test_unfollow_sends_id(self, client: AsyncClient) -> None:
        with patch.object(client, "request_route", new_callable=AsyncMock, return_value=_ok(_COLLECT)) as mock:
            await client.characters.unfollow("col1")
            body: dict[str, Any] = mock.call_args.kwargs["json"]
            assert body["id"] == "col1"


class TestCharactersDetail:
    async def test_detail_returns_envelope(self, client: AsyncClient) -> None:
        with patch.object(client, "request_route", new_callable=AsyncMock, return_value=_ok(_CHARACTER_DETAIL)):
            result = await client.characters.detail("ch1")
            assert result.value.id == "ch1"
            assert result.value.name == "Bot"

    async def test_detail_sends_id(self, client: AsyncClient) -> None:
        with patch.object(client, "request_route", new_callable=AsyncMock, return_value=_ok(_CHARACTER_DETAIL)) as mock:
            await client.characters.detail("ch1")
            body: dict[str, Any] = mock.call_args.kwargs["json"]
            assert body["id"] == "ch1"


class TestCharactersModels:
    async def test_models_returns_envelope(self, client: AsyncClient) -> None:
        data: dict[str, Any] = {
            "List": [], "functionality_list": [], "vip_open_use_key": [],
            "activity_item": {"discount_activity": False},
            "workflow_list": [],
            "model_title": {},
            "default_recommend": {
                "tourist_default_model": "", "tourist_default_workflow_no": "",
                "default_model": "", "default_workflow_no": "", "free_model_mapping": "",
            },
            "tile_list": [], "svip_free_model": [], "show_version": "v1",
        }
        with patch.object(client, "request_route", new_callable=AsyncMock, return_value=_ok(data)):
            result = await client.characters.models()
            assert result.status.code == 10000


# ── Chat (create, start, list, delete, delete_all) ──────────────────────────


class TestCharactersChatCreate:
    async def test_create_returns_session_id(self, client: AsyncClient) -> None:
        with patch.object(client, "request_route", new_callable=AsyncMock, return_value=_ok(_CREATE_CHAT)):
            result = await client.characters.chats.create("ch1")
            assert result.value.session_id == "s1"

    async def test_create_sends_character_id(self, client: AsyncClient) -> None:
        with patch.object(client, "request_route", new_callable=AsyncMock, return_value=_ok(_CREATE_CHAT)) as mock:
            await client.characters.chats.create("ch1", mask="custom")
            body: dict[str, Any] = mock.call_args.kwargs["json"]
            assert body["character_id"] == "ch1"
            assert body["mask"] == "custom"


class TestCharactersChatStart:
    async def test_start_returns_info(self, client: AsyncClient) -> None:
        with patch.object(client, "request_route", new_callable=AsyncMock, return_value=_ok(_START_CHAT)):
            result = await client.characters.chats.start("s1")
            assert result.value.free_times == 10

    async def test_start_sends_session_id(self, client: AsyncClient) -> None:
        with patch.object(client, "request_route", new_callable=AsyncMock, return_value=_ok(_START_CHAT)) as mock:
            await client.characters.chats.start("s1", dft_timbre_id="t1")
            body: dict[str, Any] = mock.call_args.kwargs["json"]
            assert body["session_id"] == "s1"
            assert body["dft_timbre_id"] == "t1"


class TestCharactersChatList:
    async def test_list_returns_items(self, client: AsyncClient) -> None:
        with patch.object(client, "request_route", new_callable=AsyncMock, return_value=_ok(_HISTORY_CHAT)):
            result = await client.characters.chats.list("ch1")
            assert result.value.has_more is False

    async def test_list_sends_pagination(self, client: AsyncClient) -> None:
        with patch.object(client, "request_route", new_callable=AsyncMock, return_value=_ok(_HISTORY_CHAT)) as mock:
            await client.characters.chats.list("ch1", page=2, page_size=10)
            body: dict[str, Any] = mock.call_args.kwargs["json"]
            assert body["character_id"] == "ch1"
            assert body["page"] == 2
            assert body["page_size"] == 10


class TestCharactersChatDelete:
    async def test_delete_sends_id(self, client: AsyncClient) -> None:
        with patch.object(client, "request_route", new_callable=AsyncMock, return_value=_ok()) as mock:
            await client.characters.chats.delete("s1")
            body: dict[str, Any] = mock.call_args.kwargs["json"]
            assert body["id"] == "s1"

    async def test_delete_all_sends_character_id(self, client: AsyncClient) -> None:
        with patch.object(client, "request_route", new_callable=AsyncMock, return_value=_ok()) as mock:
            await client.characters.chats.delete_all("ch1")
            body: dict[str, Any] = mock.call_args.kwargs["json"]
            assert body["character_id"] == "ch1"


# ── DataCards (create, my) ───────────────────────────────────────────────────


class TestDataCards:
    async def test_create_returns_datacard(self, client: AsyncClient) -> None:
        with patch.object(client, "request_route", new_callable=AsyncMock, return_value=_ok(_DATA_CARD)), \
             patch.object(client, "get_account_id", new_callable=AsyncMock, return_value="acc1"):
            result = await client.characters.datacards.create(username="Bob")
            assert result.value.id == "dc1"

    async def test_create_uses_provided_account_id(self, client: AsyncClient) -> None:
        with patch.object(client, "request_route", new_callable=AsyncMock, return_value=_ok(_DATA_CARD)) as mock, \
             patch.object(client, "get_account_id", new_callable=AsyncMock) as get_id:
            await client.characters.datacards.create(account_id="explicit-id")
            get_id.assert_not_awaited()
            body: dict[str, Any] = mock.call_args.kwargs["json"]
            assert body["account_id"] == "explicit-id"

    async def test_my_returns_list(self, client: AsyncClient) -> None:
        with patch.object(client, "request_route", new_callable=AsyncMock, return_value=_ok(_MY_DATACARDS)):
            result = await client.characters.datacards.my()
            items = result.value.items
            assert items is not None
            assert len(items) == 2


# ── Replies (recommend) ──────────────────────────────────────────────────────


class TestReplies:
    async def test_recommend_returns_data(self, client: AsyncClient) -> None:
        with patch.object(client, "request_route", new_callable=AsyncMock, return_value=_ok(_REPLY_RECOMMEND)):
            result = await client.characters.replies.recommend("ch1")
            assert result.value.free_times == 5

    async def test_recommend_sends_character_id(self, client: AsyncClient) -> None:
        with patch.object(client, "request_route", new_callable=AsyncMock, return_value=_ok(_REPLY_RECOMMEND)) as mock:
            await client.characters.replies.recommend("ch1", char_name="Bot", user_name="User")
            body: dict[str, Any] = mock.call_args.kwargs["json"]
            assert body["character_id"] == "ch1"
            assert body["char_name"] == "Bot"
            assert body["user_name"] == "User"


# ── Chat settings (update) ──────────────────────────────────────────────────


class TestChatSettings:
    async def test_update_sends_params(self, client: AsyncClient) -> None:
        with patch.object(client, "request_route", new_callable=AsyncMock, return_value=_ok()) as mock, \
             patch.object(client, "get_account_id", new_callable=AsyncMock, return_value="acc1"):
            await client.characters.chats.settings.update("ch1", gpt_model="gpt-4o", temperature=0.7)
            body: dict[str, Any] = mock.call_args.kwargs["json"]
            assert body["character_id"] == "ch1"
            assert body["gpt_model"] == "gpt-4o"
            assert body["temperature"] == 0.7
            assert body["account_id"] == "acc1"

    async def test_update_uses_explicit_account_id(self, client: AsyncClient) -> None:
        with patch.object(client, "request_route", new_callable=AsyncMock, return_value=_ok()) as mock, \
             patch.object(client, "get_account_id", new_callable=AsyncMock) as get_id:
            await client.characters.chats.settings.update("ch1", account_id="my-id")
            get_id.assert_not_awaited()
            body: dict[str, Any] = mock.call_args.kwargs["json"]
            assert body["account_id"] == "my-id"


# ── Characters settings (update, set_default) ───────────────────────────────


class TestCharactersSettings:
    async def test_update_sends_params(self, client: AsyncClient) -> None:
        with patch.object(client, "request_route", new_callable=AsyncMock, return_value=_ok()) as mock:
            await client.characters.settings.update(enable_memory=1, model="gpt-4o")
            body: dict[str, Any] = mock.call_args.kwargs["json"]
            assert body["enable_memory"] == 1
            assert body["gpt_model"] == "gpt-4o"

    async def test_set_default_sends_character_id(self, client: AsyncClient) -> None:
        with patch.object(client, "request_route", new_callable=AsyncMock, return_value=_ok()) as mock:
            await client.characters.settings.set_default("ch1")
            body: dict[str, Any] = mock.call_args.kwargs["json"]
            assert body["character_id"] == "ch1"
