"""
RabbitMQ consumer for GlobalAuth events.

Uses a single subscriber with internal dispatch to handle
different event types correctly.
"""
from typing import Callable, Awaitable
from loguru import logger

from faststream import AckPolicy
from faststream.rabbit import RabbitBroker, RabbitQueue, RabbitExchange, ExchangeType

from opengater_globalauth_client.broker.events import (
    UserCreatedEvent,
    UserUpdatedEvent,
    AuthMethodLinkedEvent,
    AuthMethodUnlinkedEvent,
    UserBannedEvent,
    UserUnbannedEvent,
)


# Mapping event type -> event class
_EVENT_CLASSES = {
    "user_created": UserCreatedEvent,
    "user_updated": UserUpdatedEvent,
    "auth_method_linked": AuthMethodLinkedEvent,
    "auth_method_unlinked": AuthMethodUnlinkedEvent,
    "user_banned": UserBannedEvent,
    "user_unbanned": UserUnbannedEvent,
}


class GlobalAuthConsumer:
    """
    Consumer for GlobalAuth events from RabbitMQ.
    
    Uses existing RabbitBroker instance from your application.
    Registers a single subscriber on the queue and dispatches
    events to registered handlers internally.
    
    Args:
        broker: RabbitBroker instance
        queue_name: Queue name to consume from (e.g., "globalauth.opengater")
    
    Example:
        ```python
        from faststream.rabbit import RabbitBroker
        from opengater_globalauth_client.broker import GlobalAuthConsumer
        
        broker = RabbitBroker(settings.rabbitmq_url)
        consumer = GlobalAuthConsumer(broker, "globalauth.opengater")
        
        @consumer.on_user_created
        async def handle_user_created(event: UserCreatedEvent):
            print(f"New user: {event.user_id}")
            if event.invited_by_id:
                # Award referral bonus
                pass
        
        @consumer.on_auth_method_linked
        async def handle_linked(event: AuthMethodLinkedEvent):
            print(f"User {event.user_id} linked {event.auth_type}")
        ```
    """
    
    def __init__(
        self,
        broker: RabbitBroker,
        queue_name: str,
    ):
        self.broker = broker
        self.queue_name = queue_name
        
        # Exchange для событий GlobalAuth
        self._exchange = RabbitExchange(
            "globalauth.events",
            type=ExchangeType.FANOUT,
            durable=True,
        )
        
        # Очередь для получения событий
        self._queue = RabbitQueue(
            queue_name,
            durable=True,
        )
        
        # Registered handlers: event_type -> handler function
        self._handlers: dict[str, Callable] = {}
        self._subscriber_registered = False
    
    def _ensure_subscriber(self) -> None:
        """Register the single subscriber on first handler registration."""
        if self._subscriber_registered:
            return
        
        self._subscriber_registered = True
        handlers = self._handlers  # capture reference
        
        @self.broker.subscriber(
            self._queue,
            self._exchange,
            ack_policy=AckPolicy.REJECT_ON_ERROR,
        )
        async def _dispatch(data: dict) -> None:
            """Single entry point — dispatch by event type."""
            event_type = data.get("event")
            
            if not event_type:
                logger.warning(f"⚠️ GlobalAuth event without 'event' field: {data}")
                return
            
            handler = handlers.get(event_type)
            
            if handler is None:
                logger.debug(f"No handler for GlobalAuth event: {event_type}")
                return
            
            # Parse event into typed model
            event_class = _EVENT_CLASSES.get(event_type)
            
            if event_class:
                try:
                    event = event_class(**data)
                    await handler(event)
                except Exception as e:
                    logger.error(f"❌ Error handling GlobalAuth event {event_type}: {e}")
                    raise
            else:
                # Unknown event type — pass raw dict
                await handler(data)
    
    def _register_handler(
        self,
        event_type: str,
    ) -> Callable:
        """Create decorator that registers handler for event type."""
        
        def decorator(func: Callable[..., Awaitable[None]]):
            self._handlers[event_type] = func
            self._ensure_subscriber()
            return func
        
        return decorator
    
    @property
    def on_user_created(self) -> Callable:
        """
        Decorator for user_created event handler.
        
        Example:
            ```python
            @consumer.on_user_created
            async def handle(event: UserCreatedEvent):
                print(f"New user: {event.user_id}")
            ```
        """
        return self._register_handler("user_created")
    
    @property
    def on_user_updated(self) -> Callable:
        """
        Decorator for user_updated event handler.
        
        Fired when user's profile data changes on login
        (e.g. Telegram username or name changed).
        
        Example:
            ```python
            @consumer.on_user_updated
            async def handle(event: UserUpdatedEvent):
                print(f"User {event.user_id} updated: {event.telegram_username}")
            ```
        """
        return self._register_handler("user_updated")
    
    @property
    def on_auth_method_linked(self) -> Callable:
        """
        Decorator for auth_method_linked event handler.
        
        Example:
            ```python
            @consumer.on_auth_method_linked
            async def handle(event: AuthMethodLinkedEvent):
                print(f"User {event.user_id} linked {event.auth_type}")
            ```
        """
        return self._register_handler("auth_method_linked")
    
    @property
    def on_auth_method_unlinked(self) -> Callable:
        """
        Decorator for auth_method_unlinked event handler.
        
        Example:
            ```python
            @consumer.on_auth_method_unlinked
            async def handle(event: AuthMethodUnlinkedEvent):
                print(f"User {event.user_id} unlinked {event.auth_type}")
            ```
        """
        return self._register_handler("auth_method_unlinked")
    
    @property
    def on_user_banned(self) -> Callable:
        """
        Decorator for user_banned event handler.
        
        Example:
            ```python
            @consumer.on_user_banned
            async def handle(event: UserBannedEvent):
                print(f"User {event.user_id} was banned")
            ```
        """
        return self._register_handler("user_banned")
    
    @property
    def on_user_unbanned(self) -> Callable:
        """
        Decorator for user_unbanned event handler.
        
        Example:
            ```python
            @consumer.on_user_unbanned
            async def handle(event: UserUnbannedEvent):
                print(f"User {event.user_id} was unbanned")
            ```
        """
        return self._register_handler("user_unbanned")
    
    def on_event(self, event_type: str) -> Callable:
        """
        Generic decorator for any event type.
        
        Use this if you need to handle custom events or
        handle raw event data.
        
        Example:
            ```python
            @consumer.on_event("custom_event")
            async def handle(data: dict):
                print(f"Custom event: {data}")
            ```
        """
        return self._register_handler(event_type)
