from __future__ import annotations

from copy import deepcopy

from ..config import DEFAULT_PROFILE
from ..errors import QingflowApiError, backend_code_int, is_auth_like_error, raise_tool_error
from ..json_types import JSONObject
from .base import ToolBase


class CustomButtonTools(ToolBase):
    """自定义按钮工具（中文名：按钮配置管理）。

    类型：应用配置工具。
    主要职责：
    1. 查询应用自定义按钮；
    2. 创建、更新、删除按钮配置；
    3. 支持草稿与发布态按钮配置维护。
    """

    def custom_button_list(
        self,
        *,
        profile: str,
        app_key: str,
        being_draft: bool = True,
        include_raw: bool = False,
    ) -> JSONObject:
        """执行工具方法逻辑。"""
        self._require_app_key(app_key)

        def runner(session_profile, context):
            effective_being_draft = being_draft
            fallback_error: QingflowApiError | None = None
            try:
                result = self.backend.request(
                    "GET",
                    context,
                    f"/app/{app_key}/customButton",
                    params={"beingDraft": being_draft},
                )
            except QingflowApiError as exc:
                if not being_draft or not _is_optional_draft_button_read_error(exc):
                    raise
                fallback_error = exc
                effective_being_draft = False
                result = self.backend.request(
                    "GET",
                    context,
                    f"/app/{app_key}/customButton",
                    params={"beingDraft": False},
                )
            items = []
            raw_items = result.get("result") if isinstance(result, dict) and isinstance(result.get("result"), list) else []
            for item in raw_items:
                if not isinstance(item, dict):
                    continue
                items.append(self._compact_button_base_info(item))
            response = {
                "profile": profile,
                "ws_id": session_profile.selected_ws_id,
                "app_key": app_key,
                "being_draft": effective_being_draft,
                "requested_being_draft": being_draft,
                "items": result if include_raw else items,
                "count": len(items),
                "compact": not include_raw,
            }
            if fallback_error is not None:
                response["warnings"] = [
                    {
                        "code": "CUSTOM_BUTTON_DRAFT_LIST_UNAVAILABLE",
                        "message": "draft custom button list is unavailable; returned published custom buttons instead",
                        "backend_code": fallback_error.backend_code,
                        "http_status": fallback_error.http_status,
                        "request_id": fallback_error.request_id,
                    }
                ]
                response["verification"] = {"draft_readable": False, "published_fallback_used": True}
            if include_raw:
                response["summary"] = items
            return response

        return self._run(profile, runner, tool_name='自定义按钮列表')

    def custom_button_get(
        self,
        *,
        profile: str,
        app_key: str,
        button_id: int,
        being_draft: bool = True,
        include_raw: bool = False,
    ) -> JSONObject:
        """执行工具方法逻辑。"""
        self._require_app_key(app_key)
        self._require_button_id(button_id)

        def runner(session_profile, context):
            params = {"beingDraft": being_draft}
            effective_being_draft = being_draft
            fallback_error: QingflowApiError | None = None
            try:
                result = self.backend.request("GET", context, f"/app/{app_key}/customButton/{button_id}", params=params)
            except QingflowApiError as exc:
                if not being_draft or not _is_optional_draft_button_read_error(exc):
                    raise
                fallback_error = exc
                effective_being_draft = False
                result = self.backend.request("GET", context, f"/app/{app_key}/customButton/{button_id}", params={"beingDraft": False})
            response = {
                "profile": profile,
                "ws_id": session_profile.selected_ws_id,
                "app_key": app_key,
                "button_id": button_id,
                "being_draft": effective_being_draft,
                "requested_being_draft": being_draft,
                "result": result if include_raw else self._compact_button_detail(result if isinstance(result, dict) else {}),
                "compact": not include_raw,
            }
            if fallback_error is not None:
                response["warnings"] = [
                    {
                        "code": "CUSTOM_BUTTON_DRAFT_DETAIL_UNAVAILABLE",
                        "message": "draft custom button detail is unavailable; returned published custom button detail instead",
                        "backend_code": fallback_error.backend_code,
                        "http_status": fallback_error.http_status,
                        "request_id": fallback_error.request_id,
                    }
                ]
                response["verification"] = {"draft_readable": False, "published_fallback_used": True}
            if include_raw:
                response["summary"] = self._compact_button_detail(result if isinstance(result, dict) else {})
            return response

        return self._run(profile, runner, tool_name='自定义按钮详情')

    def custom_button_create(self, *, profile: str, app_key: str, payload: JSONObject) -> JSONObject:
        """执行工具方法逻辑。"""
        self._require_app_key(app_key)
        body = self._require_dict(payload)

        def runner(session_profile, context):
            result = self.backend.request("POST", context, f"/app/{app_key}/customButton", json_body=deepcopy(body))
            return {"profile": profile, "ws_id": session_profile.selected_ws_id, "app_key": app_key, "result": result}

        return self._run(profile, runner, tool_name='创建自定义按钮')

    def custom_button_update(self, *, profile: str, app_key: str, button_id: int, payload: JSONObject) -> JSONObject:
        """执行工具方法逻辑。"""
        self._require_app_key(app_key)
        self._require_button_id(button_id)
        body = self._require_dict(payload)

        def runner(session_profile, context):
            result = self.backend.request(
                "POST",
                context,
                f"/app/{app_key}/customButton/{button_id}",
                json_body=deepcopy(body),
            )
            return self._attach_human_review_notice(
                {
                    "profile": profile,
                    "ws_id": session_profile.selected_ws_id,
                    "app_key": app_key,
                    "button_id": button_id,
                    "result": result,
                },
                operation="update",
                target="custom button configuration",
            )

        return self._run(profile, runner, tool_name='更新自定义按钮')

    def custom_button_delete(self, *, profile: str, app_key: str, button_id: int) -> JSONObject:
        """执行工具方法逻辑。"""
        self._require_app_key(app_key)
        self._require_button_id(button_id)

        def runner(session_profile, context):
            result = self.backend.request("DELETE", context, f"/app/{app_key}/customButton/{button_id}")
            return self._attach_human_review_notice(
                {
                    "profile": profile,
                    "ws_id": session_profile.selected_ws_id,
                    "app_key": app_key,
                    "button_id": button_id,
                    "result": result,
                },
                operation="delete",
                target="custom button configuration",
            )

        return self._run(profile, runner, tool_name='删除自定义按钮')

    def _require_app_key(self, app_key: str) -> None:
        """执行内部辅助逻辑。"""
        if not str(app_key or "").strip():
            raise_tool_error(QingflowApiError.config_error("app_key is required"))

    def _require_button_id(self, button_id: int) -> None:
        """执行内部辅助逻辑。"""
        if not isinstance(button_id, int) or isinstance(button_id, bool) or button_id <= 0:
            raise_tool_error(QingflowApiError.config_error("button_id must be a positive integer"))

    def _compact_button_base_info(self, item: dict[str, object]) -> JSONObject:
        """执行内部辅助逻辑。"""
        creator = item.get("creatorUserInfo") if isinstance(item.get("creatorUserInfo"), dict) else {}
        return {
            "button_id": item.get("buttonId"),
            "button_text": item.get("buttonText"),
            "button_icon": item.get("buttonIcon"),
            "background_color": item.get("backgroundColor"),
            "text_color": item.get("textColor"),
            "creator_user_info": {
                "uid": creator.get("uid"),
                "name": creator.get("name"),
                "email": creator.get("email"),
            }
            if creator
            else None,
            "used_in_chart_count": item.get("userInChartCount"),
            "being_effective_external_qrobot": item.get("beingEffectiveExternalQRobot"),
        }

    def _compact_button_detail(self, item: dict[str, object]) -> JSONObject:
        """执行内部辅助逻辑。"""
        return {
            "button_id": item.get("buttonId"),
            "button_text": item.get("buttonText"),
            "button_icon": item.get("buttonIcon"),
            "background_color": item.get("backgroundColor"),
            "text_color": item.get("textColor"),
            "trigger_action": item.get("triggerAction"),
            "trigger_link_url": item.get("triggerLinkUrl"),
            "trigger_add_data_config": deepcopy(item.get("triggerAddDataConfig")) if isinstance(item.get("triggerAddDataConfig"), dict) else None,
            "external_qrobot_config": deepcopy(item.get("customButtonExternalQRobotRelationVO"))
            if isinstance(item.get("customButtonExternalQRobotRelationVO"), dict)
            else None,
            "trigger_wings_config": deepcopy(item.get("triggerWingsConfig")) if isinstance(item.get("triggerWingsConfig"), dict) else None,
        }


def _is_optional_draft_button_read_error(error: QingflowApiError) -> bool:
    if is_auth_like_error(error):
        return False
    backend_code = backend_code_int(error)
    return backend_code in {40002, 40027, 404} or error.http_status == 404
