from __future__ import annotations

import json

from lark_oapi.api.im.v1 import (
    CreateMessageRequest,
    CreateMessageRequestBody,
    CreatePinRequest,
    CreatePinRequestBody,
    DeleteMessageRequest,
    DeletePinRequest,
    GetChatMembersRequest,
    GetChatRequest,
    GetMessageRequest,
    ListChatRequest,
    ListMessageRequest,
    ReplyMessageRequest,
    ReplyMessageRequestBody,
    SearchChatRequest,
    CreateMessageReactionRequest,
    CreateMessageReactionRequestBody,
    DeleteMessageReactionRequest,
    Emoji,
)

from ._client import get_client, make_option
from ._scopes import InsufficientScopeError, MODULE_SCOPES


def _receive_id_type(receive_id: str) -> str:
    if receive_id.startswith("ou_"):
        return "open_id"
    if receive_id.startswith("oc_"):
        return "chat_id"
    if receive_id.startswith("on_"):
        return "union_id"
    return "chat_id"


def _encode_content(content: str | dict) -> str:
    if isinstance(content, dict):
        return json.dumps(content, ensure_ascii=False)
    return content


def send(
    token: str,
    receive_id: str,
    msg_type: str,
    content: str | dict,
    *,
    receive_id_type: str | None = None,
) -> str:
    client = get_client()
    id_type = receive_id_type or _receive_id_type(receive_id)
    request = (
        CreateMessageRequest.builder()
        .receive_id_type(id_type)
        .request_body(
            CreateMessageRequestBody.builder()
            .receive_id(receive_id)
            .msg_type(msg_type)
            .content(_encode_content(content))
            .build()
        )
        .build()
    )
    response = client.im.v1.message.create(request, make_option(token))
    if not response.success():
        if response.code == 99991679:
            raise InsufficientScopeError(MODULE_SCOPES.get("message", []), module="message")
        raise RuntimeError(
            f"发送消息失败: code={response.code}, msg={response.msg}"
        )
    return response.data.message_id


def send_text(token: str, receive_id: str, text: str, **kwargs) -> str:
    return send(token, receive_id, "text", {"text": text}, **kwargs)


def send_markdown(token: str, receive_id: str, markdown: str, title: str = "", **kwargs) -> str:
    content = {
        "config": {"wide_screen_mode": True},
        "elements": [{"tag": "markdown", "content": markdown}],
    }
    if title:
        content["header"] = {"title": {"tag": "plain_text", "content": title}, "template": "blue"}
    return send(token, receive_id, "interactive", content, **kwargs)


def send_card(token: str, receive_id: str, card: dict, **kwargs) -> str:
    return send(token, receive_id, "interactive", card, **kwargs)


def send_post(token: str, receive_id: str, post_content: dict, **kwargs) -> str:
    return send(token, receive_id, "post", post_content, **kwargs)


def reply(
    token: str,
    message_id: str,
    msg_type: str,
    content: str | dict,
    *,
    reply_in_thread: bool = False,
) -> str:
    client = get_client()
    request = (
        ReplyMessageRequest.builder()
        .message_id(message_id)
        .request_body(
            ReplyMessageRequestBody.builder()
            .msg_type(msg_type)
            .content(_encode_content(content))
            .reply_in_thread(reply_in_thread)
            .build()
        )
        .build()
    )
    response = client.im.v1.message.reply(request, make_option(token))
    if not response.success():
        if response.code == 99991679:
            raise InsufficientScopeError(MODULE_SCOPES.get("message", []), module="message")
        raise RuntimeError(
            f"回复消息失败: code={response.code}, msg={response.msg}"
        )
    return response.data.message_id


def reply_text(token: str, message_id: str, text: str, **kwargs) -> str:
    return reply(token, message_id, "text", {"text": text}, **kwargs)


def delete(token: str, message_id: str) -> None:
    client = get_client()
    request = DeleteMessageRequest.builder().message_id(message_id).build()
    response = client.im.v1.message.delete(request, make_option(token))
    if not response.success():
        if response.code == 99991679:
            raise InsufficientScopeError(MODULE_SCOPES.get("message", []), module="message")
        raise RuntimeError(
            f"撤回消息失败: code={response.code}, msg={response.msg}"
        )


def get(token: str, message_id: str) -> object:
    client = get_client()
    request = GetMessageRequest.builder().message_id(message_id).build()
    response = client.im.v1.message.get(request, make_option(token))
    if not response.success():
        if response.code == 99991679:
            raise InsufficientScopeError(MODULE_SCOPES.get("message", []), module="message")
        raise RuntimeError(
            f"获取消息失败: code={response.code}, msg={response.msg}"
        )
    items = response.data.items or []
    return items[0] if items else None


def list_messages(
    token: str,
    container_id: str,
    *,
    container_id_type: str = "chat",
    start_time: str | None = None,
    end_time: str | None = None,
    page_size: int = 50,
    max_count: int = 500,
) -> list:
    client = get_client()
    builder = (
        ListMessageRequest.builder()
        .container_id_type(container_id_type)
        .container_id(container_id)
        .page_size(page_size)
    )
    if start_time:
        builder = builder.start_time(start_time)
    if end_time:
        builder = builder.end_time(end_time)

    all_items: list = []
    page_token: str | None = None

    while len(all_items) < max_count:
        if page_token:
            builder = builder.page_token(page_token)
        response = client.im.v1.message.list(builder.build(), make_option(token))
        if not response.success():
            if response.code == 99991679:
                raise InsufficientScopeError(MODULE_SCOPES.get("message", []), module="message")
            raise RuntimeError(
                f"获取消息列表失败: code={response.code}, msg={response.msg}"
            )
        items = response.data.items or []
        all_items.extend(items)
        if not response.data.has_more:
            break
        page_token = response.data.page_token

    return all_items[:max_count]


def add_reaction(token: str, message_id: str, emoji_type: str) -> str:
    client = get_client()
    request = (
        CreateMessageReactionRequest.builder()
        .message_id(message_id)
        .request_body(
            CreateMessageReactionRequestBody.builder()
            .reaction_type(Emoji.builder().emoji_type(emoji_type).build())
            .build()
        )
        .build()
    )
    response = client.im.v1.message_reaction.create(request, make_option(token))
    if not response.success():
        if response.code == 99991679:
            raise InsufficientScopeError(MODULE_SCOPES.get("message", []), module="message")
        raise RuntimeError(
            f"添加 reaction 失败: code={response.code}, msg={response.msg}"
        )
    return response.data.reaction_id


def remove_reaction(token: str, message_id: str, reaction_id: str) -> None:
    client = get_client()
    request = (
        DeleteMessageReactionRequest.builder()
        .message_id(message_id)
        .reaction_id(reaction_id)
        .build()
    )
    response = client.im.v1.message_reaction.delete(request, make_option(token))
    if not response.success():
        if response.code == 99991679:
            raise InsufficientScopeError(MODULE_SCOPES.get("message", []), module="message")
        raise RuntimeError(
            f"删除 reaction 失败: code={response.code}, msg={response.msg}"
        )


def list_chats(
    token: str,
    *,
    page_size: int = 20,
    max_count: int = 200,
) -> list[dict]:
    client = get_client()
    all_items: list[dict] = []
    page_token: str | None = None
    while len(all_items) < max_count:
        builder = (
            ListChatRequest.builder()
            .user_id_type("open_id")
            .page_size(page_size)
        )
        if page_token:
            builder = builder.page_token(page_token)
        response = client.im.v1.chat.list(builder.build(), make_option(token))
        if not response.success():
            if response.code == 99991679:
                raise InsufficientScopeError(MODULE_SCOPES.get("message", []), module="message")
            raise RuntimeError(f"获取群列表失败: code={response.code}, msg={response.msg}")
        for c in response.data.items or []:
            all_items.append({
                "chat_id": getattr(c, "chat_id", ""),
                "name": getattr(c, "name", ""),
                "description": getattr(c, "description", ""),
                "owner_id": getattr(c, "owner_id", ""),
                "chat_type": getattr(c, "chat_type", ""),
            })
        if not response.data.has_more:
            break
        page_token = response.data.page_token
    return all_items[:max_count]


def get_chat(token: str, chat_id: str) -> dict:
    client = get_client()
    request = GetChatRequest.builder().chat_id(chat_id).user_id_type("open_id").build()
    response = client.im.v1.chat.get(request, make_option(token))
    if not response.success():
        if response.code == 99991679:
            raise InsufficientScopeError(MODULE_SCOPES.get("message", []), module="message")
        raise RuntimeError(f"获取群信息失败: code={response.code}, msg={response.msg}")
    c = response.data
    return {
        "chat_id": getattr(c, "chat_id", ""),
        "name": getattr(c, "name", ""),
        "description": getattr(c, "description", ""),
        "owner_id": getattr(c, "owner_id", ""),
        "chat_type": getattr(c, "chat_type", ""),
        "member_count": getattr(c, "member_count", 0),
        "external": getattr(c, "external", False),
    }


def get_chat_members(
    token: str,
    chat_id: str,
    *,
    page_size: int = 50,
    max_count: int = 500,
) -> list[dict]:
    client = get_client()
    all_items: list[dict] = []
    page_token: str | None = None
    while len(all_items) < max_count:
        builder = (
            GetChatMembersRequest.builder()
            .chat_id(chat_id)
            .member_id_type("open_id")
            .page_size(page_size)
        )
        if page_token:
            builder = builder.page_token(page_token)
        response = client.im.v1.chat_members.get(builder.build(), make_option(token))
        if not response.success():
            if response.code == 99991679:
                raise InsufficientScopeError(MODULE_SCOPES.get("message", []), module="message")
            raise RuntimeError(f"获取群成员失败: code={response.code}, msg={response.msg}")
        for m in response.data.items or []:
            all_items.append({
                "member_id": getattr(m, "member_id", ""),
                "name": getattr(m, "name", ""),
                "tenant_key": getattr(m, "tenant_key", ""),
                "member_id_type": getattr(m, "member_id_type", ""),
            })
        if not response.data.has_more:
            break
        page_token = response.data.page_token
    return all_items[:max_count]


def pin_message(token: str, message_id: str) -> dict:
    client = get_client()
    request = (
        CreatePinRequest.builder()
        .request_body(CreatePinRequestBody.builder().message_id(message_id).build())
        .build()
    )
    response = client.im.v1.pin.create(request, make_option(token))
    if not response.success():
        if response.code == 99991679:
            raise InsufficientScopeError(MODULE_SCOPES.get("message", []), module="message")
        raise RuntimeError(f"Pin 消息失败: code={response.code}, msg={response.msg}")
    pin = response.data.pin
    return {
        "message_id": getattr(pin, "message_id", message_id),
        "chat_id": getattr(pin, "chat_id", ""),
        "operator_id": getattr(pin, "operator_id", ""),
        "create_time": getattr(pin, "create_time", ""),
    }


def unpin_message(token: str, message_id: str) -> dict:
    client = get_client()
    request = DeletePinRequest.builder().message_id(message_id).build()
    response = client.im.v1.pin.delete(request, make_option(token))
    if not response.success():
        if response.code == 99991679:
            raise InsufficientScopeError(MODULE_SCOPES.get("message", []), module="message")
        raise RuntimeError(f"取消 Pin 失败: code={response.code}, msg={response.msg}")
    return {"message_id": message_id, "unpinned": True}


def list_messages_in_chat(
    token: str,
    chat_id: str,
    *,
    start_time: str | None = None,
    end_time: str | None = None,
    page_size: int = 50,
    max_count: int = 500,
) -> list:
    return list_messages(
        token,
        chat_id,
        container_id_type="chat",
        start_time=start_time,
        end_time=end_time,
        page_size=page_size,
        max_count=max_count,
    )


def search_chats(
    token: str,
    query: str,
    *,
    page_size: int = 20,
    max_count: int = 100,
) -> list[dict]:
    client = get_client()
    all_items: list[dict] = []
    page_token: str | None = None
    while len(all_items) < max_count:
        builder = (
            SearchChatRequest.builder()
            .query(query)
            .user_id_type("open_id")
            .page_size(min(page_size, 100))
        )
        if page_token:
            builder = builder.page_token(page_token)
        response = client.im.v1.chat.search(builder.build(), make_option(token))
        if not response.success():
            if response.code == 99991679:
                raise InsufficientScopeError(MODULE_SCOPES.get("message", []), module="message")
            raise RuntimeError(f"搜索群失败: code={response.code}, msg={response.msg}")
        for c in response.data.items or []:
            all_items.append({
                "chat_id": getattr(c, "chat_id", ""),
                "name": getattr(c, "name", ""),
                "description": getattr(c, "description", ""),
                "owner_id": getattr(c, "owner_id", ""),
                "chat_type": getattr(c, "chat_type", ""),
            })
        if not response.data.has_more:
            break
        page_token = response.data.page_token
    return all_items[:max_count]
