"""消息路由：构造 inbound JSON 喂给 MessageRouter，验证 callback 是否按预期被调用。"""

from __future__ import annotations

import base64
import json
from typing import Any, Dict, List, Optional

import pytest

from linksoul_agentsdk.active import FlowStartCallback, TaskFlowRequest
from linksoul_agentsdk.agent_param import AgentParam
from linksoul_agentsdk.agent_sdk import AgentSdk
from linksoul_agentsdk.client.message_router import MessageRouter
from linksoul_agentsdk.passive import (
    Asr2LlmCallback,
    Audio2TtsCallback,
    AsrVideo2VlmCallback,
)

from .conftest import FakeLinkskyClient


def _envelope(event_type: str, **kw) -> Dict[str, Any]:
    base = {"type": event_type, "agentId": "AGENT_1", "robotCid": "cid_x", "eventId": "ev_1"}
    base.update(kw)
    return base


def _router_pair(initialized_sdk: AgentSdk):
    return MessageRouter(initialized_sdk, initialized_sdk.linksky_client), initialized_sdk.linksky_client


# ---------- 机器人上线 → on_robot_online + 绑定 ----------


def test_robot_state_online_binds_meta_and_callback(initialized_sdk):
    seen: List[Dict[str, Any]] = []

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

        def on_robot_online(self, agent_id, agent_meta):
            seen.append({"agent_id": agent_id, "meta": agent_meta})

    initialized_sdk.register_asr2_llm(Cb(initialized_sdk))
    router, _ = _router_pair(initialized_sdk)
    router.dispatch(_envelope(
        "agentsdk.robot_state.sync",
        state="online",
        callbackType="asr2llm",
        agentMeta={"def.wakeupWord": "X"},
    ))

    assert seen and seen[0]["agent_id"] == "AGENT_1"
    assert seen[0]["meta"].get_wakeup_word() == "X"
    assert initialized_sdk.get_agent_meta("AGENT_1").get_wakeup_word() == "X"
    from linksoul_agentsdk.enums import AgentCallbackType
    assert initialized_sdk.get_agent_callback_type("AGENT_1") is AgentCallbackType.ASR_2_LLM


# ---------- ASR 文本 → Asr2LlmCallback.on_request ----------


def test_asr_request_text_routes_to_asr2llm_callback(initialized_sdk):
    captured: List[Dict[str, Any]] = []

    class Cb(Asr2LlmCallback):
        def on_request(self, agent_id, event_id, text, param, response):
            captured.append({
                "agent_id": agent_id, "event_id": event_id,
                "text": text, "param": dict(param.get_ext_param()),
            })
            response.on_interrupt(event_id, "chat", None)
            response.on_llm_item_delta(event_id, "it_1", "ok")
            response.on_llm_done(event_id)

    initialized_sdk.register_asr2_llm(Cb(initialized_sdk))
    # 让 agent 已经绑定回调类型（模拟先上线）
    router, client = _router_pair(initialized_sdk)
    router.dispatch(_envelope(
        "agentsdk.robot_state.sync", state="online", callbackType="asr2llm",
    ))
    client.reset()

    router.dispatch(_envelope(
        "agentsdk.asr_request.text",
        text="今天天气",
        param={"k": "v"},
    ))

    assert captured == [{
        "agent_id": "AGENT_1",
        "event_id": "ev_1",
        "text": "今天天气",
        "param": {"k": "v"},
    }]
    # 期望出现 3 帧：interrupt → llm_delta → llm_done
    types = [s["json"]["type"] for s in client.sent]
    assert types == [
        "agentsdk.xlm_response.interrupt",
        "agentsdk.llm_response.item.delta",
        "agentsdk.llm_response.done",
    ]


# ---------- 拒识：on_request 不应答任何东西 ----------


def test_rejected_intent_emits_nothing(initialized_sdk):
    class Cb(Asr2LlmCallback):
        def on_request(self, agent_id, event_id, text, param, response):
            return  # 拒识 → 静默

    initialized_sdk.register_asr2_llm(Cb(initialized_sdk))
    router, client = _router_pair(initialized_sdk)
    router.dispatch(_envelope("agentsdk.robot_state.sync", state="online", callbackType="asr2llm"))
    client.reset()
    router.dispatch(_envelope("agentsdk.asr_request.text", text="噪音"))
    assert client.sent == []


# ---------- Audio2Tts 三段（start / append / commit） ----------


def test_audio2tts_three_stage(initialized_sdk):
    flags: List[int] = []
    audios: List[Optional[bytes]] = []

    class Cb(Audio2TtsCallback):
        def on_request(self, agent_id, event_id, item_id, flag, buf, param, response):
            flags.append(flag)
            audios.append(buf)

    initialized_sdk.register_audio2_tts(Cb(initialized_sdk))
    router, _ = _router_pair(initialized_sdk)
    router.dispatch(_envelope("agentsdk.robot_state.sync", state="online", callbackType="audio2tts"))

    router.dispatch(_envelope("agentsdk.audio_request.start"))
    router.dispatch(_envelope(
        "agentsdk.audio_request.append",
        audio=base64.b64encode(b"\x01\x02").decode(),
    ))
    router.dispatch(_envelope("agentsdk.audio_request.commit"))

    assert flags == [0, 1, 2]
    assert audios[0] is None
    assert audios[1] == b"\x01\x02"
    assert audios[2] is None


# ---------- AsrVideo2Vlm：先 text 后 image ----------


def test_asr_video2_vlm_text_then_image(initialized_sdk):
    received: List[Dict[str, Any]] = []

    class Cb(AsrVideo2VlmCallback):
        def on_request(self, agent_id, event_id, flag, text, buf, param, response):
            received.append({"flag": flag, "text": text, "buf_len": None if buf is None else len(buf)})

    initialized_sdk.register_asr_video2_vlm(Cb(initialized_sdk))
    router, _ = _router_pair(initialized_sdk)
    router.dispatch(_envelope("agentsdk.robot_state.sync", state="online", callbackType="asrVideo2vlm"))

    router.dispatch(_envelope("agentsdk.asr_request.text", text="桌上是什么"))
    router.dispatch(_envelope(
        "agentsdk.video_image_request.append",
        videoImage=base64.b64encode(b"\xff\xd8").decode(),
    ))

    assert received == [
        {"flag": 3, "text": "桌上是什么", "buf_len": None},
        {"flag": 5, "text": None, "buf_len": 2},
    ]


# ---------- on_greet_signal ----------


def test_greet_signal_invokes_callback_with_greet_response(initialized_sdk):
    captured: List[Any] = []

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

        def on_greet_signal(self, agent_id, event_id, param, response):
            response.on_greet_vlm_delta(event_id, "你好")
            response.on_greet_vlm_done(event_id)
            captured.append((agent_id, event_id))

    initialized_sdk.register_asr2_llm(Cb(initialized_sdk))
    router, client = _router_pair(initialized_sdk)
    router.dispatch(_envelope("agentsdk.robot_state.sync", state="online", callbackType="asr2llm"))
    client.reset()
    router.dispatch(_envelope("agentsdk.greet_request.signal", param={"k": 1}))

    assert captured == [("AGENT_1", "ev_1")]
    sent_types = [s["json"]["type"] for s in client.sent]
    assert sent_types == [
        "agentsdk.greet_response.vlm.delta",
        "agentsdk.greet_response.vlm.done",
    ]


# ---------- 状态推送 ----------


def test_state_request_meta_invokes_on_state(initialized_sdk):
    captured: List[Any] = []

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

        def on_state(self, agent_id, event_id, name, value):
            captured.append((name, value))

    initialized_sdk.register_asr2_llm(Cb(initialized_sdk))
    router, _ = _router_pair(initialized_sdk)
    router.dispatch(_envelope("agentsdk.robot_state.sync", state="online", callbackType="asr2llm"))
    router.dispatch(_envelope(
        "agentsdk.state_request.meta",
        stateName="battery", stateValue="80",
    ))
    router.dispatch(_envelope(
        "agentsdk.state_request.meta",
        stateName="position", stateValue={"x": 1, "y": 2},
    ))
    assert captured == [("battery", "80"), ("position", '{"x": 1, "y": 2}')]


# ---------- Flow ack 链路 ----------


def test_flow_start_ack_chain(initialized_sdk):
    rec = []

    class Cb(FlowStartCallback):
        def on_start_request_ack(self, flow_id, event_id, code, msg):
            rec.append(("req", flow_id, event_id, code, msg))

        def on_start_execute_ack(self, flow_id, event_id, code, msg):
            rec.append(("exec", flow_id, event_id, code, msg))

    flow = TaskFlowRequest(initialized_sdk, "AGENT_1", "flow_1")
    initialized_sdk.register_task_flow(flow)
    event_id = flow.start_request("[{}]", Cb(), 100)

    router, _ = _router_pair(initialized_sdk)
    router.dispatch({
        "type": "agentsdk.flow_start.request_ack",
        "agentId": "AGENT_1", "flowId": "flow_1", "eventId": event_id,
        "code": 0, "msg": "ok",
    })
    router.dispatch({
        "type": "agentsdk.flow_start.execute_ack",
        "agentId": "AGENT_1", "flowId": "flow_1", "eventId": event_id,
        "code": 0, "msg": "ok",
    })

    assert rec == [
        ("req", "flow_1", event_id, 0, "ok"),
        ("exec", "flow_1", event_id, 0, "ok"),
    ]


# ---------- 未知 event 类型不抛 ----------


def test_unknown_event_type_returns_false(initialized_sdk):
    router, _ = _router_pair(initialized_sdk)
    assert router.dispatch({"type": "what.is.this"}) is False


def test_handle_text_with_invalid_json_returns_false(initialized_sdk):
    router, _ = _router_pair(initialized_sdk)
    assert router.handle_text("not-json") is False
