from __future__ import annotations

from mcp.server.fastmcp import FastMCP

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


class ViewTools(ToolBase):
    def register(self, mcp: FastMCP) -> None:
        @mcp.tool()
        def view_list(profile: str = DEFAULT_PROFILE, app_key: str = "") -> JSONObject:
            return self.view_list(profile=profile, app_key=app_key)

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

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

        @mcp.tool()
        def view_get_base_info(profile: str = DEFAULT_PROFILE, viewgraph_key: str = "", passcode: str | None = None) -> JSONObject:
            return self.view_get_base_info(profile=profile, viewgraph_key=viewgraph_key, passcode=passcode)

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

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

        @mcp.tool()
        def view_board_get_lane_base_info(profile: str = DEFAULT_PROFILE, viewgraph_key: str = "", page_num: int = 1, page_size: int = 20) -> JSONObject:
            return self.view_board_get_lane_base_info(profile=profile, viewgraph_key=viewgraph_key, page_num=page_num, page_size=page_size)

        @mcp.tool()
        def view_get_future_nodes(profile: str = DEFAULT_PROFILE, viewgraph_key: str = "", apply_id: int = 0, app_key: str = "") -> JSONObject:
            return self.view_get_future_nodes(profile=profile, viewgraph_key=viewgraph_key, apply_id=apply_id, app_key=app_key)

        @mcp.tool()
        def view_get_workflow_status(profile: str = DEFAULT_PROFILE, viewgraph_key: str = "", row_record_id: int = 0) -> JSONObject:
            return self.view_get_workflow_status(profile=profile, viewgraph_key=viewgraph_key, row_record_id=row_record_id)

    def view_list(self, *, profile: str, app_key: str) -> JSONObject:
        self._require_app_key(app_key)
        return self._request(profile, "GET", f"/app/{app_key}/view/viewList", app_key=app_key)

    def view_list_flat(self, *, profile: str, app_key: str) -> JSONObject:
        self._require_app_key(app_key)
        return self._request(profile, "GET", f"/app/{app_key}/view/views", app_key=app_key)

    def view_reorder(self, *, profile: str, app_key: str, payload: list[JSONObject]) -> JSONObject:
        self._require_app_key(app_key)
        if not isinstance(payload, list) or not payload:
            raise_tool_error(QingflowApiError.config_error("payload must be a non-empty array"))
        return self._request(
            profile,
            "POST",
            f"/app/{app_key}/view/ordinal",
            app_key=app_key,
            json_body=self._normalize_reorder_payload(payload),
        )

    def view_set_column_width(self, *, profile: str, app_key: str, payload: JSONObject) -> JSONObject:
        self._require_app_key(app_key)
        body = self._require_dict(payload)
        return self._request(profile, "POST", f"/app/{app_key}/view/que/width", app_key=app_key, json_body=body)

    def view_create(self, *, profile: str, payload: JSONObject) -> JSONObject:
        body = self._require_dict(payload)
        app_key = str(body.get("appKey") or "")
        return self._request(profile, "POST", "/view/viewConfig", app_key=app_key, json_body=body)

    def view_get_config(self, *, profile: str, viewgraph_key: str) -> JSONObject:
        self._require_viewgraph_key(viewgraph_key)
        return self._request(profile, "GET", f"/view/{viewgraph_key}/viewConfig", viewgraph_key=viewgraph_key)

    def view_get_base_info(self, *, profile: str, viewgraph_key: str, passcode: str | None) -> JSONObject:
        self._require_viewgraph_key(viewgraph_key)
        params = {"pass": passcode} if passcode else None
        return self._request(profile, "GET", f"/view/{viewgraph_key}/viewConfig/baseInfo", viewgraph_key=viewgraph_key, params=params)

    def view_update(self, *, profile: str, viewgraph_key: str, payload: JSONObject) -> JSONObject:
        self._require_viewgraph_key(viewgraph_key)
        body = self._require_dict(payload)
        return self._request(
            profile,
            "POST",
            f"/view/{viewgraph_key}/viewConfig",
            viewgraph_key=viewgraph_key,
            json_body=body,
            risk_operation="update",
            risk_target="view configuration",
        )

    def view_delete(self, *, profile: str, viewgraph_key: str) -> JSONObject:
        self._require_viewgraph_key(viewgraph_key)
        return self._request(
            profile,
            "DELETE",
            f"/view/{viewgraph_key}",
            viewgraph_key=viewgraph_key,
            risk_operation="delete",
            risk_target="view configuration",
        )

    def view_copy(self, *, profile: str, viewgraph_key: str) -> JSONObject:
        self._require_viewgraph_key(viewgraph_key)
        return self._request(profile, "POST", f"/view/{viewgraph_key}/copy", viewgraph_key=viewgraph_key)

    def view_list_questions(self, *, profile: str, viewgraph_key: str) -> JSONObject:
        self._require_viewgraph_key(viewgraph_key)
        return self._request(profile, "GET", f"/view/{viewgraph_key}/question", viewgraph_key=viewgraph_key)

    def view_list_associations(self, *, profile: str, viewgraph_key: str) -> JSONObject:
        self._require_viewgraph_key(viewgraph_key)
        return self._request(profile, "GET", f"/view/{viewgraph_key}/association", viewgraph_key=viewgraph_key)

    def view_update_member_config(self, *, profile: str, viewgraph_key: str, payload: JSONObject) -> JSONObject:
        self._require_viewgraph_key(viewgraph_key)
        body = self._require_dict(payload)
        return self._request(
            profile,
            "POST",
            f"/view/{viewgraph_key}/member/config",
            viewgraph_key=viewgraph_key,
            json_body=body,
            risk_operation="update",
            risk_target="view member visibility",
        )

    def view_update_apply_config(self, *, profile: str, viewgraph_key: str, payload: JSONObject) -> JSONObject:
        self._require_viewgraph_key(viewgraph_key)
        body = self._require_dict(payload)
        return self._request(
            profile,
            "POST",
            f"/view/{viewgraph_key}/apply/config",
            viewgraph_key=viewgraph_key,
            json_body=body,
            risk_operation="update",
            risk_target="view apply visibility",
        )

    def view_board_set_lane_config(self, *, profile: str, viewgraph_key: str, payload: JSONObject) -> JSONObject:
        self._require_viewgraph_key(viewgraph_key)
        body = self._require_dict(payload)
        return self._request(
            profile,
            "POST",
            f"/view/{viewgraph_key}/lane/config",
            viewgraph_key=viewgraph_key,
            json_body=body,
            risk_operation="update",
            risk_target="board lane configuration",
        )

    def view_board_get_lane_base_info(self, *, profile: str, viewgraph_key: str, page_num: int, page_size: int) -> JSONObject:
        self._require_viewgraph_key(viewgraph_key)
        return self._request(
            profile,
            "GET",
            f"/view/{viewgraph_key}/lane/baseInfo",
            viewgraph_key=viewgraph_key,
            params={"pageNum": page_num, "pageSize": page_size},
        )

    def view_gantt_switch_auto_calibration(self, *, profile: str, viewgraph_key: str, payload: JSONObject) -> JSONObject:
        self._require_viewgraph_key(viewgraph_key)
        body = self._require_dict(payload)
        return self._request(
            profile,
            "PUT",
            f"/view/gantt/{viewgraph_key}/auto/calibration",
            viewgraph_key=viewgraph_key,
            json_body=body,
            risk_operation="update",
            risk_target="gantt auto calibration settings",
        )

    def view_gantt_batch_update_time(self, *, profile: str, viewgraph_key: str, payload: JSONObject) -> JSONObject:
        self._require_viewgraph_key(viewgraph_key)
        body = self._require_dict(payload)
        return self._request(
            profile,
            "PUT",
            f"/view/gantt/{viewgraph_key}/batch/update",
            viewgraph_key=viewgraph_key,
            json_body=body,
            risk_operation="update",
            risk_target="gantt task timing",
        )

    def view_get_future_nodes(self, *, profile: str, viewgraph_key: str, apply_id: int, app_key: str) -> JSONObject:
        self._require_viewgraph_key(viewgraph_key)
        self._require_app_key(app_key)
        self._require_positive("apply_id", apply_id)
        return self._request(
            profile,
            "GET",
            f"/view/{viewgraph_key}/auditNode/futureList/{apply_id}",
            viewgraph_key=viewgraph_key,
            app_key=app_key,
            apply_id=apply_id,
            params={"appKey": app_key},
        )

    def view_get_workflow_status(self, *, profile: str, viewgraph_key: str, row_record_id: int) -> JSONObject:
        self._require_viewgraph_key(viewgraph_key)
        self._require_positive("row_record_id", row_record_id)
        return self._request(profile, "GET", f"/view/{viewgraph_key}/workflow/status/{row_record_id}", viewgraph_key=viewgraph_key, row_record_id=row_record_id)

    def _request(
        self,
        profile: str,
        method: str,
        path: str,
        *,
        json_body: JSONValue = None,
        params: JSONObject | None = None,
        risk_operation: str | None = None,
        risk_target: str | None = None,
        **extra: JSONValue,
    ) -> JSONObject:
        def runner(session_profile, context):
            result = self.backend.request(method, context, path, json_body=json_body, params=params)
            response: JSONObject = {"profile": profile, "ws_id": session_profile.selected_ws_id, "result": result}
            response.update(extra)
            if risk_operation and risk_target:
                return self._attach_human_review_notice(response, operation=risk_operation, target=risk_target)
            return response

        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 _require_viewgraph_key(self, viewgraph_key: str) -> None:
        if not viewgraph_key:
            raise_tool_error(QingflowApiError.config_error("viewgraph_key is required"))

    def _require_positive(self, field_name: str, value: int) -> None:
        if value <= 0:
            raise_tool_error(QingflowApiError.config_error(f"{field_name} must be positive"))

    def _normalize_reorder_payload(self, payload: list[JSONObject]) -> list[JSONObject]:
        first_item = payload[0]
        if "ordinalType" in first_item and "viewKeyList" in first_item:
            return payload
        if "ordinalType" in first_item and "viewList" in first_item:
            normalized_groups: list[JSONObject] = []
            for group in payload:
                view_list = group.get("viewList")
                view_keys = [
                    str(view.get("viewKey"))
                    for view in view_list
                    if isinstance(view_list, list) and isinstance(view, dict) and view.get("viewKey")
                ]
                if group.get("ordinalType") and view_keys:
                    normalized_groups.append({"ordinalType": group["ordinalType"], "viewKeyList": view_keys})
            if normalized_groups:
                return normalized_groups
        sorted_items = sorted(
            payload,
            key=lambda item: int(item.get("ordinal", payload.index(item))) if isinstance(item.get("ordinal"), int) else payload.index(item),
        )
        view_keys = [
            str(item.get("viewgraphKey") or item.get("viewKey"))
            for item in sorted_items
            if item.get("viewgraphKey") or item.get("viewKey")
        ]
        if not view_keys:
            raise_tool_error(QingflowApiError.config_error("payload must include viewgraphKey/viewKey or ordinalType/viewKeyList"))
        return [{"ordinalType": "FIXED_VIEW_LIST", "viewKeyList": view_keys}]
