"""
Core error capture service
"""

import asyncio
import threading
from typing import Optional, List, Callable
from ..models.captured_models import ErrorEvent, CaptureConfig, EventType
from ..config.settings import get_settings
from ..utils.logger_config import get_logger
from ..utils.helpers import (
    get_frame_info,
    format_stack_trace,
    is_ignored_exception,
    sanitize_metadata,
    merge_metadata,
)
from .transport import Transport, create_default_transport

logger = get_logger()

class ErrorCaptureService:
    """Core service for capturing and routing error events."""

    def __init__(self, transport: Optional[Transport] = None):
        self.settings = get_settings()
        self.transport = transport or create_default_transport()
        self.enrichers: List[Callable[[ErrorEvent], ErrorEvent]] = []
        
        self._event_loop_thread: Optional[threading.Thread] = None
        self._event_loop: Optional[asyncio.AbstractEventLoop] = None
        
        logger.info("ErrorCaptureService initialized")

    def capture_exception(
        self,
        exception: Exception,
        config: Optional[CaptureConfig] = None,
        skip_frames: int = 0,
        async_send: bool = True,
    ) -> Optional[ErrorEvent]:
        """
        Capture an exception and create an error event.
        
        Args:
            exception: The exception to capture
            config: Capture configuration
            skip_frames: Number of stack frames to skip
            async_send: If True, use non-blocking send. If False, use synchronous blocking send.
        """
        try:
            if config is None:
                config = CaptureConfig()

            if is_ignored_exception(exception, config.ignore_error_types):
                logger.debug(f"Ignoring exception type: {type(exception).__name__}")
                if config.reraise:
                    raise exception
                return None

            # Extract error information
            error_type_name = type(exception).__name__
            frame_info = get_frame_info(exception, skip_frames=skip_frames)

            # Format stack trace
            stack_trace = None
            if self.settings.enable_stack_traces:
                stack_trace = format_stack_trace(
                    exception, max_length=self.settings.max_stack_trace_length
                )

            # Merge and sanitize metadata
            merged_metadata = merge_metadata(
                config.metadata, 
                {"tags": config.tags}
            )
            metadata = sanitize_metadata(merged_metadata)
            
            if not isinstance(metadata, dict) or metadata is None:
                metadata = {}

            # Extract pipeline/engine names
            pipeline_name = config.pipeline_name
            engine_name = config.engine_name
            component = config.component
            component_name = config.component_name
            
            if not pipeline_name and 'pipeline_name' in metadata:
                pipeline_name = metadata.pop('pipeline_name', None)
            if not engine_name and 'engine_name' in metadata:
                engine_name = metadata.pop('engine_name', None)
            if not component and 'component' in metadata:
                component = metadata.pop('component', None)
            if not component_name and 'component_name' in metadata:
                component_name = metadata.pop('component_name', None)

            # Build details dict for RCA system
            details = {
                'component': str(component) if component else None,
                'component_name': component_name,
                'source': component_name or str(component) if component else None,
                'pipeline_name': pipeline_name,
                'engine_name': engine_name,
                'function': frame_info.get('function_name'),
                'module': metadata.get('module'),
                'message': str(exception),
                'original_message': {
                    'component': str(component) if component else None,
                    'component_name': component_name,
                    'message': str(exception),
                    'error_type': error_type_name,
                    **metadata
                }
            }

            # Create event
            event = ErrorEvent(
                event_type=config.event_type,
                pipeline_name=pipeline_name,
                engine_name=engine_name,
                component=component,
                component_name=component_name,
                severity=config.severity,
                message=str(exception),
                error_type=error_type_name,
                stack_trace=stack_trace,
                function_name=frame_info.get('function_name'),
                file_path=frame_info.get('file_path'),
                line_number=frame_info.get('line_number'),
                metadata=metadata,
                details=details,
            )

            # Apply enrichers
            for enricher in self.enrichers:
                try:
                    event = enricher(event)
                except Exception as e:
                    logger.warning(f"Enricher failed: {e}")

            # Log the capture
            if config.log_to_console:
                logger.opt(exception=exception).error(
                    f"Captured {error_type_name}: {str(exception)}"
                )

            if config.send_to_transport:
                logger.debug(f"[CAPTURE] Sending event {event.id} via transport.send()")
                
                try:
                    # Direct send - BackgroundTransport.send() will queue it
                    success = self.transport.send(event)
                    
                    if success:
                        logger.info(f"[CAPTURE] ✅ Event {event.id} sent/queued successfully")
                    else:
                        logger.error(f"[CAPTURE] ❌ Transport.send() returned False for event {event.id}")
                        
                except Exception as e:
                    logger.error(f"[CAPTURE] ❌ Failed to send event {event.id}: {e}")
                    import traceback
                    traceback.print_exc()

            # Reraise if configured
            if config.reraise:
                raise exception

            return event

        except Exception as e:
            # If the exception is the one we're trying to capture, reraise it
            if e is exception and config and config.reraise:
                raise

            # Otherwise, log the capture failure
            logger.critical(f"Error capture failed: {e}")
            import traceback
            traceback.print_exc()
            
            if config and config.reraise:
                raise exception

            return None
            
    async def capture_exception_async(
        self,
        exception: Exception,
        config: Optional[CaptureConfig] = None,
        skip_frames: int = 0,
    ) -> Optional[ErrorEvent]:
        """Asynchronously capture an exception."""
        # For async, just use the sync method - transport handles async internally
        return self.capture_exception(exception, config, skip_frames, async_send=True)

    def capture_message(
        self,
        message: str,
        event_type: EventType = EventType.INFO,
        config: Optional[CaptureConfig] = None,
        async_send: bool = True,
    ) -> Optional[ErrorEvent]:
        """Capture a message event (not an exception)."""
        try:
            if config is None:
                config = CaptureConfig()

            config.event_type = event_type
            config.reraise = False

            dummy_exception = Exception(message)
            event = self.capture_exception(dummy_exception, config=config, skip_frames=1, async_send=async_send)
            return event

        except Exception as e:
            logger.error(f"Failed to capture message: {e}")
            return None

    async def capture_message_async(
        self,
        message: str,
        event_type: EventType = EventType.INFO,
        config: Optional[CaptureConfig] = None,
    ) -> Optional[ErrorEvent]:
        """Asynchronously capture a message event."""
        if config is None:
            config = CaptureConfig()
        
        config.event_type = event_type
        config.reraise = False
        
        dummy_exception = Exception(message)
        return await self.capture_exception_async(dummy_exception, config=config, skip_frames=1)

    def add_transport(self, transport: Transport) -> None:
        """Add an additional transport."""
        from .transport import MultiTransport

        if isinstance(self.transport, MultiTransport):
            self.transport.add_transport(transport)
        else:
            multi = MultiTransport([self.transport, transport])
            self.transport = multi

    def add_enricher(self, enricher: Callable[[ErrorEvent], ErrorEvent]) -> None:
        """Add an event enricher function."""
        self.enrichers.append(enricher)

    async def close_async(self) -> None:
        """Asynchronously close the service and cleanup resources"""
        try:
            from .transport import BackgroundTransport, MultiTransport
            if isinstance(self.transport, BackgroundTransport):
                await self.transport.flush()
            elif isinstance(self.transport, MultiTransport):
                for transport in self.transport.transports:
                    if isinstance(transport, BackgroundTransport):
                        await transport.flush()
            
            self.transport.close()
            logger.info("ErrorCaptureService closed")
        except Exception as e:
            logger.error(f"Error closing service: {e}")

    def close(self) -> None:
        """Close the service synchronously"""
        try:
            self.transport.close()
            logger.info("ErrorCaptureService closed")
        except Exception as e:
            logger.error(f"Error closing service: {e}")


# Global singleton instance
_capture_service: Optional[ErrorCaptureService] = None

def get_capture_service() -> ErrorCaptureService:
    """Get or create the global error capture service instance."""
    global _capture_service
    if _capture_service is None:
        _capture_service = ErrorCaptureService()
    return _capture_service


async def reset_capture_service_async() -> None:
    """Asynchronously reset the global capture service."""
    global _capture_service
    if _capture_service:
        await _capture_service.close_async()
    _capture_service = None


def reset_capture_service() -> None:
    """Reset the global capture service."""
    global _capture_service
    if _capture_service:
        _capture_service.close()
    _capture_service = None