"""AgentSdk 单例、register_* 注册表、生命周期。"""

from __future__ import annotations

import pytest

from linksoul_agentsdk.agent_sdk import AgentSdk
from linksoul_agentsdk.enums import AgentCallbackType
from linksoul_agentsdk.mgr.callback_type_mgr import AgentSdkCallbackTypeMgr
from linksoul_agentsdk.passive import (
    Asr2LlmCallback,
    Audio2TtsCallback,
)


_SECRET = "0123456789abcdef0123456789abcdef"


class _Asr2LlmCb(Asr2LlmCallback):
    def on_request(self, *a, **k):
        pass


class _Audio2TtsCb(Audio2TtsCallback):
    def on_request(self, *a, **k):
        pass


def test_create_returns_singleton_per_app_id():
    a = AgentSdk.create("wss://x/v1", "app1", "test-app-key", _SECRET)
    b = AgentSdk.create("wss://x/v1", "app1", "test-app-key", _SECRET)
    assert a is b
    c = AgentSdk.create("wss://x/v1", "app2", "test-app-key", _SECRET)
    assert a is not c
    assert set(AgentSdk.list_app_ids()) == {"app1", "app2"}


def test_get_returns_existing():
    sdk = AgentSdk.create("wss://x/v1", "app1", "test-app-key", _SECRET)
    assert AgentSdk.get("app1") is sdk
    assert AgentSdk.get("missing") is None


def test_register_callback_records_type(fake_client_factory):
    sdk = AgentSdk.create("wss://x/v1", "app1", "test-app-key", _SECRET)
    sdk.set_linksky_client_factory(fake_client_factory)

    sdk.register_asr2_llm(_Asr2LlmCb(sdk))
    sdk.register_audio2_tts(_Audio2TtsCb(sdk))

    assert AgentSdkCallbackTypeMgr.get("app1") == frozenset({
        AgentCallbackType.ASR_2_LLM,
        AgentCallbackType.AUDIO_2_TTS,
    })
    assert isinstance(sdk.get_robot_callback(AgentCallbackType.ASR_2_LLM), _Asr2LlmCb)
    assert isinstance(sdk.get_robot_callback(AgentCallbackType.AUDIO_2_TTS), _Audio2TtsCb)
    assert sdk.get_robot_callback(AgentCallbackType.AUDIO_2_LLM) is None
    assert sdk.get_robot_callback(None) is None


def test_register_rejects_wrong_type(fake_client_factory):
    sdk = AgentSdk.create("wss://x/v1", "app1", "test-app-key", _SECRET)
    sdk.set_linksky_client_factory(fake_client_factory)
    with pytest.raises(TypeError):
        sdk.register_asr2_llm(_Audio2TtsCb(sdk))  # type: ignore[arg-type]


def test_initialize_uses_factory_and_connects(fake_client_factory):
    sdk = AgentSdk.create("wss://x/v1", "app1", "test-app-key", _SECRET)
    sdk.set_linksky_client_factory(fake_client_factory)
    sdk.initialize()
    assert fake_client_factory.instances and fake_client_factory.instances[0].connect_called == 1


def test_set_factory_after_init_raises(fake_client_factory):
    sdk = AgentSdk.create("wss://x/v1", "app1", "test-app-key", _SECRET)
    sdk.set_linksky_client_factory(fake_client_factory)
    sdk.initialize()
    with pytest.raises(RuntimeError):
        sdk.set_linksky_client_factory(fake_client_factory)


def test_release_clears_state(fake_client_factory):
    sdk = AgentSdk.create("wss://x/v1", "app1", "test-app-key", _SECRET)
    sdk.set_linksky_client_factory(fake_client_factory)
    sdk.register_asr2_llm(_Asr2LlmCb(sdk))
    sdk.initialize()
    sdk.release()

    assert sdk.released is True
    assert fake_client_factory.instances[0].stop_called == 1
    assert AgentSdk.get("app1") is None
    assert AgentSdkCallbackTypeMgr.get("app1") == frozenset()


def test_on_auth_state_forwards_to_user_callback(fake_client_factory):
    from linksoul_agentsdk.agent_auth_callback import AgentAuthCallback

    captured = []

    class _Auth(AgentAuthCallback):
        def on_auth_state(self, app_id, code, msg):
            captured.append((app_id, code, msg))

    sdk = AgentSdk.create("wss://x/v1", "app1", "test-app-key", _SECRET)
    sdk.set_linksky_client_factory(fake_client_factory)
    sdk.register_auth(_Auth())
    sdk.on_auth_state(0, "ok")
    assert captured == [("app1", 0, "ok")]


def test_bind_unbind_agent_callback_type(fake_client_factory):
    sdk = AgentSdk.create("wss://x/v1", "app1", "test-app-key", _SECRET)
    sdk.set_linksky_client_factory(fake_client_factory)
    sdk.bind_agent_callback_type("AGENT_1", AgentCallbackType.ASR_2_LLM)
    assert sdk.get_agent_callback_type("AGENT_1") is AgentCallbackType.ASR_2_LLM
    sdk.unbind_agent_callback_type("AGENT_1")
    assert sdk.get_agent_callback_type("AGENT_1") is None


def test_register_task_flow_requires_initialize(fake_client_factory):
    from linksoul_agentsdk.active import TaskFlowRequest

    sdk = AgentSdk.create("wss://x/v1", "app1", "test-app-key", _SECRET)
    flow = TaskFlowRequest(sdk, "AGENT_1", "flow_1")
    with pytest.raises(RuntimeError):
        sdk.register_task_flow(flow)
