# Generated by Copilot
# Maintained by Dinesh Kumar C (https://github.com/Dinesh210805/blender-mcp)
# Forked from original work by Siddharth Ahuja (https://github.com/ahujasid/blender-mcp)

"""
Sketchfab integration tools for the Blender MCP server.

This module provides tools for searching, downloading, and importing 3D models
from Sketchfab into Blender. It includes status checking, model search with
filtering options, and secure model download functionality.

Features:
- Sketchfab service status verification
- Model search with category filtering and pagination
- Secure model download with validation
- Comprehensive error handling and logging
"""

from typing import Optional
from ..connection import get_blender_connection
from ..utils import get_logger, validate_string_input, sanitize_text_input
from ..exceptions import BlenderMCPError, ConnectionError, ValidationError
from .base import blender_tool

# Configure logging
logger = get_logger(__name__)


@blender_tool()
def get_sketchfab_status() -> str:
    """
    Check if Sketchfab integration is enabled in Blender.
    
    This tool verifies whether the Sketchfab addon is properly installed and
    configured in Blender, allowing access to the Sketchfab model library.
    
    Returns:
        str: Status message indicating whether Sketchfab features are available,
             including helpful context about Sketchfab's strengths.
    
    Raises:
        ConnectionError: If unable to connect to Blender
        BlenderMCPError: If there's an error checking Sketchfab status
    """
    try:
        logger.info("Checking Sketchfab integration status")
        blender = get_blender_connection()
        
        result = blender.send_command("get_sketchfab_status")
        
        if not isinstance(result, dict):
            raise BlenderMCPError("Invalid response from Blender for Sketchfab status")
        
        enabled = result.get("enabled", False)
        message = result.get("message", "")
        
        # Add helpful context about Sketchfab capabilities
        if enabled:
            message += " Sketchfab excels at realistic models and offers a wider variety than PolyHaven."
        
        logger.info(f"Sketchfab status check completed: enabled={enabled}")
        return message
        
    except ConnectionError:
        raise
    except Exception as e:
        error_msg = f"Error checking Sketchfab status: {str(e)}"
        logger.error(error_msg)
        raise BlenderMCPError(error_msg) from e


@blender_tool()
def search_sketchfab_models(
    query: str,
    categories: Optional[str] = None,
    count: int = 20,
    downloadable: bool = True
) -> str:
    """
    Search for 3D models on Sketchfab with optional filtering.
    
    Performs a comprehensive search of the Sketchfab model library with support
    for category filtering, result limiting, and downloadability constraints.
    Results include detailed metadata for informed model selection.
    
    Args:
        query: Search term or description of desired models
        categories: Optional comma-separated list of category filters
        count: Maximum number of results to return (1-100, default: 20)
        downloadable: Whether to include only downloadable models (default: True)
    
    Returns:
        str: Formatted list of matching models with metadata including:
             - Model name and unique identifier (UID)
             - Author information
             - License details
             - Face count and downloadability status
    
    Raises:
        ValidationError: If input parameters are invalid
        ConnectionError: If unable to connect to Blender
        BlenderMCPError: If search operation fails
    
    Example:
        >>> search_sketchfab_models("medieval sword", categories="weapons", count=10)
        Found 8 models matching 'medieval sword':
        
        - Medieval Sword (UID: abc123)
          Author: ArtistName
          License: CC BY 4.0
          Face count: 2500
          Downloadable: Yes
        ...
    """
    try:
        # Input validation and sanitization
        query = validate_string_input(query, "query", min_length=1, max_length=200)
        query = sanitize_text_input(query)
        
        if categories is not None:
            categories = validate_string_input(categories, "categories", max_length=500)
            categories = sanitize_text_input(categories)
        
        if not isinstance(count, int) or not (1 <= count <= 100):
            raise ValidationError("count must be an integer between 1 and 100")
        
        if not isinstance(downloadable, bool):
            raise ValidationError("downloadable must be a boolean value")
        
        logger.info(f"Searching Sketchfab models: query='{query}', categories='{categories}', "
                   f"count={count}, downloadable={downloadable}")
        
        blender = get_blender_connection()
        
        # Send search command to Blender
        result = blender.send_command("search_sketchfab_models", {
            "query": query,
            "categories": categories,
            "count": count,
            "downloadable": downloadable
        })
        
        if not isinstance(result, dict):
            raise BlenderMCPError("Invalid response from Blender for Sketchfab search")
        
        if "error" in result:
            error_msg = f"Sketchfab search error: {result['error']}"
            logger.error(error_msg)
            raise BlenderMCPError(error_msg)
        
        # Process and format search results
        models = result.get("results", []) or []
        
        if not models:
            return f"No models found matching '{query}'"
        
        logger.info(f"Found {len(models)} models matching query")
        return _format_model_results(models, query)
        
    except (ValidationError, ConnectionError):
        raise
    except Exception as e:
        error_msg = f"Error searching Sketchfab models: {str(e)}"
        logger.error(error_msg, exc_info=True)
        raise BlenderMCPError(error_msg) from e


@blender_tool()
def download_sketchfab_model(uid: str) -> str:
    """
    Download and import a Sketchfab model by its unique identifier.
    
    Downloads a specific 3D model from Sketchfab and imports it into the current
    Blender scene. The model must be downloadable and you must have proper access
    rights (free models or valid subscription for premium content).
    
    Args:
        uid: The unique identifier of the Sketchfab model to download.
             This UID is obtained from search results.
    
    Returns:
        str: Success message with list of imported objects, or error details
    
    Raises:
        ValidationError: If UID format is invalid
        ConnectionError: If unable to connect to Blender
        BlenderMCPError: If download or import operation fails
    
    Security Notes:
        - Only downloads from verified Sketchfab sources
        - Validates model format and safety before import
        - Respects licensing and access permissions
    
    Example:
        >>> download_sketchfab_model("abc123def456")
        Successfully imported model. Created objects: Sword_Mesh, Sword_Handle
    """
    try:
        # Input validation and sanitization
        uid = validate_string_input(uid, "uid", min_length=1, max_length=100)
        uid = sanitize_text_input(uid, allow_special_chars=True)
        
        # Additional UID format validation (Sketchfab UIDs are typically alphanumeric)
        if not uid.replace("-", "").replace("_", "").isalnum():
            raise ValidationError("Invalid UID format: contains invalid characters")
        
        logger.info(f"Attempting to download Sketchfab model with UID: {uid}")
        
        blender = get_blender_connection()
        
        # Send download command to Blender
        result = blender.send_command("download_sketchfab_model", {
            "uid": uid
        })
        
        if not isinstance(result, dict):
            raise BlenderMCPError("Invalid response from Blender for Sketchfab download")
        
        if "error" in result:
            error_msg = f"Sketchfab download error: {result['error']}"
            logger.error(error_msg)
            raise BlenderMCPError(error_msg)
        
        # Process download results
        if result.get("success"):
            imported_objects = result.get("imported_objects", [])
            object_names = ", ".join(imported_objects) if imported_objects else "none"
            
            success_msg = f"Successfully imported model. Created objects: {object_names}"
            logger.info(f"Sketchfab model import successful: {len(imported_objects)} objects created")
            return success_msg
        else:
            failure_msg = f"Failed to download model: {result.get('message', 'Unknown error')}"
            logger.warning(f"Sketchfab model download failed: {failure_msg}")
            return failure_msg
            
    except (ValidationError, ConnectionError):
        raise
    except Exception as e:
        error_msg = f"Error downloading Sketchfab model: {str(e)}"
        logger.error(error_msg, exc_info=True)
        raise BlenderMCPError(error_msg) from e


def _format_model_results(models: list, query: str) -> str:
    """
    Format Sketchfab model search results into a readable string.
    
    Args:
        models: List of model dictionaries from Sketchfab API
        query: Original search query for context
    
    Returns:
        str: Formatted results with model details
    """
    try:
        formatted_output = f"Found {len(models)} models matching '{query}':\\n\\n"
        
        for model in models:
            if not isinstance(model, dict):
                logger.warning("Skipping invalid model data in search results")
                continue
            
            # Extract and sanitize model information
            model_name = sanitize_text_input(model.get("name", "Unnamed model"))
            model_uid = sanitize_text_input(model.get("uid", "Unknown ID"))
            
            formatted_output += f"- {model_name} (UID: {model_uid})\\n"
            
            # Process user information safely
            user = model.get("user") or {}
            if isinstance(user, dict):
                username = sanitize_text_input(user.get("username", "Unknown author"))
            else:
                username = "Unknown author"
            formatted_output += f"  Author: {username}\\n"
            
            # Process license information safely
            license_data = model.get("license") or {}
            if isinstance(license_data, dict):
                license_label = sanitize_text_input(license_data.get("label", "Unknown"))
            else:
                license_label = "Unknown"
            formatted_output += f"  License: {license_label}\\n"
            
            # Add technical details
            face_count = model.get("faceCount", "Unknown")
            is_downloadable = "Yes" if model.get("isDownloadable") else "No"
            formatted_output += f"  Face count: {face_count}\\n"
            formatted_output += f"  Downloadable: {is_downloadable}\\n\\n"
        
        return formatted_output
        
    except Exception as e:
        logger.error(f"Error formatting model results: {str(e)}")
        return f"Found {len(models)} models but encountered formatting error: {str(e)}"
