"""被动交互基类。

包括：
    * :class:`AgentSdkResponse` —— Response 基类，封装"业务侧 → 机器人"的所有协议帧；
    * :class:`GreetResponse` —— ``on_greet_signal`` 的应答对象；
    * :class:`PassiveCallback` —— 业务侧子类的基类（提供 ``on_robot_online`` /
      ``on_greet_signal`` / ``on_state`` / ``on_face_info`` / ``on_video_frame`` 等默认空实现）。

具体的 ``Audio2LlmCallback`` / ``Audio2TtsResponse`` 等在各自模块里继承。
"""

from __future__ import annotations

import base64
import json
import logging
import time
from typing import TYPE_CHECKING, Any, Dict, Optional

from ..agent_meta import AgentMeta
from ..agent_param import AgentParam
from ..enums import AgentCallbackType, AgentEventType, AgentPolicy

if TYPE_CHECKING:  # 仅在类型检查时引入，避免循环依赖
    from ..agent_sdk import AgentSdk
    from ..client.linksky_client import LinkskyClient


_log = logging.getLogger("linksoul_agentsdk.passive")


# ---------------------------------------------------------------------------
# AgentSdkResponse
# ---------------------------------------------------------------------------


class AgentSdkResponse:
    """所有 Response 类的父类。

    具体子类通过 ``_send`` 模板发出协议帧；不同语义模式（``Audio2Llm`` / ``Audio2Tts`` ...）
    只是在子类里暴露不同的便捷方法。
    """

    def __init__(self, linksky_client: "LinkskyClient", agent_id: str, robot_cid: str) -> None:
        self._linksky_client = linksky_client
        self._agent_id = agent_id
        self._robot_cid = robot_cid
        self._update_ts = int(time.time() * 1000)

    # 方便上层管理器（例如 ResponseMgr 的回收逻辑）观测最近一次活动
    @property
    def update_ts(self) -> int:
        return self._update_ts

    @property
    def agent_id(self) -> str:
        return self._agent_id

    @property
    def robot_cid(self) -> str:
        return self._robot_cid

    # ----- 通用 send helper ----------------------------------------------------

    def _build_envelope(self, event_type: AgentEventType, event_id: str) -> Dict[str, Any]:
        return {
            "type": event_type.value,
            "agentId": self._agent_id,
            "agentMode": AgentPolicy.PASSIVE.value,
            "robotCid": self._robot_cid,
            "cid": self._robot_cid,
            "eventId": event_id,
        }

    def _send(
        self,
        event_type: AgentEventType,
        event_id: str,
        extra: Optional[Dict[str, Any]] = None,
        *,
        sanitize_keys: Optional[tuple] = None,
    ) -> None:
        """渲染 JSON 并通过 LinkskyClient 发出。

        ``sanitize_keys`` 用于把日志里"音频/视频"等大字段擦掉，与 Java 行为对齐。
        """
        envelope = self._build_envelope(event_type, event_id)
        if extra:
            envelope.update(extra)
        text = json.dumps(envelope, ensure_ascii=False)
        log_text = text
        if sanitize_keys:
            log_envelope = {k: v for k, v in envelope.items() if k not in sanitize_keys}
            log_text = json.dumps(log_envelope, ensure_ascii=False)
        self._linksky_client.send_text(self._agent_id, event_id, text, log_text)
        self._update_ts = int(time.time() * 1000)

    # ----- error -----------------------------------------------------------------

    def on_error(self, event_id: str, error_code: int, error_msg: str) -> None:
        self._send(
            AgentEventType.AGENTSDK_ERROR,
            event_id,
            {"errorCode": error_code, "errorMsg": error_msg},
        )

    # ----- ASR / LLM / VLM / TTS / Skill / Interrupt — 给子类调用 ------------------

    def _response_asr_middle(self, event_id: str, text: str) -> None:
        self._send(AgentEventType.AGENTSDK_ASR_RESPONSE_MIDDLE, event_id, {"text": text})

    def _response_asr_final(self, event_id: str, text: str) -> None:
        self._send(AgentEventType.AGENTSDK_ASR_RESPONSE_FINAL, event_id, {"text": text})

    def _response_llm_item_delta(self, event_id: str, item_id: str, text_delta: str) -> None:
        self._send(
            AgentEventType.AGENTSDK_LLM_RESPONSE_ITEM_DELTA,
            event_id,
            {"itemId": item_id, "text": text_delta},
        )

    def _response_llm_item_done(self, event_id: str, item_id: str) -> None:
        self._send(
            AgentEventType.AGENTSDK_LLM_RESPONSE_ITEM_DONE,
            event_id,
            {"itemId": item_id},
        )

    def _response_llm_done(self, event_id: str) -> None:
        self._send(AgentEventType.AGENTSDK_LLM_RESPONSE_DONE, event_id)

    def _response_vlm_item_delta(self, event_id: str, item_id: str, text_delta: str) -> None:
        self._send(
            AgentEventType.AGENTSDK_VLM_RESPONSE_ITEM_DELTA,
            event_id,
            {"itemId": item_id, "text": text_delta},
        )

    def _response_vlm_item_done(self, event_id: str, item_id: str) -> None:
        self._send(
            AgentEventType.AGENTSDK_VLM_RESPONSE_ITEM_DONE,
            event_id,
            {"itemId": item_id},
        )

    def _response_vlm_done(self, event_id: str) -> None:
        self._send(AgentEventType.AGENTSDK_VLM_RESPONSE_DONE, event_id)

    def _response_xlm_skill(
        self,
        event_id: str,
        item_id: str,
        skill_type: str,
        skill_name: str,
        skill_param: Optional[AgentParam],
    ) -> None:
        extra: Dict[str, Any] = {
            "itemId": item_id,
            "skillType": skill_type,
            "skillName": skill_name,
        }
        if skill_param is not None:
            extra["skillParam"] = skill_param.get_ext_param()
        self._send(AgentEventType.AGENTSDK_XLM_RESPONSE_SKILL, event_id, extra)

    def _response_xlm_interrupt(
        self,
        event_id: str,
        interrupt_type: str,
        interrupt_tips: Optional[str],
    ) -> None:
        self._send(
            AgentEventType.AGENTSDK_XLM_RESPONSE_INTERRUPT,
            event_id,
            {"interruptType": interrupt_type, "interruptTips": interrupt_tips},
        )

    def _response_tts_item_delta(self, event_id: str, item_id: str, audio: bytes) -> None:
        extra = {
            "itemId": item_id,
            "audio": base64.b64encode(audio).decode("ascii"),
            "audioLen": len(audio),
        }
        # 与 Java 一致：日志里去掉 audio 字段
        self._send(
            AgentEventType.AGENTSDK_TTS_RESPONSE_ITEM_DELTA,
            event_id,
            extra,
            sanitize_keys=("audio",),
        )

    def _response_tts_item_done(self, event_id: str, item_id: str) -> None:
        self._send(
            AgentEventType.AGENTSDK_TTS_RESPONSE_ITEM_DONE,
            event_id,
            {"itemId": item_id},
        )

    def _response_tts_done(self, event_id: str) -> None:
        self._send(AgentEventType.AGENTSDK_TTS_RESPONSE_DONE, event_id)

    # ----- Greet 专用（供 GreetResponse 调用）-------------------------------------

    def _response_greet_vlm_delta(self, event_id: str, text_delta: str) -> None:
        self._send(
            AgentEventType.AGENTSDK_GREET_RESPONSE_VLM_DELTA,
            event_id,
            {"text": text_delta},
        )

    def _response_greet_vlm_done(self, event_id: str) -> None:
        self._send(AgentEventType.AGENTSDK_GREET_RESPONSE_VLM_DONE, event_id)

    def _response_greet_tts_delta(self, event_id: str, text_delta: str) -> None:
        self._send(
            AgentEventType.AGENTSDK_GREET_RESPONSE_TTS_DELTA,
            event_id,
            {"text": text_delta},
        )

    def _response_greet_tts_done(self, event_id: str) -> None:
        self._send(AgentEventType.AGENTSDK_GREET_RESPONSE_TTS_DONE, event_id)


# ---------------------------------------------------------------------------
# GreetResponse
# ---------------------------------------------------------------------------


class GreetResponse(AgentSdkResponse):
    """打招呼信令的应答对象（对齐 Java ``GreetResponse``）。"""

    def on_greet_vlm_delta(self, event_id: str, text_delta: str) -> None:
        self._response_greet_vlm_delta(event_id, text_delta)

    def on_greet_vlm_done(self, event_id: str) -> None:
        self._response_greet_vlm_done(event_id)

    def on_greet_tts_delta(self, event_id: str, text_delta: str) -> None:
        self._response_greet_tts_delta(event_id, text_delta)

    def on_greet_tts_done(self, event_id: str) -> None:
        self._response_greet_tts_done(event_id)


# ---------------------------------------------------------------------------
# PassiveCallback —— 业务侧子类基类
# ---------------------------------------------------------------------------


class PassiveCallback:
    """所有被动回调（``Audio2LlmCallback`` 等）的基类。

    与 Java 一致：保留对 ``AgentSdk`` 的引用，子类通过 ``self.callback_type``
    标识本次注册的类型；下列共享方法（``on_robot_online`` / ``on_greet_signal`` ...）
    都有默认日志实现，按需重载。
    """

    #: 子类构造时赋值
    callback_type: AgentCallbackType = AgentCallbackType.ILLEGAL

    def __init__(self, agent_sdk: "AgentSdk") -> None:
        self.agent_sdk = agent_sdk

    # ----- 默认 no-op 实现 -------------------------------------------------------

    def on_robot_online(self, agent_id: str, agent_meta: AgentMeta) -> None:
        _log.info("on_robot_online => agent_id=%s, agent_meta=%s", agent_id, agent_meta)

    def on_robot_offline(self, agent_id: str) -> None:
        _log.info("on_robot_offline => agent_id=%s", agent_id)

    def on_face_info(self, agent_id: str, event_id: str, param: AgentParam) -> None:
        _log.info(
            "on_face_info => agent_id=%s, event_id=%s, param=%s",
            agent_id, event_id, param,
        )

    def on_video_frame(
        self,
        agent_id: str,
        event_id: str,
        flag: int,
        buf: Optional[bytes],
        is_key_frame: bool,
        local_ts: int,
        param: AgentParam,
    ) -> None:
        _log.info(
            "on_video_frame => agent_id=%s, event_id=%s, flag=%d, is_key=%s, ts=%d, param=%s",
            agent_id, event_id, flag, is_key_frame, local_ts, param,
        )

    def on_greet_signal(
        self,
        agent_id: str,
        event_id: str,
        param: AgentParam,
        response: GreetResponse,
    ) -> None:
        _log.info(
            "on_greet_signal => agent_id=%s, event_id=%s, param=%s",
            agent_id, event_id, param,
        )

    def on_state(self, agent_id: str, event_id: str, state_name: str, state_value: str) -> None:
        _log.info(
            "on_state => agent_id=%s, event_id=%s, name=%s, value=%s",
            agent_id, event_id, state_name, state_value,
        )
