"""
Delivery Route Repository
"""

from typing import List, Dict, Any, Optional
from datetime import date, datetime
import uuid
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.future import select

from app.models.distribution import DeliveryRoute, DeliveryRouteStatus
from shared.database.base import Base


class DeliveryRouteRepository:
    def __init__(self, db_session: AsyncSession):
        self.db_session = db_session

    async def create_route(self, route_data: Dict[str, Any]) -> Dict[str, Any]:
        """
        Create a new delivery route
        """
        # Define system user ID to use when user_id is not provided
        SYSTEM_USER_ID = uuid.UUID("50000000-0000-0000-0000-000000000004")

        route = DeliveryRoute(
            id=uuid.uuid4(),
            tenant_id=route_data['tenant_id'],
            route_number=route_data['route_number'],
            route_date=route_data['route_date'],
            vehicle_id=route_data.get('vehicle_id'),
            driver_id=route_data.get('driver_id'),
            total_distance_km=route_data.get('total_distance_km'),
            estimated_duration_minutes=route_data.get('estimated_duration_minutes'),
            route_sequence=route_data.get('route_sequence'),
            status=route_data.get('status', 'planned'),
            created_by=route_data.get('created_by', SYSTEM_USER_ID),
            updated_by=route_data.get('updated_by', SYSTEM_USER_ID)
        )
        
        self.db_session.add(route)
        await self.db_session.commit()
        await self.db_session.refresh(route)
        
        # Convert SQLAlchemy object to dict for return
        return {
            'id': str(route.id),
            'tenant_id': str(route.tenant_id),
            'route_number': route.route_number,
            'route_date': route.route_date,
            'vehicle_id': route.vehicle_id,
            'driver_id': route.driver_id,
            'total_distance_km': route.total_distance_km,
            'estimated_duration_minutes': route.estimated_duration_minutes,
            'route_sequence': route.route_sequence,
            'status': route.status.value if hasattr(route.status, 'value') else route.status,
            'created_at': route.created_at,
            'updated_at': route.updated_at
        }

    async def get_routes_by_date(self, tenant_id: str, target_date: date) -> List[Dict[str, Any]]:
        """
        Get all delivery routes for a specific date and tenant
        """
        stmt = select(DeliveryRoute).where(
            (DeliveryRoute.tenant_id == tenant_id) & 
            (DeliveryRoute.route_date >= datetime.combine(target_date, datetime.min.time())) &
            (DeliveryRoute.route_date < datetime.combine(target_date, datetime.max.time().replace(hour=23, minute=59, second=59)))
        )
        
        result = await self.db_session.execute(stmt)
        routes = result.scalars().all()
        
        return [
            {
                'id': str(route.id),
                'tenant_id': str(route.tenant_id),
                'route_number': route.route_number,
                'route_date': route.route_date,
                'vehicle_id': route.vehicle_id,
                'driver_id': route.driver_id,
                'total_distance_km': route.total_distance_km,
                'estimated_duration_minutes': route.estimated_duration_minutes,
                'route_sequence': route.route_sequence,
                'status': route.status.value if hasattr(route.status, 'value') else route.status,
                'created_at': route.created_at,
                'updated_at': route.updated_at
            }
            for route in routes
        ]

    async def get_routes_by_date_range(self, tenant_id: str, start_date: date, end_date: date) -> List[Dict[str, Any]]:
        """
        Get all delivery routes for a specific date range and tenant
        """
        stmt = select(DeliveryRoute).where(
            (DeliveryRoute.tenant_id == tenant_id) & 
            (DeliveryRoute.route_date >= datetime.combine(start_date, datetime.min.time())) &
            (DeliveryRoute.route_date <= datetime.combine(end_date, datetime.max.time().replace(hour=23, minute=59, second=59)))
        )
        
        result = await self.db_session.execute(stmt)
        routes = result.scalars().all()
        
        return [
            {
                'id': str(route.id),
                'tenant_id': str(route.tenant_id),
                'route_number': route.route_number,
                'route_date': route.route_date,
                'vehicle_id': route.vehicle_id,
                'driver_id': route.driver_id,
                'total_distance_km': route.total_distance_km,
                'estimated_duration_minutes': route.estimated_duration_minutes,
                'route_sequence': route.route_sequence,
                'status': route.status.value if hasattr(route.status, 'value') else route.status,
                'created_at': route.created_at,
                'updated_at': route.updated_at
            }
            for route in routes
        ]

    async def get_route_by_id(self, route_id: str) -> Optional[Dict[str, Any]]:
        """
        Get a specific delivery route by ID
        """
        stmt = select(DeliveryRoute).where(DeliveryRoute.id == route_id)
        result = await self.db_session.execute(stmt)
        route = result.scalar_one_or_none()
        
        if route:
            return {
                'id': str(route.id),
                'tenant_id': str(route.tenant_id),
                'route_number': route.route_number,
                'route_date': route.route_date,
                'vehicle_id': route.vehicle_id,
                'driver_id': route.driver_id,
                'total_distance_km': route.total_distance_km,
                'estimated_duration_minutes': route.estimated_duration_minutes,
                'route_sequence': route.route_sequence,
                'status': route.status.value if hasattr(route.status, 'value') else route.status,
                'created_at': route.created_at,
                'updated_at': route.updated_at
            }
        return None

    async def update_route_status(self, route_id: str, status: str, user_id: str) -> Optional[Dict[str, Any]]:
        """
        Update route status
        """
        stmt = select(DeliveryRoute).where(DeliveryRoute.id == route_id)
        result = await self.db_session.execute(stmt)
        route = result.scalar_one_or_none()

        if not route:
            return None

        # Handle system user ID if passed as string
        if user_id == 'system':
            SYSTEM_USER_ID = uuid.UUID("50000000-0000-0000-0000-000000000004")
            route.updated_by = SYSTEM_USER_ID
        else:
            route.updated_by = user_id
        route.status = status
        await self.db_session.commit()
        await self.db_session.refresh(route)

        return {
            'id': str(route.id),
            'tenant_id': str(route.tenant_id),
            'route_number': route.route_number,
            'route_date': route.route_date,
            'vehicle_id': route.vehicle_id,
            'driver_id': route.driver_id,
            'total_distance_km': route.total_distance_km,
            'estimated_duration_minutes': route.estimated_duration_minutes,
            'route_sequence': route.route_sequence,
            'status': route.status.value if hasattr(route.status, 'value') else route.status,
            'created_at': route.created_at,
            'updated_at': route.updated_at
        }

    async def get_all_routes_for_tenant(self, tenant_id: str) -> List[Dict[str, Any]]:
        """
        Get all delivery routes for a tenant
        """
        stmt = select(DeliveryRoute).where(DeliveryRoute.tenant_id == tenant_id)
        
        result = await self.db_session.execute(stmt)
        routes = result.scalars().all()
        
        return [
            {
                'id': str(route.id),
                'tenant_id': str(route.tenant_id),
                'route_number': route.route_number,
                'route_date': route.route_date,
                'vehicle_id': route.vehicle_id,
                'driver_id': route.driver_id,
                'total_distance_km': route.total_distance_km,
                'estimated_duration_minutes': route.estimated_duration_minutes,
                'route_sequence': route.route_sequence,
                'status': route.status.value if hasattr(route.status, 'value') else route.status,
                'created_at': route.created_at,
                'updated_at': route.updated_at
            }
            for route in routes
        ]

    async def delete_demo_routes_for_tenant(self, tenant_id: str) -> int:
        """
        Delete all demo routes for a tenant
        Used for demo session cleanup

        Args:
            tenant_id: The tenant ID to delete routes for

        Returns:
            Number of routes deleted
        """
        from sqlalchemy import delete

        # Delete routes with DEMO- prefix in route_number
        stmt = delete(DeliveryRoute).where(
            (DeliveryRoute.tenant_id == uuid.UUID(tenant_id)) &
            (DeliveryRoute.route_number.like('DEMO-%'))
        )

        result = await self.db_session.execute(stmt)
        await self.db_session.commit()

        deleted_count = result.rowcount
        return deleted_count

    async def update_route_vrp_metrics(self, route_id: str, vrp_metrics: Dict[str, Any]) -> Optional[Dict[str, Any]]:
        """
        Update VRP optimization metrics for a route
        """
        stmt = select(DeliveryRoute).where(DeliveryRoute.id == route_id)
        result = await self.db_session.execute(stmt)
        route = result.scalar_one_or_none()

        if not route:
            return None

        # Update VRP metrics fields
        route.vrp_optimization_savings = vrp_metrics.get('vrp_optimization_savings')
        route.vrp_algorithm_version = vrp_metrics.get('vrp_algorithm_version')
        route.vrp_optimization_timestamp = vrp_metrics.get('vrp_optimization_timestamp')
        route.vrp_constraints_satisfied = vrp_metrics.get('vrp_constraints_satisfied')
        route.vrp_objective_value = vrp_metrics.get('vrp_objective_value')

        await self.db_session.commit()
        await self.db_session.refresh(route)

        return {
            'id': str(route.id),
            'vrp_optimization_savings': route.vrp_optimization_savings,
            'vrp_algorithm_version': route.vrp_algorithm_version,
            'vrp_optimization_timestamp': route.vrp_optimization_timestamp,
            'vrp_constraints_satisfied': route.vrp_constraints_satisfied,
            'vrp_objective_value': route.vrp_objective_value
        }

    async def get_routes_by_tenant(self, tenant_id: str, limit: int = None, offset: int = None, order_by: str = None) -> List[Dict[str, Any]]:
        """
        Get all routes for a specific tenant with pagination and ordering
        """
        stmt = select(DeliveryRoute).where(DeliveryRoute.tenant_id == tenant_id)
        
        # Apply ordering if specified
        if order_by:
            if 'vrp_optimization_timestamp' in order_by:
                if 'DESC' in order_by:
                    stmt = stmt.order_by(DeliveryRoute.vrp_optimization_timestamp.desc())
                else:
                    stmt = stmt.order_by(DeliveryRoute.vrp_optimization_timestamp.asc())
            elif 'route_date' in order_by:
                if 'DESC' in order_by:
                    stmt = stmt.order_by(DeliveryRoute.route_date.desc())
                else:
                    stmt = stmt.order_by(DeliveryRoute.route_date.asc())
        
        # Apply pagination if specified
        if limit is not None:
            stmt = stmt.limit(limit)
        if offset is not None:
            stmt = stmt.offset(offset)
        
        result = await self.db_session.execute(stmt)
        routes = result.scalars().all()
        
        return [{
            'id': str(route.id),
            'tenant_id': str(route.tenant_id),
            'route_number': route.route_number,
            'route_date': route.route_date,
            'vehicle_id': route.vehicle_id,
            'driver_id': route.driver_id,
            'total_distance_km': route.total_distance_km,
            'estimated_duration_minutes': route.estimated_duration_minutes,
            'route_sequence': route.route_sequence,
            'status': route.status.value if hasattr(route.status, 'value') else route.status,
            'created_at': route.created_at,
            'updated_at': route.updated_at,
            'vrp_optimization_savings': route.vrp_optimization_savings,
            'vrp_algorithm_version': route.vrp_algorithm_version,
            'vrp_optimization_timestamp': route.vrp_optimization_timestamp,
            'vrp_constraints_satisfied': route.vrp_constraints_satisfied,
            'vrp_objective_value': route.vrp_objective_value
        } for route in routes]