"""
Module Registry Services - Module and feature management.
"""
from typing import Optional, List, Dict, Any
from uuid import UUID
import hashlib

from django.utils import timezone
from django.db.models import Q

from .models import Module, TenantModule, Feature, TenantFeatureOverride, LicenseType


class ModuleService:
    """Service for managing modules."""
    
    @staticmethod
    def register_module(manifest: Dict[str, Any]) -> Module:
        """
        Register a module from its manifest.json.
        
        Args:
            manifest: Parsed manifest.json content
            
        Returns:
            Created or updated Module instance
        """
        name = manifest.get('name')
        if not name:
            raise ValueError("Module manifest must have a 'name' field")
        
        module, created = Module.objects.update_or_create(
            name=name,
            defaults={
                'display_name': manifest.get('displayName', name),
                'description': manifest.get('description', ''),
                'version': manifest.get('version', '1.0.0'),
                'category': manifest.get('category', ''),
                'tags': manifest.get('tags', []),
                'api_prefix': manifest.get('api', {}).get('prefix', f'/api/v1/{name}'),
                'supported_api_versions': manifest.get('api', {}).get('versions', ['v1']),
                'published_events': manifest.get('provides', {}).get('events', []),
                'consumed_events': manifest.get('consumes', {}).get('events', []),
                'default_settings': manifest.get('settings', {}),
                'manifest': manifest,
            }
        )
        
        return module
    
    @staticmethod
    def get_module(name: str) -> Optional[Module]:
        """Get module by name."""
        try:
            return Module.objects.get(name=name)
        except Module.DoesNotExist:
            return None
    
    @staticmethod
    def get_active_modules() -> List[Module]:
        """Get all active modules."""
        return list(Module.objects.filter(
            status__in=['active', 'beta']
        ).order_by('category', 'display_name'))
    
    @staticmethod
    def check_dependencies(module: Module) -> Dict[str, bool]:
        """
        Check if all module dependencies are satisfied.
        
        Returns:
            Dict mapping dependency name to satisfaction status
        """
        result = {}
        for dep in module.dependencies.all():
            result[dep.name] = dep.status == 'active'
        return result


class TenantModuleService:
    """Service for managing tenant module licenses."""
    
    @staticmethod
    def enable_module(
        tenant_id: UUID,
        module_name: str,
        license_type: str = LicenseType.TRIAL,
        expires_at: Optional[timezone.datetime] = None,
        enabled_features: Optional[List[str]] = None
    ) -> TenantModule:
        """
        Enable a module for a tenant.
        
        Args:
            tenant_id: Tenant UUID
            module_name: Module name
            license_type: License type
            expires_at: Optional expiration date
            enabled_features: Optional list of features to enable
            
        Returns:
            TenantModule instance
        """
        module = Module.objects.get(name=module_name)
        
        tenant_module, created = TenantModule.objects.update_or_create(
            tenant_id=tenant_id,
            module=module,
            defaults={
                'is_enabled': True,
                'license_type': license_type,
                'expires_at': expires_at,
                'enabled_features': enabled_features or [],
            }
        )
        
        return tenant_module
    
    @staticmethod
    def disable_module(tenant_id: UUID, module_name: str) -> bool:
        """Disable a module for a tenant."""
        updated = TenantModule.objects.filter(
            tenant_id=tenant_id,
            module__name=module_name
        ).update(is_enabled=False)
        return updated > 0
    
    @staticmethod
    def get_tenant_modules(tenant_id: UUID, active_only: bool = True) -> List[TenantModule]:
        """Get all modules for a tenant."""
        qs = TenantModule.objects.filter(
            tenant_id=tenant_id
        ).select_related('module')
        
        if active_only:
            now = timezone.now()
            qs = qs.filter(
                is_enabled=True
            ).filter(
                Q(expires_at__isnull=True) | Q(expires_at__gt=now)
            )
        
        return list(qs)
    
    @staticmethod
    def has_module_access(tenant_id: UUID, module_name: str) -> bool:
        """Check if tenant has access to a module."""
        now = timezone.now()
        return TenantModule.objects.filter(
            tenant_id=tenant_id,
            module__name=module_name,
            is_enabled=True
        ).filter(
            Q(expires_at__isnull=True) | Q(expires_at__gt=now)
        ).exists()
    
    @staticmethod
    def get_module_settings(tenant_id: UUID, module_name: str) -> Dict[str, Any]:
        """
        Get effective module settings for a tenant.
        Merges default settings with tenant-specific overrides.
        """
        try:
            tenant_module = TenantModule.objects.select_related('module').get(
                tenant_id=tenant_id,
                module__name=module_name
            )
            
            # Start with default settings
            settings = dict(tenant_module.module.default_settings)
            
            # Override with tenant-specific settings
            settings.update(tenant_module.settings)
            
            return settings
            
        except TenantModule.DoesNotExist:
            return {}


class FeatureService:
    """Service for managing features and feature flags."""
    
    @staticmethod
    def is_feature_enabled(
        tenant_id: UUID,
        module_name: str,
        feature_name: str
    ) -> bool:
        """
        Check if a feature is enabled for a tenant.
        
        Considers:
        1. Tenant-specific override (highest priority)
        2. Tenant module license and enabled features
        3. Global feature status
        4. License type requirements
        5. Rollout percentage
        """
        try:
            feature = Feature.objects.select_related('module').get(
                module__name=module_name,
                name=feature_name
            )
        except Feature.DoesNotExist:
            return False
        
        # Check global feature status
        if not feature.is_enabled:
            return False
        
        # Check tenant-specific override
        try:
            override = TenantFeatureOverride.objects.get(
                tenant_id=tenant_id,
                feature=feature
            )
            
            # Check if override is expired
            if override.expires_at and override.expires_at < timezone.now():
                # Override expired, delete it
                override.delete()
            else:
                return override.is_enabled
                
        except TenantFeatureOverride.DoesNotExist:
            pass
        
        # Check tenant module license
        try:
            tenant_module = TenantModule.objects.get(
                tenant_id=tenant_id,
                module__name=module_name
            )
            
            if not tenant_module.is_active():
                return False
            
            # Check license type
            license_order = [lt[0] for lt in LicenseType.choices]
            if license_order.index(tenant_module.license_type) < license_order.index(feature.minimum_license):
                return False
            
            # Check if feature is in enabled features list
            if tenant_module.enabled_features:
                if feature_name not in tenant_module.enabled_features:
                    return False
                    
        except TenantModule.DoesNotExist:
            return False
        
        # Check rollout percentage
        if feature.rollout_percentage < 100:
            # Use tenant_id to create consistent hash
            hash_input = f"{tenant_id}:{feature.id}"
            hash_value = int(hashlib.md5(hash_input.encode()).hexdigest(), 16) % 100
            if hash_value >= feature.rollout_percentage:
                return False
        
        return True
    
    @staticmethod
    def get_enabled_features(tenant_id: UUID, module_name: str) -> List[str]:
        """Get list of all enabled features for a tenant in a module."""
        features = Feature.objects.filter(
            module__name=module_name,
            is_enabled=True
        )
        
        enabled = []
        for feature in features:
            if FeatureService.is_feature_enabled(tenant_id, module_name, feature.name):
                enabled.append(feature.name)
        
        return enabled
    
    @staticmethod
    def set_feature_override(
        tenant_id: UUID,
        module_name: str,
        feature_name: str,
        is_enabled: bool,
        reason: str = '',
        expires_at: Optional[timezone.datetime] = None,
        created_by_id: Optional[UUID] = None
    ) -> TenantFeatureOverride:
        """Set a tenant-specific feature override."""
        feature = Feature.objects.get(
            module__name=module_name,
            name=feature_name
        )
        
        override, created = TenantFeatureOverride.objects.update_or_create(
            tenant_id=tenant_id,
            feature=feature,
            defaults={
                'is_enabled': is_enabled,
                'reason': reason,
                'expires_at': expires_at,
                'created_by_id': created_by_id,
            }
        )
        
        return override
    
    @staticmethod
    def remove_feature_override(
        tenant_id: UUID,
        module_name: str,
        feature_name: str
    ) -> bool:
        """Remove a tenant-specific feature override."""
        deleted, _ = TenantFeatureOverride.objects.filter(
            tenant_id=tenant_id,
            feature__module__name=module_name,
            feature__name=feature_name
        ).delete()
        return deleted > 0


class ModuleRegistryService:
    """
    Unified service for module access checks.
    این سرویس برای استفاده در permission service و سایر بخش‌های پلتفرم طراحی شده.
    """
    
    @staticmethod
    def is_module_enabled_for_user(user, module_name: str) -> bool:
        """
        Check if user's tenant has access to a module.
        
        Args:
            user: User instance
            module_name: Name of the module
            
        Returns:
            bool: True if user has access to module
        """
        if not user or not user.is_authenticated:
            return False
        
        # Superusers have access to all modules
        if user.is_superuser:
            return True
        
        # Get tenant from user
        tenant_id = getattr(user, 'tenant_id', None)
        if not tenant_id:
            # Try to get from current schema/connection
            from django.db import connection
            if hasattr(connection, 'tenant') and connection.tenant:
                tenant_id = connection.tenant.id
        
        if not tenant_id:
            return False
        
        return TenantModuleService.has_module_access(tenant_id, module_name)
    
    @staticmethod
    def is_feature_enabled_for_user(
        user, 
        module_name: str, 
        feature_name: str
    ) -> bool:
        """
        Check if a feature is enabled for user's tenant.
        
        Args:
            user: User instance
            module_name: Name of the module
            feature_name: Name of the feature
            
        Returns:
            bool: True if feature is enabled for user
        """
        if not user or not user.is_authenticated:
            return False
        
        if user.is_superuser:
            return True
        
        # Get tenant from user
        tenant_id = getattr(user, 'tenant_id', None)
        if not tenant_id:
            from django.db import connection
            if hasattr(connection, 'tenant') and connection.tenant:
                tenant_id = connection.tenant.id
        
        if not tenant_id:
            return False
        
        return FeatureService.is_feature_enabled(tenant_id, module_name, feature_name)
    
    @staticmethod
    def get_user_modules(user) -> List[str]:
        """
        Get list of module names the user has access to.
        """
        if not user or not user.is_authenticated:
            return []
        
        if user.is_superuser:
            return list(Module.objects.filter(
                status='active'
            ).values_list('name', flat=True))
        
        tenant_id = getattr(user, 'tenant_id', None)
        if not tenant_id:
            from django.db import connection
            if hasattr(connection, 'tenant') and connection.tenant:
                tenant_id = connection.tenant.id
        
        if not tenant_id:
            return []
        
        tenant_modules = TenantModuleService.get_tenant_modules(tenant_id)
        return [tm.module.name for tm in tenant_modules]
    
    @staticmethod
    def get_user_features(user, module_name: str) -> List[str]:
        """
        Get list of enabled features for a user in a module.
        """
        if not user or not user.is_authenticated:
            return []
        
        tenant_id = getattr(user, 'tenant_id', None)
        if not tenant_id:
            from django.db import connection
            if hasattr(connection, 'tenant') and connection.tenant:
                tenant_id = connection.tenant.id
        
        if not tenant_id:
            return []
        
        if user.is_superuser:
            return list(Feature.objects.filter(
                module__name=module_name,
                is_enabled=True
            ).values_list('name', flat=True))
        
        return FeatureService.get_enabled_features(tenant_id, module_name)
