"""
Organization Managers - Custom QuerySets and Managers for Organization models.
"""
from django.db import models
from django.db.models import Q
from datetime import date
from typing import Optional, List
from uuid import UUID


class OrganizationalUnitQuerySet(models.QuerySet):
    """Custom QuerySet for OrganizationalUnit."""
    
    def active(self):
        """Filter only active units."""
        return self.filter(is_active=True)
    
    def inactive(self):
        """Filter only inactive units."""
        return self.filter(is_active=False)
    
    def of_type(self, unit_type: str):
        """Filter by unit type."""
        return self.filter(unit_type=unit_type)
    
    def branches(self):
        """Get all branches."""
        return self.filter(unit_type='BRANCH')
    
    def departments(self):
        """Get all departments."""
        return self.filter(unit_type='DEPARTMENT')
    
    def teams(self):
        """Get all teams."""
        return self.filter(unit_type='TEAM')
    
    def root_units(self):
        """Get root units (no parent)."""
        return self.filter(parent__isnull=True)
    
    def children_of(self, parent_id: UUID):
        """Get direct children of a unit."""
        return self.filter(parent_id=parent_id)
    
    def descendants_of(self, unit):
        """Get all descendants of a unit."""
        path_prefix = f"{unit.path}{unit.pk}/"
        return self.filter(path__startswith=path_prefix)
    
    def ancestors_of(self, unit):
        """Get all ancestors of a unit."""
        if not unit.path or unit.path == "/":
            return self.none()
        
        ancestor_ids = [
            uuid for uuid in unit.path.strip('/').split('/') 
            if uuid
        ]
        return self.filter(pk__in=ancestor_ids).order_by('level')
    
    def at_level(self, level: int):
        """Get units at a specific hierarchy level."""
        return self.filter(level=level)
    
    def search(self, query: str):
        """Search by name or code."""
        return self.filter(
            Q(name__icontains=query) | Q(code__icontains=query)
        )
    
    def with_metadata_key(self, key: str):
        """Filter units that have a specific metadata key."""
        return self.filter(**{f'metadata__{key}__isnull': False})
    
    def for_user(self, user):
        """Get units accessible by a user."""
        from .models import UserOrganizationalUnit
        
        user_units = UserOrganizationalUnit.objects.filter(
            user=user
        ).filter(
            Q(end_date__isnull=True) | Q(end_date__gte=date.today())
        ).values_list('organizational_unit_id', flat=True)
        
        return self.filter(pk__in=user_units)


class OrganizationalUnitManager(models.Manager):
    """Custom Manager for OrganizationalUnit."""
    
    def get_queryset(self):
        return OrganizationalUnitQuerySet(self.model, using=self._db)
    
    def active(self):
        return self.get_queryset().active()
    
    def of_type(self, unit_type: str):
        return self.get_queryset().of_type(unit_type)
    
    def branches(self):
        return self.get_queryset().branches()
    
    def departments(self):
        return self.get_queryset().departments()
    
    def teams(self):
        return self.get_queryset().teams()
    
    def root_units(self):
        return self.get_queryset().root_units()
    
    def get_tree(self, tenant_id: UUID, root_id: Optional[UUID] = None):
        """
        Get hierarchical tree structure.
        
        Returns nested dict structure:
        {
            'unit': OrganizationalUnit,
            'children': [...]
        }
        """
        qs = self.get_queryset().filter(tenant_id=tenant_id, is_active=True)
        
        if root_id:
            root = self.get(pk=root_id)
            qs = qs.filter(
                Q(pk=root_id) | Q(path__startswith=f"{root.path}{root.pk}/")
            )
        
        units = list(qs.order_by('path', 'sort_order'))
        
        # Build tree
        unit_map = {unit.pk: {'unit': unit, 'children': []} for unit in units}
        roots = []
        
        for unit in units:
            node = unit_map[unit.pk]
            if unit.parent_id and unit.parent_id in unit_map:
                unit_map[unit.parent_id]['children'].append(node)
            else:
                roots.append(node)
        
        return roots


class UserOrganizationalUnitQuerySet(models.QuerySet):
    """Custom QuerySet for UserOrganizationalUnit."""
    
    def active(self):
        """Filter active memberships (not expired)."""
        today = date.today()
        return self.filter(
            Q(end_date__isnull=True) | Q(end_date__gte=today)
        ).filter(
            Q(start_date__isnull=True) | Q(start_date__lte=today)
        )
    
    def primary(self):
        """Filter primary unit assignments."""
        return self.filter(is_primary=True)
    
    def managers(self):
        """Filter manager assignments."""
        return self.filter(is_manager=True)
    
    def for_user(self, user):
        """Filter by user."""
        return self.filter(user=user)
    
    def for_unit(self, unit):
        """Filter by organizational unit."""
        return self.filter(organizational_unit=unit)
    
    def with_descendants(self):
        """Filter assignments that include descendant access."""
        return self.filter(include_descendants=True)


class UserOrganizationalUnitManager(models.Manager):
    """Custom Manager for UserOrganizationalUnit."""
    
    def get_queryset(self):
        return UserOrganizationalUnitQuerySet(self.model, using=self._db)
    
    def active(self):
        return self.get_queryset().active()
    
    def get_user_units(self, user, include_descendants: bool = True) -> List:
        """
        Get all organizational units accessible by a user.
        
        Args:
            user: User instance
            include_descendants: Whether to include descendant units
            
        Returns:
            List of OrganizationalUnit IDs
        """
        from .models import OrganizationalUnit
        
        memberships = self.get_queryset().active().for_user(user)
        
        unit_ids = set()
        
        for membership in memberships.select_related('organizational_unit'):
            unit = membership.organizational_unit
            unit_ids.add(unit.pk)
            
            # Add descendants if applicable
            if include_descendants and membership.include_descendants:
                descendants = OrganizationalUnit.objects.descendants_of(unit)
                unit_ids.update(d.pk for d in descendants)
        
        return list(unit_ids)
    
    def get_unit_users(self, unit, include_descendants: bool = False) -> List:
        """
        Get all users who have access to an organizational unit.
        
        Args:
            unit: OrganizationalUnit instance
            include_descendants: Whether to include users with descendant access
            
        Returns:
            List of User IDs
        """
        from .models import OrganizationalUnit
        
        # Direct members
        direct = self.get_queryset().active().for_unit(unit)
        user_ids = set(direct.values_list('user_id', flat=True))
        
        # Users with ancestor access (include_descendants=True)
        ancestors = OrganizationalUnit.objects.ancestors_of(unit)
        for ancestor in ancestors:
            ancestor_members = self.get_queryset().active().filter(
                organizational_unit=ancestor,
                include_descendants=True
            )
            user_ids.update(ancestor_members.values_list('user_id', flat=True))
        
        return list(user_ids)
