import json
import threading
import time
from types import SimpleNamespace
from unittest.mock import MagicMock

from bridge.context import Context, ContextType
from channel.chat_channel import ChatChannel
from channel.feishu import feishu_channel
from channel.feishu.feishu_channel import FeishuController, FeiShuChanel
from common.dequeue import Dequeue
from common.expired_dict import ExpiredDict


def _context(message_id: str) -> Context:
    return Context(
        ContextType.TEXT,
        message_id,
        {
            "session_id": "session-1",
            "msg": SimpleNamespace(msg_id=message_id),
        },
    )


def _bare_chat_channel(*contexts: Context) -> ChatChannel:
    channel = ChatChannel.__new__(ChatChannel)
    channel.lock = threading.RLock()
    channel.futures = {}
    queue = Dequeue()
    for context in contexts:
        queue.put(context)
    channel.sessions = {"session-1": [queue, MagicMock()]}
    return channel


def test_cancel_message_removes_only_recalled_queued_context(monkeypatch):
    channel = _bare_chat_channel(_context("m1"), _context("m2"), _context("m3"))
    registry = MagicMock()
    registry.cancel_request.return_value = False
    monkeypatch.setattr("agent.protocol.get_cancel_registry", lambda: registry)

    queued, active = channel.cancel_message("session-1", "m2")

    assert (queued, active) == (1, False)
    remaining = channel.sessions["session-1"][0]
    assert [remaining.get_nowait().get("msg").msg_id for _ in range(2)] == ["m1", "m3"]
    registry.cancel_request.assert_called_once_with("m2")


def test_cancel_message_targets_active_request_without_clearing_queue(monkeypatch):
    channel = _bare_chat_channel(_context("later"))
    registry = MagicMock()
    registry.cancel_request.return_value = True
    monkeypatch.setattr("agent.protocol.get_cancel_registry", lambda: registry)

    queued, active = channel.cancel_message("session-1", "active")

    assert (queued, active) == (0, True)
    remaining = channel.sessions["session-1"][0]
    assert remaining.get_nowait().get("msg").msg_id == "later"


def test_feishu_message_uses_message_id_for_precise_recall(monkeypatch):
    channel = FeiShuChanel()
    channel.receivedMsgs = ExpiredDict(60)
    channel._message_sessions = ExpiredDict(60)
    monkeypatch.setattr(channel, "fetch_access_token", lambda: "tenant-token")
    monkeypatch.setattr(channel, "_make_feishu_stream_callback", lambda *_: MagicMock())
    produced = []
    monkeypatch.setattr(channel, "produce", produced.append)

    channel._handle_message_event(
        {
            "app_id": "cli_bot",
            "sender": {"sender_id": {"open_id": "ou_user"}},
            "message": {
                "message_id": "om_recall_me",
                "chat_id": "oc_chat",
                "chat_type": "p2p",
                "message_type": "text",
                "create_time": str(int(time.time() * 1000)),
                "content": json.dumps({"text": "long task"}),
            },
        }
    )

    assert len(produced) == 1
    assert produced[0]["request_id"] == "om_recall_me"
    assert channel._message_sessions.get("om_recall_me") == "ou_user"


def test_feishu_recall_cancels_only_the_original_message(monkeypatch):
    channel = FeiShuChanel()
    channel._message_sessions = ExpiredDict(60)
    channel._message_sessions["om_recalled"] = "session-1"
    cancel_message = MagicMock(return_value=(0, True))
    monkeypatch.setattr(channel, "cancel_message", cancel_message)

    result = channel._handle_message_recalled_event(
        {"message_id": "om_recalled", "chat_id": "oc_chat"}
    )

    assert result == (0, True)
    cancel_message.assert_called_once_with("session-1", "om_recalled")
    assert channel._message_sessions.get("om_recalled") is None


def test_feishu_recall_ignores_unknown_message():
    channel = FeiShuChanel()
    channel._message_sessions = ExpiredDict(60)

    assert channel._handle_message_recalled_event({"message_id": "unknown"}) == (0, False)


def test_feishu_webhook_routes_message_recall(monkeypatch):
    channel = FeiShuChanel()
    channel.feishu_token = "verification-token"
    handle_recall = MagicMock(return_value=(1, False))
    monkeypatch.setattr(channel, "_handle_message_recalled_event", handle_recall)
    event = {"message_id": "om_recalled", "chat_id": "oc_chat"}
    request = {
        "header": {
            "event_type": "im.message.recalled_v1",
            "token": "verification-token",
        },
        "event": event,
    }
    monkeypatch.setattr(
        feishu_channel.web,
        "data",
        lambda: json.dumps(request).encode("utf-8"),
    )

    assert json.loads(FeishuController().POST()) == {"success": True}
    handle_recall.assert_called_once_with(event)
