from __future__ import annotations

from collections import Counter
from typing import Any

from mcp.server.fastmcp import FastMCP

from ..config import DEFAULT_PROFILE
from ..errors import QingflowApiError, raise_tool_error
from ..json_types import JSONObject
from ..list_type_labels import get_app_publish_status_label
from .base import ToolBase


class AppTools(ToolBase):
    def register(self, mcp: FastMCP) -> None:
        @mcp.tool()
        def app_list(profile: str = DEFAULT_PROFILE, ship_auth: bool = False) -> JSONObject:
            return self.app_list(profile=profile, ship_auth=ship_auth)

        @mcp.tool()
        def app_search(profile: str = DEFAULT_PROFILE, keyword: str = "", page_num: int = 1, page_size: int = 50) -> JSONObject:
            return self.app_search(profile=profile, keyword=keyword, page_num=page_num, page_size=page_size)

        @mcp.tool()
        def app_get_base(profile: str = DEFAULT_PROFILE, app_key: str = "", include_raw: bool = False) -> JSONObject:
            return self.app_get_base(profile=profile, app_key=app_key, include_raw=include_raw)

        @mcp.tool(description=self._high_risk_tool_description(operation="update", target="app base settings"))
        def app_update_base(profile: str = DEFAULT_PROFILE, app_key: str = "", payload: JSONObject | None = None) -> JSONObject:
            return self.app_update_base(profile=profile, app_key=app_key, payload=payload or {})

        @mcp.tool()
        def app_get_form_schema(
            profile: str = DEFAULT_PROFILE,
            app_key: str = "",
            form_type: int = 1,
            being_draft: bool | None = None,
            being_apply: bool | None = None,
            audit_node_id: int | None = None,
            include_raw: bool = False,
        ) -> JSONObject:
            return self.app_get_form_schema(
                profile=profile,
                app_key=app_key,
                form_type=form_type,
                being_draft=being_draft,
                being_apply=being_apply,
                audit_node_id=audit_node_id,
                include_raw=include_raw,
            )

        @mcp.tool()
        def app_get_edit_version_no(profile: str = DEFAULT_PROFILE, app_key: str = "") -> JSONObject:
            return self.app_get_edit_version_no(profile=profile, app_key=app_key)

        @mcp.tool()
        def app_edit_finished(profile: str = DEFAULT_PROFILE, app_key: str = "", payload: JSONObject | None = None) -> JSONObject:
            return self.app_edit_finished(profile=profile, app_key=app_key, payload=payload or {})

        @mcp.tool()
        def app_create(profile: str = DEFAULT_PROFILE, payload: JSONObject | None = None) -> JSONObject:
            return self.app_create(profile=profile, payload=payload or {})

        @mcp.tool(description=self._high_risk_tool_description(operation="delete", target="app configuration"))
        def app_delete(profile: str = DEFAULT_PROFILE, app_key: str = "") -> JSONObject:
            return self.app_delete(profile=profile, app_key=app_key)

        @mcp.tool()
        def app_publish(
            profile: str = DEFAULT_PROFILE,
            app_key: str = "",
            payload: JSONObject | None = None,
        ) -> JSONObject:
            return self.app_publish(profile=profile, app_key=app_key, payload=payload or {})

    def app_list(self, *, profile: str, ship_auth: bool = False) -> JSONObject:
        """Get all apps with full hierarchy from tag/apps endpoint."""
        def runner(session_profile, context):
            result = self.backend.request("GET", context, "/tag/apps")
            return {
                "profile": profile,
                "ws_id": session_profile.selected_ws_id,
                "items": result,
            }

        return self._run(profile, runner)

    def app_search(self, *, profile: str, keyword: str = "", page_num: int = 1, page_size: int = 50) -> JSONObject:
        """Search apps by keyword in name/title using backend search API.
        Useful for finding BUG-related apps across all packages."""
        def runner(session_profile, context):
            # Use GET /app/item which supports queryKey search across all apps
            params: JSONObject = {"pageNum": page_num, "pageSize": page_size}
            if keyword:
                params["queryKey"] = keyword
            
            result = self.backend.request("GET", context, "/app/item", params=params)
            
            # Extract app list from the response
            apps = []
            if isinstance(result, dict):
                items = result.get("list", [])
                for item in items:
                    if isinstance(item, dict):
                        apps.append({
                            "app_key": item.get("appKey"),
                            "title": item.get("title") or item.get("formTitle"),
                            "form_id": item.get("formId"),
                            "tag_id": item.get("tagId"),
                            "group_id": item.get("groupId"),
                        })
            
            return {
                "profile": profile,
                "ws_id": session_profile.selected_ws_id,
                "keyword": keyword,
                "page_num": page_num,
                "page_size": page_size,
                "total": result.get("total") if isinstance(result, dict) else len(apps),
                "apps": apps,
            }

        return self._run(profile, runner)

    def app_get_base(self, *, profile: str, app_key: str, include_raw: bool = False) -> JSONObject:
        self._require_app_key(app_key)

        def runner(session_profile, context):
            result = self.backend.request("GET", context, f"/app/{app_key}/baseInfo")
            publish_status = result.get("appPublishStatus") if isinstance(result, dict) else None
            compact = self._compact_base_info(result if isinstance(result, dict) else {})
            response = {
                "profile": profile,
                "ws_id": session_profile.selected_ws_id,
                "app_key": app_key,
                "result": result if include_raw else compact,
                "app_publish_status": publish_status,
                "app_publish_status_label": get_app_publish_status_label(publish_status if isinstance(publish_status, int) else None),
                "compact": not include_raw,
            }
            if include_raw:
                response["summary"] = compact
            return response

        return self._run(profile, runner)

    def app_update_base(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}/baseInfo", json_body=body)
            return self._attach_human_review_notice(
                {"profile": profile, "ws_id": session_profile.selected_ws_id, "app_key": app_key, "result": result},
                operation="update",
                target="app base settings",
            )

        return self._run(profile, runner)

    def app_get_form_schema(
        self,
        *,
        profile: str,
        app_key: str,
        form_type: int,
        being_draft: bool | None,
        being_apply: bool | None,
        audit_node_id: int | None,
        include_raw: bool = False,
    ) -> JSONObject:
        self._require_app_key(app_key)

        def runner(session_profile, context):
            params: JSONObject = {"type": form_type}
            if being_draft is not None:
                params["beingDraft"] = being_draft
            if being_apply is not None:
                params["beingApply"] = being_apply
            if audit_node_id is not None:
                params["auditNodeId"] = audit_node_id
            result = self.backend.request("GET", context, f"/app/{app_key}/form", params=params)
            compact = self._compact_form_schema(result if isinstance(result, dict) else {})
            response = {
                "profile": profile,
                "ws_id": session_profile.selected_ws_id,
                "app_key": app_key,
                "result": result if include_raw else compact,
                "compact": not include_raw,
            }
            if include_raw:
                response["summary"] = compact
            return response

        return self._run(profile, runner)

    def app_get_edit_version_no(self, *, profile: str, app_key: str) -> JSONObject:
        self._require_app_key(app_key)

        def runner(session_profile, context):
            result = self.backend.request("GET", context, f"/app/{app_key}/editVersionNo")
            return {"profile": profile, "ws_id": session_profile.selected_ws_id, "app_key": app_key, "result": result}

        return self._run(profile, runner)

    def app_update_form_schema(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}/form", json_body=body)
            return self._attach_human_review_notice(
                {"profile": profile, "ws_id": session_profile.selected_ws_id, "app_key": app_key, "result": result},
                operation="update",
                target="app form schema",
            )

        return self._run(profile, runner)

    def app_edit_finished(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}/editFinished", json_body=body)
            return {"profile": profile, "ws_id": session_profile.selected_ws_id, "app_key": app_key, "result": result}

        return self._run(profile, runner)

    def app_create(self, *, profile: str, payload: JSONObject) -> JSONObject:
        body = self._require_dict(payload)

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

        return self._run(profile, runner)

    def app_delete(self, *, profile: str, app_key: str) -> JSONObject:
        self._require_app_key(app_key)

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

        return self._run(profile, runner)

    def app_publish(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}/publish", json_body=body)
            return {"profile": profile, "ws_id": session_profile.selected_ws_id, "app_key": app_key, "result": result}

        return self._run(profile, runner)

    def _require_app_key(self, app_key: str) -> None:
        if not app_key:
            raise_tool_error(QingflowApiError.config_error("app_key is required"))

    def _compact_base_info(self, result: dict[str, Any]) -> JSONObject:
        publish_status = result.get("appPublishStatus")
        auth = result.get("auth") if isinstance(result.get("auth"), dict) else {}
        contact_auth = auth.get("contactAuth") if isinstance(auth, dict) and isinstance(auth.get("contactAuth"), dict) else {}
        external_auth = auth.get("externalMemberAuth") if isinstance(auth, dict) and isinstance(auth.get("externalMemberAuth"), dict) else {}
        tag_items = result.get("tagItems") if isinstance(result.get("tagItems"), list) else []
        tag_ids = result.get("tagIds") if isinstance(result.get("tagIds"), list) else []
        return {
            "appKey": result.get("appKey"),
            "formId": result.get("formId"),
            "formTitle": result.get("formTitle"),
            "appIcon": result.get("appIcon"),
            "appPublishStatus": publish_status,
            "appPublishStatusLabel": get_app_publish_status_label(publish_status if isinstance(publish_status, int) else None),
            "appOpenStatus": result.get("appOpenStatus"),
            "applyOpenStatus": result.get("applyOpenStatus"),
            "dataManageStatus": result.get("dataManageStatus"),
            "editItemStatus": result.get("editItemStatus"),
            "deleteItemStatus": result.get("deleteItemStatus"),
            "copyAppStatus": result.get("copyAppStatus"),
            "flowStatus": result.get("flowStatus"),
            "createTime": result.get("createTime"),
            "creator": self._compact_user(result.get("creator")),
            "tagIds": tag_ids,
            "tagCount": len(tag_ids),
            "tagItemCount": len(tag_items),
            "tagItemsPreview": [self._compact_tag_item(item) for item in tag_items[:10] if isinstance(item, dict)],
            "authSummary": {
                "type": auth.get("type") if isinstance(auth, dict) else None,
                "contactAuthType": contact_auth.get("type") if isinstance(contact_auth, dict) else None,
                "contactDepartments": self._count_auth_members(contact_auth, "depart"),
                "contactMembers": self._count_auth_members(contact_auth, "member"),
                "contactRoles": self._count_auth_members(contact_auth, "role"),
                "contactIncludeSubDeparts": self._extract_include_sub_departs(contact_auth),
                "externalAuthType": external_auth.get("type") if isinstance(external_auth, dict) else None,
                "externalDepartments": self._count_auth_members(external_auth, "depart"),
                "externalMembers": self._count_auth_members(external_auth, "member"),
                "externalRoles": self._count_auth_members(external_auth, "role"),
            },
        }

    def _compact_form_schema(self, result: dict[str, Any]) -> JSONObject:
        base_questions_raw = result.get("baseQues") if isinstance(result.get("baseQues"), list) else []
        form_questions_raw = result.get("formQues") if isinstance(result.get("formQues"), list) else []
        base_questions = [self._compact_question(question) for question in base_questions_raw if isinstance(question, dict)]
        form_questions = [self._compact_question(question) for question in form_questions_raw if isinstance(question, dict)]
        all_questions = base_questions + form_questions
        type_counts = Counter(
            question["queType"]
            for question in all_questions
            if isinstance(question.get("queType"), int)
        )
        required_count = sum(1 for question in all_questions if question.get("required") is True)
        return {
            "appKey": result.get("appKey"),
            "formId": result.get("formId"),
            "formTitle": result.get("formTitle"),
            "editVersionNo": result.get("editVersionNo"),
            "serialNumType": result.get("serialNumType"),
            "hideCopyright": result.get("hideCopyright"),
            "questionCounts": {
                "baseQuestions": len(base_questions),
                "formQuestions": len(form_questions),
                "totalQuestions": len(all_questions),
                "requiredQuestions": required_count,
                "questionRelations": len(result.get("questionRelations") or []),
                "selectScopeRelations": len(result.get("selectScopeRelations") or []),
                "serialNumConfigs": len(result.get("serialNumConfig") or []),
            },
            "questionTypeCounts": [
                {"queType": que_type, "count": count}
                for que_type, count in sorted(type_counts.items())
            ],
            "baseQuestions": base_questions,
            "formQuestions": form_questions,
        }

    def _compact_user(self, user: Any) -> JSONObject | None:
        if not isinstance(user, dict):
            return None
        return {
            "uid": user.get("uid"),
            "nickName": user.get("nickName"),
            "email": user.get("email"),
            "remark": user.get("remark"),
        }

    def _compact_tag_item(self, item: dict[str, Any]) -> JSONObject:
        return {
            "itemType": item.get("itemType"),
            "title": item.get("title"),
            "appKey": item.get("appKey"),
            "formId": item.get("formId"),
            "dashKey": item.get("dashKey"),
            "pageKey": item.get("pageKey"),
        }

    def _compact_question(self, question: dict[str, Any]) -> JSONObject:
        options = question.get("options")
        inner_questions = question.get("innerQuestions")
        sub_questions = question.get("subQuestions")
        compact = {
            "queId": question.get("queId"),
            "queTitle": question.get("queTitle"),
            "queType": question.get("queType"),
            "required": question.get("required") is True,
            "ordinal": question.get("ordinal"),
            "inlineOrdinal": question.get("inlineOrdinal"),
            "queWidth": question.get("queWidth"),
            "sectionId": question.get("sectionId"),
            "supId": question.get("supId"),
            "relatedQueId": question.get("relatedQueId"),
            "pluginStatus": question.get("pluginStatus"),
            "optionCount": len(options) if isinstance(options, list) else 0,
            "innerQuestionCount": len(inner_questions) if isinstance(inner_questions, list) else 0,
            "subQuestionCount": len(sub_questions) if isinstance(sub_questions, list) else 0,
        }
        return {key: value for key, value in compact.items() if value is not None}

    def _count_auth_members(self, auth_payload: Any, member_key: str) -> int:
        if not isinstance(auth_payload, dict):
            return 0
        auth_members = auth_payload.get("authMembers")
        if not isinstance(auth_members, dict):
            return 0
        members = auth_members.get(member_key)
        return len(members) if isinstance(members, list) else 0

    def _extract_include_sub_departs(self, auth_payload: Any) -> bool | None:
        if not isinstance(auth_payload, dict):
            return None
        auth_members = auth_payload.get("authMembers")
        if isinstance(auth_members, dict):
            include_sub_departs = auth_members.get("includeSubDeparts")
            if isinstance(include_sub_departs, bool):
                return include_sub_departs
        include_sub_departs = auth_payload.get("includeSubDeparts")
        if isinstance(include_sub_departs, bool):
            return include_sub_departs
        return None
