"""
Base tool class and common functionality for MCP tools.

This module provides the foundation for all MCP tools in the AGS SPARQL Agent,
ensuring consistent error handling, validation, and response formatting.
"""

import json
import sys
from datetime import datetime
from typing import Any, Dict, Optional
from pathlib import Path
from functools import wraps


class ToolError(Exception):
    """Custom exception for tool-specific errors."""
    pass


class BaseToolMixin:
    """Base mixin class for MCP tools providing common functionality."""
    
    @staticmethod
    def create_success_response(data: Dict[str, Any], message: str = None) -> str:
        """Create a standardized success response."""
        response = {
            "success": True,
            "timestamp": datetime.now().isoformat(),
            **data
        }
        if message:
            response["message"] = message
        return json.dumps(response, indent=2)
    
    @staticmethod
    def create_error_response(error: str, details: Dict[str, Any] = None, suggestions: list = None) -> str:
        """Create a standardized error response."""
        response = {
            "success": False,
            "error": error,
            "timestamp": datetime.now().isoformat()
        }
        if details:
            response.update(details)
        if suggestions:
            response["suggestions"] = suggestions
        return json.dumps(response, indent=2)
    
    @staticmethod
    def validate_required_param(param_value: Any, param_name: str, param_type: type = str):
        """Validate that a required parameter is provided and of correct type."""
        if param_value is None:
            raise ToolError(f"{param_name} is required")
        
        if param_type and not isinstance(param_value, param_type):
            raise ToolError(f"{param_name} must be of type {param_type.__name__}")
        
        if param_type == str and not param_value.strip():
            raise ToolError(f"{param_name} cannot be empty")
    
    @staticmethod
    def validate_uri(uri: str, param_name: str = "URI"):
        """Validate that a URI parameter is well-formed."""
        if not uri or not uri.startswith('http'):
            raise ToolError(f"{param_name} must be a valid HTTP URI")
    
    @staticmethod
    def validate_file_path(file_path: str, must_exist: bool = True):
        """Validate that a file path is valid and optionally exists."""
        if not file_path:
            raise ToolError("File path is required")
        
        path_obj = Path(file_path)
        
        if must_exist:
            if not path_obj.exists():
                raise ToolError(f"File not found: {file_path}")
            
            if not path_obj.is_file():
                raise ToolError(f"Path is not a file: {file_path}")
    
    @staticmethod
    def log_debug(message: str):
        """Log debug message to stderr (avoids stdout pollution for MCP)."""
        print(f"🔍 {message}", file=sys.stderr)
    
    @staticmethod
    def log_warning(message: str):
        """Log warning message to stderr."""
        print(f"⚠️  {message}", file=sys.stderr)
    
    @staticmethod
    def log_error(message: str):
        """Log error message to stderr."""
        print(f"❌ {message}", file=sys.stderr)


def handle_tool_errors(func):
    """Decorator to handle common tool errors and provide consistent responses."""
    @wraps(func)
    async def wrapper(*args, **kwargs):
        try:
            return await func(*args, **kwargs)
        except ToolError as e:
            return BaseToolMixin.create_error_response(str(e))
        except Exception as e:
            BaseToolMixin.log_error(f"Unexpected error in {func.__name__}: {e}")
            return BaseToolMixin.create_error_response(
                f"Internal error: {str(e)}",
                suggestions=["Check server logs for details", "Verify environment configuration"]
            )
    return wrapper