"""Subscriber model for message subscription"""

from dataclasses import dataclass, field
from typing import Callable, Awaitable, Any, Optional, Dict
from enum import Enum
import asyncio
import uuid


class SubscriptionType(Enum):
    CHANNEL = "channel"
    PATTERN = "pattern"
    BROADCAST = "broadcast"


@dataclass
class Subscriber:
    """Message subscriber"""
    
    id: str = field(default_factory=lambda: str(uuid.uuid4()))
    name: str = ""
    callback: Callable[[Any], Awaitable[None]] = None
    subscription_type: SubscriptionType = SubscriptionType.CHANNEL
    pattern: str = ""
    filters: Dict[str, Any] = field(default_factory=dict)
    priority: int = 0
    is_active: bool = True
    
    async def handle_message(self, message: Any) -> bool:
        """Handle incoming message"""
        if not self.is_active:
            return False
        
        if self.filters:
            if not self._check_filters(message):
                return False
        
        if self.callback:
            try:
                await self.callback(message)
                return True
            except Exception:
                return False
        return False
    
    def _check_filters(self, message: Any) -> bool:
        """Check if message matches filters"""
        for key, expected in self.filters.items():
            if hasattr(message, key):
                actual = getattr(message, key)
                if isinstance(expected, (list, tuple)):
                    if actual not in expected:
                        return False
                elif actual != expected:
                    return False
        return True


class SubscriberRegistry:
    """Registry for managing subscribers"""
    
    def __init__(self):
        self._subscribers: Dict[str, list[Subscriber]] = {}
        self._pattern_subscribers: list[Subscriber] = []
        self._broadcast_subscribers: list[Subscriber] = []
    
    def subscribe(
        self,
        channel: str,
        callback: Callable[[Any], Awaitable[None]],
        name: str = "",
        filters: Optional[Dict[str, Any]] = None,
        priority: int = 0
    ) -> Subscriber:
        """Subscribe to a channel"""
        subscriber = Subscriber(
            name=name or f"subscriber_{channel}",
            callback=callback,
            subscription_type=SubscriptionType.CHANNEL,
            filters=filters or {},
            priority=priority
        )
        
        if channel not in self._subscribers:
            self._subscribers[channel] = []
        self._subscribers[channel].append(subscriber)
        self._subscribers[channel].sort(key=lambda s: s.priority, reverse=True)
        
        return subscriber
    
    def subscribe_pattern(
        self,
        pattern: str,
        callback: Callable[[Any], Awaitable[None]],
        name: str = "",
        filters: Optional[Dict[str, Any]] = None,
        priority: int = 0
    ) -> Subscriber:
        """Subscribe to channels matching a pattern"""
        subscriber = Subscriber(
            name=name or f"subscriber_pattern_{pattern}",
            callback=callback,
            subscription_type=SubscriptionType.PATTERN,
            pattern=pattern,
            filters=filters or {},
            priority=priority
        )
        
        self._pattern_subscribers.append(subscriber)
        self._pattern_subscribers.sort(key=lambda s: s.priority, reverse=True)
        
        return subscriber
    
    def subscribe_broadcast(
        self,
        callback: Callable[[Any], Awaitable[None]],
        name: str = "",
        filters: Optional[Dict[str, Any]] = None,
        priority: int = 0
    ) -> Subscriber:
        """Subscribe to all messages (broadcast)"""
        subscriber = Subscriber(
            name=name or "subscriber_broadcast",
            callback=callback,
            subscription_type=SubscriptionType.BROADCAST,
            filters=filters or {},
            priority=priority
        )
        
        self._broadcast_subscribers.append(subscriber)
        self._broadcast_subscribers.sort(key=lambda s: s.priority, reverse=True)
        
        return subscriber
    
    def unsubscribe(self, subscriber_id: str) -> bool:
        """Unsubscribe a subscriber"""
        for channel_subs in self._subscribers.values():
            for i, sub in enumerate(channel_subs):
                if sub.id == subscriber_id:
                    channel_subs.pop(i)
                    return True
        
        for i, sub in enumerate(self._pattern_subscribers):
            if sub.id == subscriber_id:
                self._pattern_subscribers.pop(i)
                return True
        
        for i, sub in enumerate(self._broadcast_subscribers):
            if sub.id == subscriber_id:
                self._broadcast_subscribers.pop(i)
                return True
        
        return False
    
    def get_subscribers(self, channel: str) -> list[Subscriber]:
        """Get all subscribers for a channel"""
        subscribers = list(self._subscribers.get(channel, []))
        
        for pattern_sub in self._pattern_subscribers:
            if self._match_pattern(channel, pattern_sub.pattern):
                subscribers.append(pattern_sub)
        
        return subscribers
    
    def get_all_subscribers(self) -> list[Subscriber]:
        """Get all subscribers including broadcast"""
        all_subs = []
        for subs in self._subscribers.values():
            all_subs.extend(subs)
        all_subs.extend(self._pattern_subscribers)
        all_subs.extend(self._broadcast_subscribers)
        return all_subs
    
    def _match_pattern(self, channel: str, pattern: str) -> bool:
        """Check if channel matches pattern"""
        import fnmatch
        return fnmatch.fnmatch(channel, pattern)
