"""
MCP Bridge - AI 與外部工具橋樑
Phase 3: 企業功能 - ADR-001 MCP 協議採用

核心功能:
1. list_tools(server_name) - 動態獲取 MCP Server 工具清單
2. call_tool(server_name, tool_name, parameters) - 執行工具

資安機制:
- Rehydration: 執行前將 [IP_1] 還原為真實值
- 符合 leWOOOgo ActionExecutor 介面

MCP Protocol Spec: https://modelcontextprotocol.io/
"""

import logging
import re
import uuid
from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum
from typing import Any

import httpx

from src.utils.timezone import now_taipei

logger = logging.getLogger(__name__)


# ==================== Types ====================


class MCPTransport(str, Enum):
    """MCP 傳輸方式"""
    STDIO = "stdio"      # 標準輸入輸出 (本地程式)
    HTTP = "http"        # HTTP/SSE (遠端服務)
    WEBSOCKET = "ws"     # WebSocket (即時雙向)


@dataclass
class MCPTool:
    """MCP 工具定義"""
    name: str
    description: str
    input_schema: dict[str, Any]
    server_name: str


@dataclass
class MCPToolResult:
    """工具執行結果 (符合 ActionResult 介面)"""
    success: bool
    execution_id: str
    output: Any | None = None
    error: str | None = None
    duration: float = 0.0
    timestamp: datetime = field(default_factory=now_taipei)

    def to_dict(self) -> dict:
        return {
            "success": self.success,
            "executionId": self.execution_id,
            "output": self.output,
            "error": self.error,
            "duration": self.duration,
            "timestamp": self.timestamp.isoformat(),
        }


@dataclass
class MCPServer:
    """MCP Server 配置"""
    name: str
    transport: MCPTransport
    endpoint: str  # 執行檔路徑 (stdio) 或 URL (http/ws)
    args: list[str] = field(default_factory=list)
    env: dict[str, str] = field(default_factory=dict)
    enabled: bool = True


# ==================== Rehydration Engine ====================


class RehydrationEngine:
    """
    資安標籤還原器

    將 Privacy Shield 產生的 [IP_1], [EMAIL_1], [SECRET_1] 等標籤
    還原為真實值，以便 MCP Tool 執行
    """

    # 標籤格式: [TYPE_N]
    LABEL_PATTERN = re.compile(r'\[(IP|EMAIL|SECRET|CC|PHONE|ID)_(\d+)\]')

    def unredact(
        self,
        data: Any,
        mapping: dict[str, str],
    ) -> Any:
        """
        還原脫敏資料

        Args:
            data: 可能包含脫敏標籤的資料 (str, dict, list)
            mapping: 原始值 → 標籤 的映射表 (來自 Privacy Shield)

        Returns:
            還原後的資料
        """
        # 反轉映射: 標籤 → 原始值
        reverse_mapping = {v: k for k, v in mapping.items()}
        return self._recursive_unredact(data, reverse_mapping)

    def _recursive_unredact(
        self,
        data: Any,
        reverse_mapping: dict[str, str],
    ) -> Any:
        """遞迴還原各種資料結構"""
        if isinstance(data, str):
            return self._unredact_string(data, reverse_mapping)
        elif isinstance(data, dict):
            return {
                k: self._recursive_unredact(v, reverse_mapping)
                for k, v in data.items()
            }
        elif isinstance(data, list):
            return [
                self._recursive_unredact(item, reverse_mapping)
                for item in data
            ]
        else:
            return data

    def _unredact_string(
        self,
        text: str,
        reverse_mapping: dict[str, str],
    ) -> str:
        """
        還原字串中的標籤

        ⚠️ 重要: 按標籤長度從長到短排序替換
        避免 [IP_1] 被先替換而污染 [IP_10] → 結果變成 "192.168.1.1000"
        """
        result = text
        # 按標籤長度降序排序，確保 [IP_10] 先於 [IP_1] 處理
        sorted_labels = sorted(
            reverse_mapping.items(),
            key=lambda x: len(x[0]),
            reverse=True,
        )
        for label, original in sorted_labels:
            # 使用精準邊界匹配，避免部分替換
            result = result.replace(label, original)
        return result

    def validate_no_labels(self, data: Any) -> tuple[bool, list[str]]:
        """
        驗證資料中是否還有未還原的標籤

        Returns:
            (is_clean, remaining_labels)
        """
        remaining = []
        self._find_labels(data, remaining)
        return len(remaining) == 0, remaining

    def _find_labels(self, data: Any, found: list[str]) -> None:
        """遞迴搜尋標籤"""
        if isinstance(data, str):
            matches = self.LABEL_PATTERN.findall(data)
            for match in matches:
                label = f"[{match[0]}_{match[1]}]"
                if label not in found:
                    found.append(label)
        elif isinstance(data, dict):
            for v in data.values():
                self._find_labels(v, found)
        elif isinstance(data, list):
            for item in data:
                self._find_labels(item, found)


# ==================== MCP Bridge ====================


class MCPBridge:
    """
    MCP 協議橋樑

    連接 AI 與外部 MCP Server，實現動態工具調用
    符合 leWOOOgo ActionExecutor 介面設計
    """

    def __init__(self):
        self.rehydrator = RehydrationEngine()
        self._servers: dict[str, MCPServer] = {}
        self._tool_cache: dict[str, list[MCPTool]] = {}
        self._http_client = httpx.AsyncClient(timeout=30.0)

        # 註冊 MCP Servers (Phase 13.2: 整合真實服務)
        self._register_servers()

    def _register_servers(self) -> None:
        """註冊 MCP Servers"""
        self._servers["kubernetes"] = MCPServer(
            name="kubernetes",
            transport=MCPTransport.HTTP,
            endpoint="http://localhost:8081/mcp",
        )
        self._servers["signoz"] = MCPServer(
            name="signoz",
            transport=MCPTransport.HTTP,
            endpoint="http://192.168.0.188:3301",  # SignOz Query Service
        )
        # Phase 13.2 #82: Filesystem Provider (安全受限)
        self._servers["filesystem"] = MCPServer(
            name="filesystem",
            transport=MCPTransport.HTTP,  # 改用 HTTP，由 Provider 處理
            endpoint="internal://filesystem-provider",
        )
        self._servers["database"] = MCPServer(
            name="database",
            transport=MCPTransport.HTTP,
            endpoint="http://localhost:8082/mcp",
        )

    def register_server(self, server: MCPServer) -> None:
        """註冊 MCP Server"""
        self._servers[server.name] = server
        logger.info(f"MCP Server registered: {server.name} ({server.transport.value})")

    async def list_tools(self, server_name: str) -> list[MCPTool]:
        """
        動態獲取 MCP Server 工具清單

        Args:
            server_name: MCP Server 名稱

        Returns:
            可用工具列表
        """
        if server_name not in self._servers:
            raise ValueError(f"Unknown MCP Server: {server_name}")

        # 快取檢查
        if server_name in self._tool_cache:
            return self._tool_cache[server_name]

        server = self._servers[server_name]
        tools = await self._fetch_tools(server)
        self._tool_cache[server_name] = tools
        return tools

    async def _fetch_tools(self, server: MCPServer) -> list[MCPTool]:
        """從 MCP Server 獲取工具清單"""
        if server.transport == MCPTransport.HTTP:
            return await self._fetch_tools_http(server)
        elif server.transport == MCPTransport.STDIO:
            return await self._fetch_tools_stdio(server)
        else:
            raise NotImplementedError(f"Transport not supported: {server.transport}")

    async def _fetch_tools_http(self, server: MCPServer) -> list[MCPTool]:
        """HTTP 方式獲取工具 (Mock 實作)"""
        # Phase 3: Mock 回傳，實際連接待 MCP Server 部署
        mock_tools = {
            "kubernetes": [
                MCPTool(
                    name="kubectl_get",
                    description="Get Kubernetes resources",
                    input_schema={
                        "type": "object",
                        "properties": {
                            "resource": {"type": "string"},
                            "namespace": {"type": "string"},
                            "name": {"type": "string"},
                        },
                        "required": ["resource"],
                    },
                    server_name=server.name,
                ),
                MCPTool(
                    name="kubectl_delete",
                    description="Delete Kubernetes resources",
                    input_schema={
                        "type": "object",
                        "properties": {
                            "resource": {"type": "string"},
                            "namespace": {"type": "string"},
                            "name": {"type": "string"},
                        },
                        "required": ["resource", "name"],
                    },
                    server_name=server.name,
                ),
                MCPTool(
                    name="kubectl_scale",
                    description="Scale Kubernetes deployment",
                    input_schema={
                        "type": "object",
                        "properties": {
                            "deployment": {"type": "string"},
                            "namespace": {"type": "string"},
                            "replicas": {"type": "integer"},
                        },
                        "required": ["deployment", "replicas"],
                    },
                    server_name=server.name,
                ),
                MCPTool(
                    name="kubectl_restart",
                    description="Restart Kubernetes deployment (rollout restart)",
                    input_schema={
                        "type": "object",
                        "properties": {
                            "deployment": {"type": "string"},
                            "namespace": {"type": "string"},
                        },
                        "required": ["deployment"],
                    },
                    server_name=server.name,
                ),
            ],
            "signoz": [
                MCPTool(
                    name="gold_metrics",
                    description="Get Gold Metrics (RPS, Error Rate, P99 Latency) for a service",
                    input_schema={
                        "type": "object",
                        "properties": {
                            "service_name": {"type": "string", "description": "Service name (e.g., api-gateway)"},
                            "namespace": {"type": "string", "description": "K8s namespace (default: awoooi-prod)"},
                            "time_window_minutes": {"type": "integer", "description": "Time window in minutes (default: 10)"},
                        },
                        "required": ["service_name"],
                    },
                    server_name=server.name,
                ),
                MCPTool(
                    name="trace_url",
                    description="Generate SignOz trace URL for debugging a service",
                    input_schema={
                        "type": "object",
                        "properties": {
                            "service_name": {"type": "string", "description": "Service name"},
                            "window_minutes": {"type": "integer", "description": "Time window ± minutes (default: 5)"},
                        },
                        "required": ["service_name"],
                    },
                    server_name=server.name,
                ),
                MCPTool(
                    name="system_metrics",
                    description="Get system metrics (CPU, Disk I/O) from SignOz",
                    input_schema={
                        "type": "object",
                        "properties": {
                            "host": {"type": "string", "description": "Host IP (default: 192.168.0.188)"},
                            "time_window_minutes": {"type": "integer", "description": "Time window in minutes (default: 5)"},
                        },
                    },
                    server_name=server.name,
                ),
            ],
            "database": [
                MCPTool(
                    name="list_approvals",
                    description="List approval requests with optional status filter",
                    input_schema={
                        "type": "object",
                        "properties": {
                            "status": {"type": "string", "description": "Filter by status: pending, approved, rejected, expired"},
                            "limit": {"type": "integer", "description": "Max results (default: 20)"},
                        },
                    },
                    server_name=server.name,
                ),
                MCPTool(
                    name="get_approval",
                    description="Get a single approval by ID",
                    input_schema={
                        "type": "object",
                        "properties": {
                            "approval_id": {"type": "string", "description": "Approval UUID"},
                        },
                        "required": ["approval_id"],
                    },
                    server_name=server.name,
                ),
                MCPTool(
                    name="list_incidents",
                    description="List incidents with optional status filter",
                    input_schema={
                        "type": "object",
                        "properties": {
                            "status": {"type": "string", "description": "Filter by status: active, resolved, escalated"},
                            "limit": {"type": "integer", "description": "Max results (default: 20)"},
                        },
                    },
                    server_name=server.name,
                ),
                MCPTool(
                    name="list_timeline",
                    description="List recent timeline events",
                    input_schema={
                        "type": "object",
                        "properties": {
                            "limit": {"type": "integer", "description": "Max results (default: 50)"},
                        },
                    },
                    server_name=server.name,
                ),
            ],
        }
        return mock_tools.get(server.name, [])

    async def _fetch_tools_stdio(self, server: MCPServer) -> list[MCPTool]:
        """STDIO 方式獲取工具 (Mock 實作)"""
        # Phase 3: Mock 回傳
        return [
            MCPTool(
                name="read_file",
                description="Read file contents",
                input_schema={
                    "type": "object",
                    "properties": {"path": {"type": "string"}},
                    "required": ["path"],
                },
                server_name=server.name,
            ),
            MCPTool(
                name="write_file",
                description="Write file contents",
                input_schema={
                    "type": "object",
                    "properties": {
                        "path": {"type": "string"},
                        "content": {"type": "string"},
                    },
                    "required": ["path", "content"],
                },
                server_name=server.name,
            ),
        ]

    # ╔════════════════════════════════════════════════════════════════╗
    # ║  ⚠️  SECURITY CRITICAL - DO NOT LOG REHYDRATED PARAMETERS  ⚠️  ║
    # ║                                                                ║
    # ║  After rehydration, `parameters` contains REAL sensitive      ║
    # ║  data (IPs, emails, secrets). Logging them defeats the        ║
    # ║  entire purpose of Privacy Shield.                            ║
    # ║                                                                ║
    # ║  ALLOWED: logger.info(f"Calling {tool_name}")                 ║
    # ║  FORBIDDEN: logger.info(f"Params: {parameters}")              ║
    # ╚════════════════════════════════════════════════════════════════╝

    async def call_tool(
        self,
        server_name: str,
        tool_name: str,
        parameters: dict[str, Any],
        redaction_mapping: dict[str, str] | None = None,
    ) -> MCPToolResult:
        """
        執行 MCP 工具

        ⚠️ 資安關鍵路徑：
        1. Rehydration - 還原脫敏標籤為真實值
        2. 驗證 - 確保無殘留標籤
        3. 執行 - 調用 MCP Server
        4. 結果 - 返回 ActionResult 格式

        ⛔ 禁止 logging 任何已 rehydrate 的 parameters！

        Args:
            server_name: MCP Server 名稱
            tool_name: 工具名稱
            parameters: 工具參數 (可能包含脫敏標籤)
            redaction_mapping: Privacy Shield 映射表 (原始值 → 標籤)

        Returns:
            MCPToolResult (符合 ActionResult 介面)
        """
        execution_id = str(uuid.uuid4())
        start_time = now_taipei()

        try:
            # ========================================
            # 1. Rehydration: 還原脫敏標籤
            # ========================================
            if redaction_mapping:
                logger.info(f"[{execution_id}] Rehydrating {len(redaction_mapping)} labels")
                parameters = self.rehydrator.unredact(parameters, redaction_mapping)

            # ========================================
            # 2. 驗證: 確保無殘留標籤
            # ========================================
            is_clean, remaining = self.rehydrator.validate_no_labels(parameters)
            if not is_clean:
                logger.error(f"[{execution_id}] Unrehydrated labels found: {remaining}")
                return MCPToolResult(
                    success=False,
                    execution_id=execution_id,
                    error=f"Security violation: Unrehydrated labels found: {remaining}",
                    duration=self._calc_duration(start_time),
                )

            # ========================================
            # 3. 執行: 調用 MCP Server
            # ========================================
            logger.info(f"[{execution_id}] Calling {server_name}.{tool_name}")

            if server_name not in self._servers:
                raise ValueError(f"Unknown MCP Server: {server_name}")

            server = self._servers[server_name]
            result = await self._execute_tool(server, tool_name, parameters)

            # ========================================
            # 4. 結果: 返回 ActionResult 格式
            # ========================================
            return MCPToolResult(
                success=True,
                execution_id=execution_id,
                output=result,
                duration=self._calc_duration(start_time),
            )

        except Exception as e:
            logger.error(f"[{execution_id}] Tool execution failed: {e}")
            return MCPToolResult(
                success=False,
                execution_id=execution_id,
                error=str(e),
                duration=self._calc_duration(start_time),
            )

    async def _execute_tool(
        self,
        server: MCPServer,
        tool_name: str,
        parameters: dict[str, Any],
    ) -> Any:
        """執行 MCP 工具 (實際調用)"""
        if server.transport == MCPTransport.HTTP:
            return await self._execute_http(server, tool_name, parameters)
        elif server.transport == MCPTransport.STDIO:
            return await self._execute_stdio(server, tool_name, parameters)
        else:
            raise NotImplementedError(f"Transport not supported: {server.transport}")

    async def _execute_http(
        self,
        server: MCPServer,
        tool_name: str,
        parameters: dict[str, Any],
    ) -> Any:
        """
        HTTP 方式執行工具

        ADR-015 重構: 透過 ProviderRegistry 委派執行
        不再直接 import services，符合 leWOOOgo 積木化原則
        """
        # =============================================
        # ADR-015: 使用 Provider Registry (DI 模式)
        # =============================================
        from src.plugins.mcp.registry import get_provider

        provider = get_provider(server.name)
        if provider:
            result = await provider.execute(tool_name, parameters)
            if result.success:
                return result.output
            else:
                return {"error": result.error}

        # =============================================
        # Fallback: 舊邏輯 (逐步遷移後刪除)
        # =============================================
        # Kubernetes: 使用真實 ActionExecutor
        # =============================================
        if server.name == "kubernetes":
            from src.services.executor import get_executor

            executor = get_executor()

            if tool_name == "kubectl_get":
                # 使用 kubectl 指令查詢
                namespace = parameters.get("namespace", "awoooi-prod")
                resource = parameters.get("resource", "pods")
                name = parameters.get("name", "")
                cmd = f"kubectl get {resource} {name} -n {namespace} -o json".strip()
                result = await executor.execute_kubectl_command(cmd)
                if result.success and result.k8s_response:
                    return result.k8s_response.get("stdout", "")
                return {"error": result.error}

            elif tool_name == "kubectl_delete":
                namespace = parameters.get("namespace", "awoooi-prod")
                resource = parameters.get("resource", "pod")
                name = parameters.get("name", "")
                if not name:
                    return {"error": "Missing 'name' parameter"}

                # Dry-run 驗證
                if resource == "pod":
                    dry_run = await executor.validate_pod_exists(name, namespace)
                else:
                    dry_run = await executor.validate_deployment_exists(name, namespace)

                if not dry_run.passed:
                    return {"error": dry_run.message, "dry_run": False}

                # 執行刪除
                if resource == "pod":
                    result = await executor.delete_pod(name, namespace)
                else:
                    # deployment 不支援直接刪除，改用 restart
                    return {"error": "Direct deployment deletion not supported, use restart"}

                return {
                    "success": result.success,
                    "message": result.message,
                    "duration_ms": result.duration_ms,
                }

            elif tool_name == "kubectl_scale":
                namespace = parameters.get("namespace", "awoooi-prod")
                deployment = parameters.get("deployment", "")
                replicas = parameters.get("replicas", 1)
                if not deployment:
                    return {"error": "Missing 'deployment' parameter"}

                cmd = f"kubectl scale deployment/{deployment} --replicas={replicas} -n {namespace}"
                result = await executor.execute_kubectl_command(cmd)
                return {
                    "success": result.success,
                    "scaled": result.success,
                    "replicas": replicas,
                    "message": result.message,
                }

            elif tool_name == "kubectl_restart":
                namespace = parameters.get("namespace", "awoooi-prod")
                deployment = parameters.get("deployment", "")
                if not deployment:
                    return {"error": "Missing 'deployment' parameter"}

                dry_run = await executor.validate_deployment_exists(deployment, namespace)
                if not dry_run.passed:
                    return {"error": dry_run.message, "dry_run": False}

                result = await executor.restart_deployment(deployment, namespace)
                return {
                    "success": result.success,
                    "restarted": result.success,
                    "message": result.message,
                    "duration_ms": result.duration_ms,
                }

            else:
                return {"error": f"Unknown kubernetes tool: {tool_name}"}

        # =============================================
        # SignOz: 監控指標查詢 (Phase 13.2 #79)
        # =============================================
        elif server.name == "signoz":
            from src.services.signoz_client import get_signoz_client

            signoz = get_signoz_client()

            if tool_name == "gold_metrics":
                # 取得 Gold Metrics (RPS, Error Rate, P99)
                service_name = parameters.get("service_name", "")
                if not service_name:
                    return {"error": "Missing 'service_name' parameter"}

                namespace = parameters.get("namespace", "awoooi-prod")
                time_window = parameters.get("time_window_minutes", 10)

                metrics = await signoz.get_gold_metrics(
                    service_name=service_name,
                    namespace=namespace,
                    time_window_minutes=time_window,
                )

                return {
                    "service_name": metrics.service_name,
                    "namespace": metrics.namespace,
                    "rps": round(metrics.rps, 2),
                    "rps_trend": metrics.rps_trend,
                    "error_rate": round(metrics.error_rate, 2),
                    "error_count": metrics.error_count,
                    "total_requests": metrics.total_requests,
                    "p50_latency_ms": round(metrics.p50_latency_ms, 1),
                    "p95_latency_ms": round(metrics.p95_latency_ms, 1),
                    "p99_latency_ms": round(metrics.p99_latency_ms, 1),
                    "latency_trend": metrics.latency_trend,
                    "summary": metrics.to_summary(),
                }

            elif tool_name == "trace_url":
                # 生成 SignOz Trace URL
                service_name = parameters.get("service_name", "")
                if not service_name:
                    return {"error": "Missing 'service_name' parameter"}

                window_minutes = parameters.get("window_minutes", 5)
                url = signoz.generate_trace_url(
                    service_name=service_name,
                    window_minutes=window_minutes,
                )

                return {
                    "service_name": service_name,
                    "trace_url": url,
                    "window_minutes": window_minutes,
                }

            elif tool_name == "system_metrics":
                # 取得系統指標 (CPU/Disk)
                host = parameters.get("host", "192.168.0.188")
                time_window = parameters.get("time_window_minutes", 5)

                metrics = await signoz.get_system_metrics(
                    _host=host,
                    time_window_minutes=time_window,
                )

                return {
                    "host": host,
                    "cpu": metrics.get("cpu", {}),
                    "disk": metrics.get("disk", {}),
                    "time_range": metrics.get("time_range", {}),
                }

            else:
                return {"error": f"Unknown signoz tool: {tool_name}"}

        # =============================================
        # Database: 查詢 incident/approval 歷史 (Phase 13.2 #81)
        # =============================================
        elif server.name == "database":
            from uuid import UUID

            from src.models.approval import ApprovalStatus
            from src.services.approval_db import (
                get_approval_service,
                get_timeline_service,
            )
            from src.services.incident_service import get_incident_service

            if tool_name == "list_approvals":
                # 列出 Approval 請求
                approval_svc = get_approval_service()
                status_str = parameters.get("status")
                limit = parameters.get("limit", 20)

                status_filter = None
                if status_str:
                    try:
                        status_filter = ApprovalStatus(status_str.lower())
                    except ValueError:
                        return {"error": f"Invalid status: {status_str}. Valid: pending, approved, rejected, expired"}

                approvals = await approval_svc.get_all_approvals(
                    status=status_filter,
                    limit=limit,
                )

                return {
                    "count": len(approvals),
                    "approvals": [
                        {
                            "id": str(a.id),
                            "action": a.action[:80] if a.action else "",
                            "status": a.status.value if hasattr(a.status, 'value') else str(a.status),
                            "risk_level": a.risk_level.value if hasattr(a.risk_level, 'value') else str(a.risk_level),
                            "signatures": f"{a.current_signatures}/{a.required_signatures}",
                            "created_at": a.created_at.isoformat() if a.created_at else None,
                        }
                        for a in approvals
                    ],
                }

            elif tool_name == "get_approval":
                # 取得單一 Approval
                approval_id = parameters.get("approval_id")
                if not approval_id:
                    return {"error": "Missing 'approval_id' parameter"}

                approval_svc = get_approval_service()
                try:
                    approval = await approval_svc.get_approval_by_id(UUID(approval_id))
                except ValueError:
                    return {"error": f"Invalid UUID format: {approval_id}"}

                if not approval:
                    return {"error": f"Approval not found: {approval_id}"}

                return {
                    "id": str(approval.id),
                    "action": approval.action,
                    "description": approval.description,
                    "status": approval.status.value if hasattr(approval.status, 'value') else str(approval.status),
                    "risk_level": approval.risk_level.value if hasattr(approval.risk_level, 'value') else str(approval.risk_level),
                    "required_signatures": approval.required_signatures,
                    "current_signatures": approval.current_signatures,
                    "signatures": [
                        {"signer": s.signer_name, "timestamp": s.timestamp.isoformat()}
                        for s in (approval.signatures or [])
                    ],
                    "created_at": approval.created_at.isoformat() if approval.created_at else None,
                    "resolved_at": approval.resolved_at.isoformat() if approval.resolved_at else None,
                }

            elif tool_name == "list_incidents":
                # 列出 Incidents
                incident_svc = get_incident_service()
                status_filter = parameters.get("status")
                limit = parameters.get("limit", 20)

                incidents = await incident_svc.get_active_incidents()

                # 狀態過濾
                if status_filter:
                    incidents = [i for i in incidents if i.status.value == status_filter.lower()]

                incidents = incidents[:limit]

                return {
                    "count": len(incidents),
                    "incidents": [
                        {
                            "id": str(i.id),
                            "title": i.title[:80] if i.title else "",
                            "severity": i.severity.value if hasattr(i.severity, 'value') else str(i.severity),
                            "status": i.status.value if hasattr(i.status, 'value') else str(i.status),
                            "source": i.source,
                            "created_at": i.created_at.isoformat() if i.created_at else None,
                        }
                        for i in incidents
                    ],
                }

            elif tool_name == "list_timeline":
                # 列出 Timeline 事件
                timeline_svc = get_timeline_service()
                limit = parameters.get("limit", 50)

                events = await timeline_svc.get_events(limit=limit)

                return {
                    "count": len(events),
                    "events": events,
                }

            else:
                return {"error": f"Unknown database tool: {tool_name}"}

        # =============================================
        # Mock fallback
        # =============================================
        logger.info(f"[MOCK] HTTP call to {server.endpoint}: {tool_name}")
        return {"status": "ok", "mock": True}

    async def _execute_stdio(
        self,
        server: MCPServer,
        tool_name: str,
        parameters: dict[str, Any],
    ) -> Any:
        """STDIO 方式執行工具 (Mock 實作)"""
        # Phase 3: Mock 執行
        # ⛔ 禁止 logging parameters！(ADR-015 Code Review 修復)
        logger.info(f"[MOCK] STDIO call to {server.endpoint}: {tool_name}")

        mock_responses = {
            "read_file": f"[Mock] Contents of {parameters.get('path')}",
            "write_file": {"written": True, "path": parameters.get("path")},
        }
        return mock_responses.get(tool_name, {"status": "ok"})

    def _calc_duration(self, start_time: datetime) -> float:
        """計算執行時間 (毫秒)"""
        return (now_taipei() - start_time).total_seconds() * 1000

    # ==================== ActionExecutor 介面對齊 ====================

    def get_supported_operations(self) -> list[str]:
        """取得支援的操作列表 (符合 ActionExecutor 介面)"""
        operations = []
        for server_name, tools in self._tool_cache.items():
            for tool in tools:
                operations.append(f"{server_name}.{tool.name}")
        return operations

    async def execute(
        self,
        operation: str,
        parameters: dict[str, Any],
        redaction_mapping: dict[str, str] | None = None,
    ) -> MCPToolResult:
        """
        執行操作 (符合 ActionExecutor.execute 介面)

        Args:
            operation: 格式為 "server_name.tool_name"
            parameters: 工具參數
            redaction_mapping: Privacy Shield 映射表

        Returns:
            MCPToolResult
        """
        parts = operation.split(".", 1)
        if len(parts) != 2:
            return MCPToolResult(
                success=False,
                execution_id=str(uuid.uuid4()),
                error=f"Invalid operation format: {operation}. Expected: server.tool",
            )

        server_name, tool_name = parts
        return await self.call_tool(server_name, tool_name, parameters, redaction_mapping)

    async def close(self) -> None:
        """關閉連線"""
        await self._http_client.aclose()


# 全域實例
mcp_bridge = MCPBridge()
