"""scry-run: LLM-powered dynamic code generation via metaclasses."""

from typing import Any

from scry_run.meta import ScryClass, ScryMeta
from scry_run.cache import ScryCache
from scry_run.home import get_home, get_app_dir, ensure_home_exists
from scry_run.config import Config, load_config
from scry_run.generator import (
    CodeGenerator,
    ScryRunError,
    APIKeyError,
    RateLimitError,
    QuotaExceededError,
    ModelNotFoundError,
    ContentBlockedError,
    NetworkError,
    CodeGenerationError,
    CodeValidationError,
)
from scry_run.context import ContextBuilder
from scry_run.backends.frozen import FrozenAppError


def scry_create(name: str, description: str = "") -> type:
    """Create a new ScryClass subclass dynamically.
    
    Use this to create multiple classes without manually defining them.
    
    Args:
        name: Class name (e.g., "TodoItem", "Database")
        description: Docstring describing what this class does
        
    Returns:
        A new class inheriting from ScryClass
        
    Example:
        >>> TodoItem = scry_create("TodoItem", "A single todo item with title and status")
        >>> item = TodoItem("Buy groceries")
        >>> item.mark_done()  # auto-generated!
    """
    doc = description or f"Auto-generated {name} class"
    return type(name, (ScryClass,), {"__doc__": doc})


def is_generated(obj: Any) -> bool:
    """Check if an object (function, method, property, class) was generated by scry-run.
    
    This function reliably checks for internal metadata attached to generated code.
    It handles unwrapping of properties, classmethods, and bound methods.
    
    Args:
        obj: The object to check
        
    Returns:
        True if the object was generated by LLM, False otherwise.
    """
    marker = "_llm_is_generated"
    
    # Handle descriptors (property, classmethod, staticmethod)
    if isinstance(obj, property) and obj.fget:
        return getattr(obj.fget, marker, False)
    
    if isinstance(obj, (classmethod, staticmethod)):
        return getattr(obj.__func__, marker, False)
        
    # Handle bound methods (e.g. instance.method)
    if hasattr(obj, "__func__"):
        return getattr(obj.__func__, marker, False)
        
    # Handle plain functions/classes
    return getattr(obj, marker, False)


__version__ = "0.1.0"
__all__ = [
    "ScryClass",
    "ScryMeta",
    "ScryCache",
    "CodeGenerator",
    "ContextBuilder",
    "scry_create",
    "is_generated",
    # Home directory
    "get_home",
    "get_app_dir",
    "ensure_home_exists",
    # Config
    "Config",
    "load_config",
    # Exceptions
    "ScryRunError",
    "APIKeyError",
    "RateLimitError",
    "QuotaExceededError",
    "ModelNotFoundError",
    "ContentBlockedError",
    "NetworkError",
    "CodeGenerationError",
    "CodeValidationError",
    "FrozenAppError",
]
