from typing import Any, Dict, Tuple
from functools import partial

from mcp import types as mcp_types

from ..logging import get_logger
from ..meta_client import MetaClient
from ..schemas import (
    GetAuditLogsRequest,
    GetPerformanceMetricsRequest,
    TrackChangesRequest,
    GetMetricsRequest,
    SetupObservabilityRequest,
    ToolResult
)
from .base import ToolHandler, _tool_schema

async def check_budget_health_handler(client: MetaClient, arguments: Dict[str, Any]) -> ToolResult:
    """
    Budget Watchdog: Checks if current spend exceeds defined safety thresholds across the account.
    """
    # Simply getting current spend and comparing to a threshold
    ad_account_id = arguments.get("ad_account_id")
    threshold = arguments.get("daily_spend_cap")
    
    if not ad_account_id or not threshold:
        return ToolResult(ok=False, error={"message": "Missing ad_account_id or daily_spend_cap"})

    try:
        # User would want to know 'today's spend'
        # We can reuse insights logic, but let's keep it self-contained for safety
        params = {
            "fields": "spend",
            "date_preset": "today",
            "level": "account"
        }
        response = await client.get(f"{ad_account_id}/insights", params=params)
        
        data = response.get("data", [])
        if not data:
            current_spend = 0.0
        else:
            current_spend = float(data[0].get("spend", 0))
            
        status = "HEALTHY"
        if current_spend > float(threshold):
            status = "CRITICAL_OVERSPEND"
        elif current_spend > (float(threshold) * 0.9):
            status = "WARNING_NEAR_CAP"
            
        return ToolResult(ok=True, data={
            "status": status,
            "current_spend": current_spend,
            "threshold": threshold,
            "utilization_percent": (current_spend / float(threshold)) * 100
        })
    except Exception as exc:
         return ToolResult(ok=False, error={"message": str(exc)})


logger = get_logger(__name__)

async def get_audit_logs_handler(client: MetaClient, arguments: Dict[str, Any]) -> ToolResult:
    request = GetAuditLogsRequest.model_validate(arguments)
    try:
        from ..audit import get_audit_logger, OperationType
        audit_logger = get_audit_logger()
        
        operation_type = None
        if request.operation_type:
            operation_type = OperationType(request.operation_type)
        
        entries = audit_logger.get_audit_entries(
            resource_type=request.resource_type,
            resource_id=request.resource_id,
            operation_type=operation_type,
            user_id=request.user_id,
            ad_account_id=request.ad_account_id,
            start_time=request.start_time,
            end_time=request.end_time,
            limit=request.limit,
        )
        
        return ToolResult(ok=True, data=[{
            "timestamp": entry.timestamp,
            "operation_type": entry.operation_type.value,
            "resource_type": entry.resource_type,
            "resource_id": entry.resource_id,
            "user_id": entry.user_id,
            "ad_account_id": entry.ad_account_id,
            "operation_details": entry.operation_details,
            "duration_ms": entry.duration_ms,
            "level": entry.level.value,
        } for entry in entries])
    except Exception as exc:
        logger.error("tool_get_audit_logs_failed", error=str(exc))
        return ToolResult(ok=False, error={"message": str(exc)})

async def get_performance_metrics_handler(client: MetaClient, arguments: Dict[str, Any]) -> ToolResult:
    request = GetPerformanceMetricsRequest.model_validate(arguments)
    try:
        from ..audit import get_audit_logger, OperationType
        audit_logger = get_audit_logger()
        
        operation_type = None
        if request.operation_type:
            operation_type = OperationType(request.operation_type)
        
        metrics = audit_logger.get_performance_metrics(
            operation_type=operation_type,
            resource_type=request.resource_type,
            ad_account_id=request.ad_account_id,
            start_time=request.start_time,
            end_time=request.end_time,
            limit=request.limit,
        )
        
        return ToolResult(ok=True, data=[{
            "operation_type": metric.operation_type.value,
            "resource_type": metric.resource_type,
            "duration_ms": metric.duration_ms,
            "success": metric.success,
            "timestamp": metric.timestamp,
            "ad_account_id": metric.ad_account_id,
            "error_type": metric.error_type,
        } for metric in metrics])
    except Exception as exc:
        logger.error("tool_get_performance_metrics_failed", error=str(exc))
        return ToolResult(ok=False, error={"message": str(exc)})

async def track_changes_handler(client: MetaClient, arguments: Dict[str, Any]) -> ToolResult:
    request = TrackChangesRequest.model_validate(arguments)
    try:
        from ..audit import get_audit_logger
        audit_logger = get_audit_logger()
        
        changes = audit_logger.track_changes(
            resource_type=request.resource_type,
            resource_id=request.resource_id,
            old_data=request.old_data,
            new_data=request.new_data,
            user_id=request.user_id,
            ad_account_id=request.ad_account_id,
        )
        
        return ToolResult(ok=True, data=[{
            "field_name": change.field_name,
            "old_value": change.old_value,
            "new_value": change.new_value,
            "change_type": change.change_type,
        } for change in changes])
    except Exception as exc:
        logger.error("tool_track_changes_failed", error=str(exc))
        return ToolResult(ok=False, error={"message": str(exc)})

async def get_metrics_handler(client: MetaClient, arguments: Dict[str, Any]) -> ToolResult:
    request = GetMetricsRequest.model_validate(arguments)
    try:
        from ..observability import get_observability_manager
        manager = get_observability_manager()
        
        metrics_data = manager.get_prometheus_metrics()
        
        if request.format == "json":
            # Convert Prometheus format to JSON (simplified)
            lines = metrics_data.strip().split('\n')
            json_metrics = []
            for line in lines:
                if line and not line.startswith('#'):
                    parts = line.split(' ')
                    if len(parts) >= 2:
                        json_metrics.append({
                            "metric": parts[0],
                            "value": parts[1],
                        })
            return ToolResult(ok=True, data={"metrics": json_metrics})
        else:
            return ToolResult(ok=True, data={"metrics": metrics_data})
            
    except Exception as exc:
        logger.error("tool_get_metrics_failed", error=str(exc))
        return ToolResult(ok=False, error={"message": str(exc)})

async def setup_observability_handler(client: MetaClient, arguments: Dict[str, Any]) -> ToolResult:
    request = SetupObservabilityRequest.model_validate(arguments)
    try:
        from ..observability import setup_observability
        setup_observability(
            service_name=request.service_name,
            service_version=request.service_version,
            enable_prometheus=request.enable_prometheus,
            enable_tracing=request.enable_tracing,
            enable_metrics=request.enable_metrics,
        )
        
        return ToolResult(ok=True, data={
            "service_name": request.service_name,
            "service_version": request.service_version,
            "prometheus_enabled": request.enable_prometheus,
            "tracing_enabled": request.enable_tracing,
            "metrics_enabled": request.enable_metrics,
            "status": "initialized",
        })
            
    except Exception as exc:
        logger.error("tool_setup_observability_failed", error=str(exc))
        return ToolResult(ok=False, error={"message": str(exc)})

def get_tools(client: MetaClient) -> Dict[str, Tuple[mcp_types.Tool, ToolHandler]]:
    return {
        "meta.get_audit_logs": (
            mcp_types.Tool(
                name="meta.get_audit_logs",
                description="Get audit logs for campaign changes, user actions, and API operations. Track who changed what and when. Use for compliance, troubleshooting, and change history.",
                inputSchema=_tool_schema(GetAuditLogsRequest),
            ),
            partial(get_audit_logs_handler, client),
        ),
        "meta.get_performance_metrics": (
            mcp_types.Tool(
                name="meta.get_performance_metrics",
                description="Get API performance metrics including response times, error rates, and throughput. Monitor system health and identify bottlenecks. Use for operations monitoring.",
                inputSchema=_tool_schema(GetPerformanceMetricsRequest),
            ),
            partial(get_performance_metrics_handler, client),
        ),
        "meta.track_changes": (
            mcp_types.Tool(
                name="meta.track_changes",
                description="Track field-level changes for audit logging. Compare old vs new values and record change history. Use for compliance auditing and rollback support.",
                inputSchema=_tool_schema(TrackChangesRequest),
            ),
            partial(track_changes_handler, client),
        ),
        "meta.get_metrics": (
            mcp_types.Tool(
                name="meta.get_metrics",
                description="Get system metrics in Prometheus format for monitoring dashboards. Includes request counts, latencies, and error rates. Use for observability and alerting integration.",
                inputSchema=_tool_schema(GetMetricsRequest),
            ),
            partial(get_metrics_handler, client),
        ),
        "meta.setup_observability": (
            mcp_types.Tool(
                name="meta.setup_observability",
                description="Configure observability stack including Prometheus metrics, distributed tracing, and service health checks. Use for production monitoring setup.",
                inputSchema=_tool_schema(SetupObservabilityRequest),
            ),
            partial(setup_observability_handler, client),
        ),
        "meta.monitor.budget_health": (
            mcp_types.Tool(
                name="meta.monitor.budget_health",
                description="Budget Watchdog: Check if account spend is within safety caps. Alerts on overspend risk and near-cap warnings. Use for automated budget protection and cost control.",
                inputSchema={
                    "type": "object",
                    "properties": {
                         "ad_account_id": {"type": "string"},
                         "daily_spend_cap": {"type": "number", "description": "Maximum allowed daily spend"}
                    },
                    "required": ["ad_account_id", "daily_spend_cap"]
                }
            ),
            partial(check_budget_health_handler, client),
        )
    }
