# ================================================================
# shared/monitoring/decorators.py
# ================================================================
"""
Decorators for monitoring and metrics
"""

import time
import logging
import functools
from typing import Callable, Any, Optional
from .metrics import get_metrics_collector

logger = logging.getLogger(__name__)

def track_execution_time(metric_name: str, service_name: str, 
                        labels: Optional[dict] = None):
    """Decorator to track function execution time"""
    def decorator(func: Callable) -> Callable:
        @functools.wraps(func)
        async def async_wrapper(*args, **kwargs) -> Any:
            start_time = time.time()
            try:
                result = await func(*args, **kwargs)
                duration = time.time() - start_time
                
                metrics_collector = get_metrics_collector(service_name)
                if metrics_collector:
                    metrics_collector.observe_histogram(metric_name, duration, labels)
                
                return result
            except Exception as e:
                duration = time.time() - start_time
                logger.error(f"Function {func.__name__} failed after {duration:.2f}s: {e}")
                raise
        
        @functools.wraps(func)
        def sync_wrapper(*args, **kwargs) -> Any:
            start_time = time.time()
            try:
                result = func(*args, **kwargs)
                duration = time.time() - start_time
                
                metrics_collector = get_metrics_collector(service_name)
                if metrics_collector:
                    metrics_collector.observe_histogram(metric_name, duration, labels)
                
                return result
            except Exception as e:
                duration = time.time() - start_time
                logger.error(f"Function {func.__name__} failed after {duration:.2f}s: {e}")
                raise
        
        # Return appropriate wrapper based on function type
        import asyncio
        if asyncio.iscoroutinefunction(func):
            return async_wrapper
        else:
            return sync_wrapper
    
    return decorator


def count_calls(metric_name: str, service_name: str, 
                labels: Optional[dict] = None):
    """Decorator to count function calls"""
    def decorator(func: Callable) -> Callable:
        @functools.wraps(func)
        async def async_wrapper(*args, **kwargs) -> Any:
            metrics_collector = get_metrics_collector(service_name)
            if metrics_collector:
                metrics_collector.increment_counter(metric_name, labels=labels)
            return await func(*args, **kwargs)
        
        @functools.wraps(func)
        def sync_wrapper(*args, **kwargs) -> Any:
            metrics_collector = get_metrics_collector(service_name)
            if metrics_collector:
                metrics_collector.increment_counter(metric_name, labels=labels)
            return func(*args, **kwargs)
        
        # Return appropriate wrapper based on function type
        import asyncio
        if asyncio.iscoroutinefunction(func):
            return async_wrapper
        else:
            return sync_wrapper
    
    return decorator


def monitor_performance(operation_name: str, labels: Optional[dict] = None):
    """
    General purpose performance monitoring decorator
    Tracks execution time and call counts for the given operation
    """
    def decorator(func: Callable) -> Callable:
        @functools.wraps(func)
        async def async_wrapper(*args, **kwargs) -> Any:
            start_time = time.time()
            service_name = "orders-service"  # Could be dynamic based on context
            
            try:
                # Count the call
                metrics_collector = get_metrics_collector(service_name)
                if metrics_collector:
                    call_labels = {**(labels or {}), "operation": operation_name}
                    metrics_collector.increment_counter(f"{service_name}_operations_total", labels=call_labels)
                
                # Execute the function
                result = await func(*args, **kwargs)
                
                # Record success timing
                duration = time.time() - start_time
                if metrics_collector:
                    timing_labels = {**(labels or {}), "operation": operation_name, "status": "success"}
                    metrics_collector.observe_histogram(f"{service_name}_operation_duration_seconds", duration, timing_labels)
                
                return result
                
            except Exception as e:
                # Record failure timing
                duration = time.time() - start_time
                metrics_collector = get_metrics_collector(service_name)
                if metrics_collector:
                    timing_labels = {**(labels or {}), "operation": operation_name, "status": "error"}
                    metrics_collector.observe_histogram(f"{service_name}_operation_duration_seconds", duration, timing_labels)
                    
                    error_labels = {**(labels or {}), "operation": operation_name, "error_type": type(e).__name__}
                    metrics_collector.increment_counter(f"{service_name}_errors_total", labels=error_labels)
                
                logger.error(f"Operation {operation_name} failed after {duration:.2f}s: {e}")
                raise
        
        @functools.wraps(func)
        def sync_wrapper(*args, **kwargs) -> Any:
            start_time = time.time()
            service_name = "orders-service"  # Could be dynamic based on context
            
            try:
                # Count the call
                metrics_collector = get_metrics_collector(service_name)
                if metrics_collector:
                    call_labels = {**(labels or {}), "operation": operation_name}
                    metrics_collector.increment_counter(f"{service_name}_operations_total", labels=call_labels)
                
                # Execute the function
                result = func(*args, **kwargs)
                
                # Record success timing
                duration = time.time() - start_time
                if metrics_collector:
                    timing_labels = {**(labels or {}), "operation": operation_name, "status": "success"}
                    metrics_collector.observe_histogram(f"{service_name}_operation_duration_seconds", duration, timing_labels)
                
                return result
                
            except Exception as e:
                # Record failure timing
                duration = time.time() - start_time
                metrics_collector = get_metrics_collector(service_name)
                if metrics_collector:
                    timing_labels = {**(labels or {}), "operation": operation_name, "status": "error"}
                    metrics_collector.observe_histogram(f"{service_name}_operation_duration_seconds", duration, timing_labels)
                    
                    error_labels = {**(labels or {}), "operation": operation_name, "error_type": type(e).__name__}
                    metrics_collector.increment_counter(f"{service_name}_errors_total", labels=error_labels)
                
                logger.error(f"Operation {operation_name} failed after {duration:.2f}s: {e}")
                raise
        
        # Return appropriate wrapper based on function type
        import asyncio
        if asyncio.iscoroutinefunction(func):
            return async_wrapper
        else:
            return sync_wrapper
    
    return decorator