"""
Base Repository Abstract Classes

Repository pattern interfaces for domain layer.
Infrastructure layer provides implementations.
"""

from abc import ABC, abstractmethod
from typing import TypeVar, Generic, Optional, List, Any
from uuid import UUID


T = TypeVar('T')  # Entity type


class BaseRepository(ABC, Generic[T]):
    """
    Abstract base repository for synchronous operations.
    
    Provides standard CRUD operations for aggregate roots.
    Infrastructure layer must implement all abstract methods.
    """
    
    @abstractmethod
    def save(self, entity: T) -> T:
        """
        Persist an entity (create or update).
        
        Args:
            entity: The entity to persist
            
        Returns:
            The persisted entity with updated fields
        """
        pass
    
    @abstractmethod
    def get_by_id(self, id: UUID) -> Optional[T]:
        """
        Retrieve an entity by its unique identifier.
        
        Args:
            id: The entity's UUID
            
        Returns:
            The entity if found, None otherwise
        """
        pass
    
    @abstractmethod
    def find(self, **criteria) -> List[T]:
        """
        Find entities matching the given criteria.
        
        Args:
            **criteria: Field-value pairs to filter by
            
        Returns:
            List of matching entities
        """
        pass
    
    @abstractmethod
    def delete(self, id: UUID) -> bool:
        """
        Delete an entity by its identifier.
        
        Args:
            id: The entity's UUID
            
        Returns:
            True if deleted, False if not found
        """
        pass
    
    def exists(self, id: UUID) -> bool:
        """
        Check if an entity exists.
        
        Args:
            id: The entity's UUID
            
        Returns:
            True if exists, False otherwise
        """
        return self.get_by_id(id) is not None
    
    def count(self, **criteria) -> int:
        """
        Count entities matching criteria.
        
        Args:
            **criteria: Field-value pairs to filter by
            
        Returns:
            Number of matching entities
        """
        return len(self.find(**criteria))


class AsyncBaseRepository(ABC, Generic[T]):
    """
    Abstract base repository for asynchronous operations.
    
    Provides async CRUD operations for aggregate roots.
    Infrastructure layer must implement all abstract methods.
    """
    
    @abstractmethod
    async def save(self, entity: T) -> T:
        """Async version of save."""
        pass
    
    @abstractmethod
    async def get_by_id(self, id: UUID) -> Optional[T]:
        """Async version of get_by_id."""
        pass
    
    @abstractmethod
    async def find(self, **criteria) -> List[T]:
        """Async version of find."""
        pass
    
    @abstractmethod
    async def delete(self, id: UUID) -> bool:
        """Async version of delete."""
        pass
    
    async def exists(self, id: UUID) -> bool:
        """Async version of exists."""
        return await self.get_by_id(id) is not None
    
    async def count(self, **criteria) -> int:
        """Async version of count."""
        results = await self.find(**criteria)
        return len(results)


class TenantAwareRepository(BaseRepository[T], Generic[T]):
    """
    Base repository with multi-tenant awareness.
    
    Automatically scopes queries to the current tenant.
    """
    
    def __init__(self, tenant_id: UUID):
        self._tenant_id = tenant_id
    
    @property
    def tenant_id(self) -> UUID:
        """Current tenant identifier."""
        return self._tenant_id
    
    @abstractmethod
    def find_by_tenant(self, tenant_id: UUID, **criteria) -> List[T]:
        """
        Find entities for a specific tenant.
        
        Args:
            tenant_id: The tenant's UUID
            **criteria: Additional filter criteria
            
        Returns:
            List of matching entities for the tenant
        """
        pass
