"""
Middleware integrations for popular bot frameworks
"""

import time
import functools
from typing import Callable, Optional, Any

from .async_client import AsyncMetricsFirst
from .client import MetricsFirst
from .types import (
    CommandEventData,
    InteractionEventData,
    InteractionType,
    ServiceEventData,
    ErrorEventData,
    ErrorSeverity,
)


# ==========================================
# Decorators
# ==========================================

def track_command(
    client: MetricsFirst,
    get_user_id: Callable[[Any], int],
):
    """
    Decorator to track commands (sync version).
    
    Usage:
        @track_command(mf, lambda ctx: ctx.from_user.id)
        def start_handler(ctx):
            ctx.reply("Hello!")
    """
    def decorator(func: Callable):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            start_time = time.time()
            user_id = get_user_id(args[0] if args else kwargs.get("message") or kwargs.get("ctx"))
            command = f"/{func.__name__}".replace("_handler", "").replace("_command", "")
            
            try:
                result = func(*args, **kwargs)
                response_time = int((time.time() - start_time) * 1000)
                
                client.track_command(CommandEventData(
                    user_id=user_id,
                    command=command,
                    response_time_ms=response_time,
                    is_success=True,
                ))
                
                return result
            except Exception as e:
                response_time = int((time.time() - start_time) * 1000)
                
                client.track_command(CommandEventData(
                    user_id=user_id,
                    command=command,
                    response_time_ms=response_time,
                    is_success=False,
                    error_message=str(e),
                ))
                raise
        
        return wrapper
    return decorator


def async_track_command(
    client: AsyncMetricsFirst,
    get_user_id: Callable[[Any], int],
):
    """
    Decorator to track commands (async version).
    
    Usage:
        @async_track_command(mf, lambda ctx: ctx.from_user.id)
        async def start_handler(ctx):
            await ctx.reply("Hello!")
    """
    def decorator(func: Callable):
        @functools.wraps(func)
        async def wrapper(*args, **kwargs):
            start_time = time.time()
            user_id = get_user_id(args[0] if args else kwargs.get("message") or kwargs.get("ctx"))
            command = f"/{func.__name__}".replace("_handler", "").replace("_command", "")
            
            try:
                result = await func(*args, **kwargs)
                response_time = int((time.time() - start_time) * 1000)
                
                await client.track_command(CommandEventData(
                    user_id=user_id,
                    command=command,
                    response_time_ms=response_time,
                    is_success=True,
                ))
                
                return result
            except Exception as e:
                response_time = int((time.time() - start_time) * 1000)
                
                await client.track_command(CommandEventData(
                    user_id=user_id,
                    command=command,
                    response_time_ms=response_time,
                    is_success=False,
                    error_message=str(e),
                ))
                raise
        
        return wrapper
    return decorator


def track_service(
    client: MetricsFirst,
    service_name: str,
    get_user_id: Callable[[Any], int],
    is_free: bool = True,
    price: float = 0.0,
    currency: str = "USD",
):
    """
    Decorator to track service usage (sync version).
    """
    def decorator(func: Callable):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            start_time = time.time()
            user_id = get_user_id(args[0] if args else kwargs.get("message") or kwargs.get("ctx"))
            
            try:
                result = func(*args, **kwargs)
                duration_ms = int((time.time() - start_time) * 1000)
                
                client.track_service(ServiceEventData(
                    user_id=user_id,
                    service_name=service_name,
                    is_free=is_free,
                    price=price,
                    currency=currency,
                    duration_ms=duration_ms,
                    is_success=True,
                ))
                
                return result
            except Exception as e:
                duration_ms = int((time.time() - start_time) * 1000)
                
                client.track_service(ServiceEventData(
                    user_id=user_id,
                    service_name=service_name,
                    is_free=is_free,
                    price=price,
                    currency=currency,
                    duration_ms=duration_ms,
                    is_success=False,
                    error_message=str(e),
                ))
                raise
        
        return wrapper
    return decorator


def async_track_service(
    client: AsyncMetricsFirst,
    service_name: str,
    get_user_id: Callable[[Any], int],
    is_free: bool = True,
    price: float = 0.0,
    currency: str = "USD",
):
    """
    Decorator to track service usage (async version).
    """
    def decorator(func: Callable):
        @functools.wraps(func)
        async def wrapper(*args, **kwargs):
            start_time = time.time()
            user_id = get_user_id(args[0] if args else kwargs.get("message") or kwargs.get("ctx"))
            
            try:
                result = await func(*args, **kwargs)
                duration_ms = int((time.time() - start_time) * 1000)
                
                await client.track_service(ServiceEventData(
                    user_id=user_id,
                    service_name=service_name,
                    is_free=is_free,
                    price=price,
                    currency=currency,
                    duration_ms=duration_ms,
                    is_success=True,
                ))
                
                return result
            except Exception as e:
                duration_ms = int((time.time() - start_time) * 1000)
                
                await client.track_service(ServiceEventData(
                    user_id=user_id,
                    service_name=service_name,
                    is_free=is_free,
                    price=price,
                    currency=currency,
                    duration_ms=duration_ms,
                    is_success=False,
                    error_message=str(e),
                ))
                raise
        
        return wrapper
    return decorator


def with_error_tracking(
    client: MetricsFirst,
    get_user_id: Callable[[Any], Optional[int]],
    severity: ErrorSeverity = ErrorSeverity.ERROR,
):
    """
    Decorator to automatically track errors (sync version).
    """
    def decorator(func: Callable):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            try:
                return func(*args, **kwargs)
            except Exception as e:
                user_id = None
                try:
                    user_id = get_user_id(args[0] if args else kwargs.get("message") or kwargs.get("ctx"))
                except:
                    pass
                
                client.track_error_from_exception(
                    e,
                    user_id=user_id,
                    severity=severity.value,
                    context={"handler": func.__name__},
                )
                raise
        
        return wrapper
    return decorator


def async_with_error_tracking(
    client: AsyncMetricsFirst,
    get_user_id: Callable[[Any], Optional[int]],
    severity: ErrorSeverity = ErrorSeverity.ERROR,
):
    """
    Decorator to automatically track errors (async version).
    """
    def decorator(func: Callable):
        @functools.wraps(func)
        async def wrapper(*args, **kwargs):
            try:
                return await func(*args, **kwargs)
            except Exception as e:
                user_id = None
                try:
                    user_id = get_user_id(args[0] if args else kwargs.get("message") or kwargs.get("ctx"))
                except:
                    pass
                
                await client.track_error_from_exception(
                    e,
                    user_id=user_id,
                    severity=severity.value,
                    context={"handler": func.__name__},
                )
                raise
        
        return wrapper
    return decorator


# ==========================================
# Aiogram Middleware
# ==========================================

def create_aiogram_middleware(client: AsyncMetricsFirst):
    """
    Create middleware for aiogram 3.x.
    
    Usage:
        from aiogram import Router
        from metricsfirst.middleware import create_aiogram_middleware
        
        router = Router()
        middleware = create_aiogram_middleware(mf)
        router.message.middleware(middleware)
        router.callback_query.middleware(middleware)
    """
    from aiogram import BaseMiddleware
    from aiogram.types import Message, CallbackQuery, Update
    
    class MetricsFirstMiddleware(BaseMiddleware):
        async def __call__(self, handler, event, data):
            start_time = time.time()
            user_id = None
            interaction_type = InteractionType.OTHER
            command = None
            callback_data = None
            
            # Extract user info based on event type
            if isinstance(event, Message):
                user_id = event.from_user.id if event.from_user else None
                
                if event.text and event.text.startswith("/"):
                    interaction_type = InteractionType.COMMAND
                    command = event.text.split()[0]
                elif event.photo:
                    interaction_type = InteractionType.PHOTO
                elif event.video:
                    interaction_type = InteractionType.VIDEO
                elif event.document:
                    interaction_type = InteractionType.DOCUMENT
                elif event.voice:
                    interaction_type = InteractionType.VOICE
                elif event.sticker:
                    interaction_type = InteractionType.STICKER
                else:
                    interaction_type = InteractionType.MESSAGE
                    
            elif isinstance(event, CallbackQuery):
                user_id = event.from_user.id if event.from_user else None
                interaction_type = InteractionType.CALLBACK_QUERY
                callback_data = event.data
            
            try:
                result = await handler(event, data)
                response_time = int((time.time() - start_time) * 1000)
                
                if user_id:
                    if command:
                        await client.track_command(CommandEventData(
                            user_id=user_id,
                            command=command,
                            response_time_ms=response_time,
                            is_success=True,
                        ))
                    else:
                        await client.track_interaction(InteractionEventData(
                            user_id=user_id,
                            interaction_type=interaction_type,
                            callback_data=callback_data,
                            response_time_ms=response_time,
                        ))
                
                return result
                
            except Exception as e:
                response_time = int((time.time() - start_time) * 1000)
                
                if user_id and command:
                    await client.track_command(CommandEventData(
                        user_id=user_id,
                        command=command,
                        response_time_ms=response_time,
                        is_success=False,
                        error_message=str(e),
                    ))
                
                raise
    
    return MetricsFirstMiddleware()

