"""
Tenant Location Repository
Handles database operations for tenant location data
"""

from typing import List, Optional, Dict, Any
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, update, delete
from sqlalchemy.orm import selectinload
import structlog

from app.models.tenant_location import TenantLocation
from app.models.tenants import Tenant
from shared.database.exceptions import DatabaseError
from .base import BaseRepository


logger = structlog.get_logger()


class TenantLocationRepository(BaseRepository):
    """Repository for tenant location operations"""
    
    def __init__(self, session: AsyncSession):
        super().__init__(TenantLocation, session)

    async def create_location(self, location_data: Dict[str, Any]) -> TenantLocation:
        """
        Create a new tenant location
        
        Args:
            location_data: Dictionary containing location information
            
        Returns:
            Created TenantLocation object
        """
        try:
            # Create new location instance
            location = TenantLocation(**location_data)
            self.session.add(location)
            await self.session.commit()
            await self.session.refresh(location)
            logger.info(f"Created new tenant location: {location.id} for tenant {location.tenant_id}")
            return location
        except Exception as e:
            await self.session.rollback()
            logger.error(f"Failed to create tenant location: {str(e)}")
            raise DatabaseError(f"Failed to create tenant location: {str(e)}")

    async def get_location_by_id(self, location_id: str) -> Optional[TenantLocation]:
        """
        Get a location by its ID
        
        Args:
            location_id: UUID of the location
            
        Returns:
            TenantLocation object if found, None otherwise
        """
        try:
            stmt = select(TenantLocation).where(TenantLocation.id == location_id)
            result = await self.session.execute(stmt)
            location = result.scalar_one_or_none()
            return location
        except Exception as e:
            logger.error(f"Failed to get location by ID: {str(e)}")
            raise DatabaseError(f"Failed to get location by ID: {str(e)}")

    async def get_locations_by_tenant(self, tenant_id: str) -> List[TenantLocation]:
        """
        Get all locations for a specific tenant
        
        Args:
            tenant_id: UUID of the tenant
            
        Returns:
            List of TenantLocation objects
        """
        try:
            stmt = select(TenantLocation).where(TenantLocation.tenant_id == tenant_id)
            result = await self.session.execute(stmt)
            locations = result.scalars().all()
            return locations
        except Exception as e:
            logger.error(f"Failed to get locations by tenant: {str(e)}")
            raise DatabaseError(f"Failed to get locations by tenant: {str(e)}")

    async def get_location_by_type(self, tenant_id: str, location_type: str) -> Optional[TenantLocation]:
        """
        Get a location by tenant and type
        
        Args:
            tenant_id: UUID of the tenant
            location_type: Type of location (e.g., 'central_production', 'retail_outlet')
            
        Returns:
            TenantLocation object if found, None otherwise
        """
        try:
            stmt = select(TenantLocation).where(
                TenantLocation.tenant_id == tenant_id,
                TenantLocation.location_type == location_type
            )
            result = await self.session.execute(stmt)
            location = result.scalar_one_or_none()
            return location
        except Exception as e:
            logger.error(f"Failed to get location by type: {str(e)}")
            raise DatabaseError(f"Failed to get location by type: {str(e)}")

    async def update_location(self, location_id: str, location_data: Dict[str, Any]) -> Optional[TenantLocation]:
        """
        Update a tenant location
        
        Args:
            location_id: UUID of the location to update
            location_data: Dictionary containing updated location information
            
        Returns:
            Updated TenantLocation object if successful, None if location not found
        """
        try:
            stmt = (
                update(TenantLocation)
                .where(TenantLocation.id == location_id)
                .values(**location_data)
            )
            await self.session.execute(stmt)

            # Now fetch the updated location
            location_stmt = select(TenantLocation).where(TenantLocation.id == location_id)
            result = await self.session.execute(location_stmt)
            location = result.scalar_one_or_none()

            if location:
                await self.session.commit()
                logger.info(f"Updated tenant location: {location_id}")
                return location
            else:
                await self.session.rollback()
                logger.warning(f"Location not found for update: {location_id}")
                return None
        except Exception as e:
            await self.session.rollback()
            logger.error(f"Failed to update location: {str(e)}")
            raise DatabaseError(f"Failed to update location: {str(e)}")

    async def delete_location(self, location_id: str) -> bool:
        """
        Delete a tenant location
        
        Args:
            location_id: UUID of the location to delete
            
        Returns:
            True if deleted successfully, False if location not found
        """
        try:
            stmt = delete(TenantLocation).where(TenantLocation.id == location_id)
            result = await self.session.execute(stmt)

            if result.rowcount > 0:
                await self.session.commit()
                logger.info(f"Deleted tenant location: {location_id}")
                return True
            else:
                await self.session.rollback()
                logger.warning(f"Location not found for deletion: {location_id}")
                return False
        except Exception as e:
            await self.session.rollback()
            logger.error(f"Failed to delete location: {str(e)}")
            raise DatabaseError(f"Failed to delete location: {str(e)}")

    async def get_active_locations_by_tenant(self, tenant_id: str) -> List[TenantLocation]:
        """
        Get all active locations for a specific tenant
        
        Args:
            tenant_id: UUID of the tenant
            
        Returns:
            List of active TenantLocation objects
        """
        try:
            stmt = select(TenantLocation).where(
                TenantLocation.tenant_id == tenant_id,
                TenantLocation.is_active == True
            )
            result = await self.session.execute(stmt)
            locations = result.scalars().all()
            return locations
        except Exception as e:
            logger.error(f"Failed to get active locations by tenant: {str(e)}")
            raise DatabaseError(f"Failed to get active locations by tenant: {str(e)}")

    async def get_locations_by_tenant_with_type(self, tenant_id: str, location_types: List[str]) -> List[TenantLocation]:
        """
        Get locations for a specific tenant filtered by location types
        
        Args:
            tenant_id: UUID of the tenant
            location_types: List of location types to filter by
            
        Returns:
            List of TenantLocation objects matching the criteria
        """
        try:
            stmt = select(TenantLocation).where(
                TenantLocation.tenant_id == tenant_id,
                TenantLocation.location_type.in_(location_types)
            )
            result = await self.session.execute(stmt)
            locations = result.scalars().all()
            return locations
        except Exception as e:
            logger.error(f"Failed to get locations by tenant and type: {str(e)}")
            raise DatabaseError(f"Failed to get locations by tenant and type: {str(e)}")