"""
AbuLang Type Checker - Optional type checking system
"""

from typing import Dict, Any, Type, Union, List, Optional, get_type_hints
import inspect


class TypeAnnotation:
    """Type annotation for variables and functions"""
    
    def __init__(self, name: str, type_hint: Type):
        self.name = name
        self.type_hint = type_hint
    
    def check(self, value: Any) -> bool:
        """Check if value matches type"""
        if self.type_hint == Any:
            return True
        
        if isinstance(self.type_hint, type):
            return isinstance(value, self.type_hint)
        
        # Handle Union types
        if hasattr(self.type_hint, '__origin__'):
            if self.type_hint.__origin__ is Union:
                return any(isinstance(value, t) for t in self.type_hint.__args__)
        
        return True
    
    def get_error_message(self, value: Any) -> str:
        """Get type error message"""
        return f"Expected {self.type_hint.__name__}, got {type(value).__name__}"


class FunctionSignature:
    """Function signature with type hints"""
    
    def __init__(self, func):
        self.func = func
        self.name = func.__name__
        self.annotations = get_type_hints(func) if hasattr(func, '__annotations__') else {}
        self.parameters = inspect.signature(func).parameters
    
    def check_arguments(self, *args, **kwargs) -> bool:
        """Check if arguments match signature"""
        # Get parameter names
        param_names = list(self.parameters.keys())
        
        # Check positional arguments
        for i, arg in enumerate(args):
            if i < len(param_names):
                param_name = param_names[i]
                if param_name in self.annotations:
                    expected_type = self.annotations[param_name]
                    if not isinstance(arg, expected_type):
                        return False
        
        # Check keyword arguments
        for key, value in kwargs.items():
            if key in self.annotations:
                expected_type = self.annotations[key]
                if not isinstance(value, expected_type):
                    return False
        
        return True
    
    def get_signature_string(self) -> str:
        """Get function signature as string"""
        sig = inspect.signature(self.func)
        return f"{self.name}{sig}"


class TypeChecker:
    """Main type checker"""
    
    def __init__(self, strict: bool = False):
        self.strict = strict
        self.type_annotations: Dict[str, TypeAnnotation] = {}
        self.errors: List[str] = []
    
    def enable_strict(self):
        """Enable strict type checking"""
        self.strict = True
    
    def disable_strict(self):
        """Disable strict type checking"""
        self.strict = False
    
    def annotate_variable(self, name: str, type_hint: Type):
        """Annotate variable with type"""
        self.type_annotations[name] = TypeAnnotation(name, type_hint)
    
    def check_variable(self, name: str, value: Any) -> bool:
        """Check variable type"""
        if name in self.type_annotations:
            annotation = self.type_annotations[name]
            if not annotation.check(value):
                error = annotation.get_error_message(value)
                self.errors.append(error)
                if self.strict:
                    raise TypeError(error)
                return False
        return True
    
    def check_function(self, func, *args, **kwargs) -> bool:
        """Check function arguments"""
        sig = FunctionSignature(func)
        if not sig.check_arguments(*args, **kwargs):
            error = f"Invalid arguments for {sig.get_signature_string()}"
            self.errors.append(error)
            if self.strict:
                raise TypeError(error)
            return False
        return True
    
    def infer_type(self, value: Any) -> Type:
        """Infer type of value"""
        return type(value)
    
    def get_errors(self) -> List[str]:
        """Get all type errors"""
        return self.errors.copy()
    
    def clear_errors(self):
        """Clear error list"""
        self.errors.clear()
    
    def get_report(self) -> str:
        """Get type checking report"""
        if not self.errors:
            return "No type errors found"
        
        report = f"Type Checking Report ({len(self.errors)} errors):\n"
        for i, error in enumerate(self.errors, 1):
            report += f"{i}. {error}\n"
        
        return report


class TypeDecorator:
    """Decorator for type checking"""
    
    @staticmethod
    def typed(func):
        """Decorator to add type checking to function"""
        def wrapper(*args, **kwargs):
            sig = FunctionSignature(func)
            if not sig.check_arguments(*args, **kwargs):
                raise TypeError(f"Invalid arguments for {sig.get_signature_string()}")
            return func(*args, **kwargs)
        return wrapper
    
    @staticmethod
    def returns(return_type: Type):
        """Decorator to check return type"""
        def decorator(func):
            def wrapper(*args, **kwargs):
                result = func(*args, **kwargs)
                if not isinstance(result, return_type):
                    raise TypeError(f"Expected return type {return_type.__name__}, got {type(result).__name__}")
                return result
            return wrapper
        return decorator


# Global type checker
_type_checker = TypeChecker()


def get_type_checker() -> TypeChecker:
    """Get global type checker"""
    return _type_checker
