"""
Decorators for automatic error capture.
"""

import functools
import asyncio
import time
from typing import Callable, Optional, Any, TypeVar, ParamSpec

from ..models.captured_models import CaptureConfig, PipelineComponent, Severity, EventType
from ..services.error_capture import get_capture_service
from ..utils.logger_config import get_logger

logger = get_logger()

P = ParamSpec('P')
T = TypeVar('T')


def capture_errors(
    component: Optional[PipelineComponent] = None,
    component_name: Optional[str] = None,
    pipeline_name: Optional[str] = None,
    engine_name: Optional[str] = None,
    severity: Severity = Severity.HIGH,
    event_type: EventType = EventType.ERROR,
    reraise: bool = True,
    log_to_console: bool = True,
    send_to_transport: bool = True,
    **metadata: Any,
) -> Callable[[Callable[P, T]], Callable[P, T]]:
    """
    Decorator to automatically capture exceptions in functions.
    
    Args:
        reraise: If True, re-raise the exception after capturing.
                 If False, swallow the exception and return None.
    """

    def decorator(func: Callable[P, T]) -> Callable[P, T]:
        @functools.wraps(func)
        def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
            config = CaptureConfig(
                component=component,
                component_name=component_name,
                pipeline_name=pipeline_name,
                engine_name=engine_name,
                severity=severity,
                event_type=event_type,
                reraise=False,
                log_to_console=log_to_console,
                send_to_transport=send_to_transport,
                metadata={
                    'function': func.__name__,
                    'module': func.__module__,
                    **metadata,
                },
            )

            try:
                return func(*args, **kwargs)
            except Exception as e:
                logger.debug(f"[DECORATOR] ❌ Caught {type(e).__name__}: {e}")
                
                service = get_capture_service()
                
                # Capture the error (won't reraise because config.reraise=False)
                try:
                    event = service.capture_exception(
                        e, 
                        config=config, 
                        skip_frames=1,
                        async_send=True
                    )
                    
                    if event:
                        logger.info(f"[DECORATOR] ✅ Event {event.id} captured and sent")
                    else:
                        logger.warning(f"[DECORATOR] ⚠️ Event capture returned None")
                        
                except Exception as capture_error:
                    logger.error(f"[DECORATOR] ❌ Capture failed: {capture_error}")
                    # Continue to reraise check below
                
                if reraise:
                    logger.debug(f"[DECORATOR] Re-raising {type(e).__name__}")
                    raise
                else:
                    logger.debug(f"[DECORATOR] Swallowing error, returning None")
                    return None  # Swallow error, return None

        return wrapper 

    return decorator


def async_capture_errors(
    component: Optional[PipelineComponent] = None,
    component_name: Optional[str] = None,
    pipeline_name: Optional[str] = None,
    engine_name: Optional[str] = None,
    severity: Severity = Severity.HIGH,
    event_type: EventType = EventType.ERROR,
    reraise: bool = True,
    log_to_console: bool = True,
    send_to_transport: bool = True,
    **metadata: Any,
) -> Callable[[Callable[P, T]], Callable[P, T]]:
    """
    Async decorator to automatically capture exceptions in async functions.
    """

    def decorator(func: Callable[P, T]) -> Callable[P, T]:
        @functools.wraps(func)
        async def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
            config = CaptureConfig(
                component=component,
                component_name=component_name,
                pipeline_name=pipeline_name,
                engine_name=engine_name,
                severity=severity,
                event_type=event_type,
                reraise=False,
                log_to_console=log_to_console,
                send_to_transport=send_to_transport,
                metadata={
                    'function': func.__name__,
                    'module': func.__module__,
                    **metadata,
                },
            )

            try:
                return await func(*args, **kwargs)
            except Exception as e:
                logger.debug(f"[ASYNC DECORATOR] ❌ Caught {type(e).__name__}: {e}")
                
                service = get_capture_service()
                
                try:
                    event = service.capture_exception(
                        e, 
                        config=config, 
                        skip_frames=1,
                        async_send=True
                    )
                    
                    if event:
                        logger.info(f"[ASYNC DECORATOR] ✅ Event {event.id} captured")
                    else:
                        logger.warning(f"[ASYNC DECORATOR] ⚠️ Event capture returned None")
                        
                except Exception as capture_error:
                    logger.error(f"[ASYNC DECORATOR] ❌ Capture failed: {capture_error}")
                
                # Handle reraise
                if reraise:
                    logger.debug(f"[ASYNC DECORATOR] Re-raising {type(e).__name__}")
                    raise
                else:
                    logger.debug(f"[ASYNC DECORATOR] Swallowing error, returning None")
                    return None

        return wrapper  # type: ignore

    return decorator


def retry_with_capture(
    max_retries: int = 3,
    delay: float = 1.0,
    backoff: float = 2.0,
    component: Optional[PipelineComponent] = None,
    component_name: Optional[str] = None,
    pipeline_name: Optional[str] = None,
    engine_name: Optional[str] = None,
    severity: Severity = Severity.MEDIUM,
    capture_all_attempts: bool = False,
    **metadata: Any,
) -> Callable[[Callable[P, T]], Callable[P, T]]:
    """
    Decorator that retries a function on failure and captures errors.
    """

    def decorator(func: Callable[P, T]) -> Callable[P, T]:
        @functools.wraps(func)
        def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
            last_exception: Optional[Exception] = None
            current_delay = delay

            for attempt in range(max_retries + 1):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    last_exception = e

                    # Determine if this is the final attempt
                    is_final = attempt == max_retries

                    # Capture this attempt if configured
                    if capture_all_attempts or is_final:
                        config = CaptureConfig(
                            component=component,
                            component_name=component_name,
                            pipeline_name=pipeline_name,
                            engine_name=engine_name,
                            severity=severity if not is_final else Severity.HIGH,
                            event_type=EventType.ERROR if is_final else EventType.WARNING,
                            reraise=False,  # Never reraise in capture_exception
                            log_to_console=True,
                            send_to_transport=is_final,  # Only send final failure
                            metadata={
                                'function': func.__name__,
                                'module': func.__module__,
                                'attempt': attempt + 1,
                                'max_retries': max_retries,
                                'is_final_attempt': is_final,
                                **metadata,
                            },
                        )
                        service = get_capture_service()
                        try:
                            service.capture_exception(e, config=config, skip_frames=1, async_send=True)
                        except Exception:
                            pass  # Ignore exceptions during capture

                    # Sleep before retry (except on last attempt)
                    if not is_final:
                        time.sleep(current_delay)
                        current_delay *= backoff

            # Raise the last exception after all retries
            if last_exception:
                raise last_exception

            # Should never reach here
            raise RuntimeError("Unexpected state in retry_with_capture")

        return wrapper  # type: ignore

    return decorator


def capture_method_errors(
    component: Optional[PipelineComponent] = None,
    severity: Severity = Severity.HIGH,
    reraise: bool = True,
) -> Callable[[type], type]:
    """
    Class decorator to wrap all methods with error capture.
    """

    def decorator(cls: type) -> type:
        for attr_name in dir(cls):
            if attr_name.startswith('_'):
                continue

            attr = getattr(cls, attr_name)
            if callable(attr):
                # Check if it's an async method
                if asyncio.iscoroutinefunction(attr):
                    wrapped = async_capture_errors(
                        component=component,
                        component_name=cls.__name__,
                        severity=severity,
                        reraise=reraise,
                    )(attr)
                else:
                    wrapped = capture_errors(
                        component=component,
                        component_name=cls.__name__,
                        severity=severity,
                        reraise=reraise,
                    )(attr)
                setattr(cls, attr_name, wrapped)

        return cls

    return decorator