"""
VRP Optimization Service
Business logic for VRP optimization and metrics management
"""

from typing import List, Dict, Any, Optional
from datetime import datetime
import structlog
from sqlalchemy.ext.asyncio import AsyncSession

from app.repositories.delivery_route_repository import DeliveryRouteRepository
from app.services.routing_optimizer import RoutingOptimizer
from app.core.database import get_db

logger = structlog.get_logger()


class VRPOptimizationService:
    """
    Service for VRP optimization operations
    """
    
    def __init__(self, distribution_service: "DistributionService", database_manager: Any):
        """
        Initialize VRP optimization service
        
        Args:
            distribution_service: Distribution service instance
            database_manager: Database manager for session management
        """
        self.distribution_service = distribution_service
        self.database_manager = database_manager
        self.routing_optimizer = RoutingOptimizer()

    async def optimize_route(
        self,
        tenant_id: str,
        route_id: str,
        optimization_params: Dict[str, Any]
    ) -> Dict[str, Any]:
        """
        Optimize a specific delivery route using VRP
        
        Args:
            tenant_id: Tenant ID
            route_id: Route ID to optimize
            optimization_params: Optimization parameters
        
        Returns:
            Optimization result with metrics
        """
        try:
            # Get the current route using distribution service
            route = await self.distribution_service.get_route_by_id(route_id)
            if not route:
                raise ValueError(f"Route {route_id} not found")
            
            # Extract deliveries from route sequence
            deliveries = self._extract_deliveries_from_route(route)
            
            # Perform VRP optimization
            depot_location = optimization_params.get('depot_location', (0.0, 0.0))
            vehicle_capacity = optimization_params.get('vehicle_capacity_kg', 1000.0)
            time_limit = optimization_params.get('time_limit_seconds', 30.0)
            
            optimization_result = await self.routing_optimizer.optimize_daily_routes(
                deliveries=deliveries,
                depot_location=depot_location,
                vehicle_capacity_kg=vehicle_capacity,
                time_limit_seconds=time_limit
            )
            
            # Update route with optimization metrics
            vrp_metrics = {
                'vrp_optimization_savings': {
                    'distance_saved_km': optimization_result.get('distance_savings_km', 0.0),
                    'time_saved_minutes': optimization_result.get('time_savings_minutes', 0.0),
                    'cost_saved': optimization_result.get('cost_savings', 0.0)
                },
                'vrp_algorithm_version': 'or-tools-v1.0',
                'vrp_optimization_timestamp': datetime.utcnow(),
                'vrp_constraints_satisfied': optimization_result.get('constraints_satisfied', True),
                'vrp_objective_value': optimization_result.get('objective_value', 0.0)
            }
            
            # Update the route with VRP metrics using distribution service
            await self.distribution_service.update_route_vrp_metrics(route_id, vrp_metrics)
            
            return {
                'success': True,
                'route_id': route_id,
                'optimization_metrics': vrp_metrics,
                'optimized_route': optimization_result.get('optimized_route', [])
            }
            
        except Exception as e:
            logger.error("vrp_optimization_failed", error=str(e), route_id=route_id)
            raise

    def _extract_deliveries_from_route(self, route: Any) -> List[Dict[str, Any]]:
        """
        Extract deliveries from route sequence
        
        Args:
            route: Delivery route object
        
        Returns:
            List of delivery dictionaries
        """
        deliveries = []
        route_sequence = route.route_sequence or []
        
        for stop in route_sequence:
            deliveries.append({
                'id': stop.get('id', ''),
                'location': (stop.get('lat', 0.0), stop.get('lng', 0.0)),
                'weight_kg': stop.get('weight_kg', 0.0),
                'time_window': stop.get('time_window')
            })
        
        return deliveries

    async def get_route_optimization_metrics(
        self,
        tenant_id: str,
        route_id: str
    ) -> Dict[str, Any]:
        """
        Get VRP optimization metrics for a specific route
        
        Args:
            tenant_id: Tenant ID
            route_id: Route ID
        
        Returns:
            VRP optimization metrics
        """
        route = await self.route_repository.get_route_by_id(route_id)
        if not route:
            raise ValueError(f"Route {route_id} not found")
        
        return {
            '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_network_optimization_summary(
        self,
        tenant_id: str
    ) -> Dict[str, Any]:
        """
        Get VRP optimization summary across all routes for a tenant
        
        Args:
            tenant_id: Tenant ID
        
        Returns:
            Network optimization summary
        """
        routes = await self.route_repository.get_routes_by_tenant(tenant_id)
        
        total_optimized = 0
        total_distance_saved = 0.0
        total_time_saved = 0.0
        total_cost_saved = 0.0
        
        for route in routes:
            if route.vrp_optimization_timestamp:
                total_optimized += 1
                savings = route.vrp_optimization_savings or {}
                total_distance_saved += savings.get('distance_saved_km', 0.0)
                total_time_saved += savings.get('time_saved_minutes', 0.0)
                total_cost_saved += savings.get('cost_saved', 0.0)
        
        return {
            'total_routes': len(routes),
            'total_optimized_routes': total_optimized,
            'optimization_rate': total_optimized / len(routes) if routes else 0.0,
            'total_distance_saved_km': total_distance_saved,
            'total_time_saved_minutes': total_time_saved,
            'total_cost_saved': total_cost_saved,
            'average_savings_per_route': {
                'distance_km': total_distance_saved / total_optimized if total_optimized > 0 else 0.0,
                'time_minutes': total_time_saved / total_optimized if total_optimized > 0 else 0.0,
                'cost': total_cost_saved / total_optimized if total_optimized > 0 else 0.0
            }
        }

    async def batch_optimize_routes(
        self,
        tenant_id: str,
        route_ids: List[str],
        optimization_params: Dict[str, Any]
    ) -> Dict[str, Any]:
        """
        Batch optimize multiple routes
        
        Args:
            tenant_id: Tenant ID
            route_ids: List of route IDs to optimize
            optimization_params: Optimization parameters
        
        Returns:
            Batch optimization results
        """
        results = []
        
        for route_id in route_ids:
            try:
                result = await self.optimize_route(tenant_id, route_id, optimization_params)
                results.append({
                    'route_id': route_id,
                    'success': True,
                    'metrics': result['optimization_metrics']
                })
            except Exception as e:
                results.append({
                    'route_id': route_id,
                    'success': False,
                    'error': str(e)
                })
        
        return {
            'total_routes': len(route_ids),
            'successful_optimizations': sum(1 for r in results if r['success']),
            'failed_optimizations': sum(1 for r in results if not r['success']),
            'results': results
        }

    async def validate_optimization_constraints(
        self,
        tenant_id: str,
        route_id: str
    ) -> Dict[str, Any]:
        """
        Validate VRP optimization constraints for a route
        
        Args:
            tenant_id: Tenant ID
            route_id: Route ID
        
        Returns:
            Constraint validation results
        """
        route = await self.route_repository.get_route_by_id(route_id)
        if not route:
            raise ValueError(f"Route {route_id} not found")
        
        # Check if route has been optimized
        if not route.vrp_optimization_timestamp:
            return {
                'route_id': route_id,
                'is_optimized': False,
                'constraints_valid': False,
                'message': 'Route has not been optimized yet'
            }
        
        # Validate constraints
        constraints_valid = route.vrp_constraints_satisfied or False
        
        return {
            'route_id': route_id,
            'is_optimized': True,
            'constraints_valid': constraints_valid,
            'vrp_algorithm_version': route.vrp_algorithm_version,
            'optimization_timestamp': route.vrp_optimization_timestamp
        }

    async def get_optimization_history(
        self,
        tenant_id: str,
        limit: int = 50,
        offset: int = 0
    ) -> Dict[str, Any]:
        """
        Get VRP optimization history for a tenant
        
        Args:
            tenant_id: Tenant ID
            limit: Maximum number of records to return
            offset: Pagination offset
        
        Returns:
            Optimization history
        """
        routes = await self.route_repository.get_routes_by_tenant(
            tenant_id, 
            limit=limit, 
            offset=offset,
            order_by='vrp_optimization_timestamp DESC'
        )
        
        history = []
        for route in routes:
            if route.vrp_optimization_timestamp:
                history.append({
                    'route_id': str(route.id),
                    'route_number': route.route_number,
                    'optimization_timestamp': route.vrp_optimization_timestamp,
                    'algorithm_version': route.vrp_algorithm_version,
                    'constraints_satisfied': route.vrp_constraints_satisfied,
                    'objective_value': route.vrp_objective_value,
                    'savings': route.vrp_optimization_savings
                })
        
        return {
            'total_records': len(history),
            'history': history
        }

    async def simulate_optimization(
        self,
        tenant_id: str,
        route_data: Dict[str, Any]
    ) -> Dict[str, Any]:
        """
        Simulate VRP optimization without saving results
        
        Args:
            tenant_id: Tenant ID
            route_data: Route data for simulation
        
        Returns:
            Simulation results
        """
        try:
            deliveries = route_data.get('deliveries', [])
            depot_location = route_data.get('depot_location', (0.0, 0.0))
            vehicle_capacity = route_data.get('vehicle_capacity_kg', 1000.0)
            time_limit = route_data.get('time_limit_seconds', 30.0)
            
            simulation_result = await self.routing_optimizer.optimize_daily_routes(
                deliveries=deliveries,
                depot_location=depot_location,
                vehicle_capacity_kg=vehicle_capacity,
                time_limit_seconds=time_limit
            )
            
            return {
                'success': True,
                'simulation_results': simulation_result,
                'estimated_savings': {
                    'distance_km': simulation_result.get('distance_savings_km', 0.0),
                    'time_minutes': simulation_result.get('time_savings_minutes', 0.0),
                    'cost': simulation_result.get('cost_savings', 0.0)
                }
            }
            
        except Exception as e:
            logger.error("vrp_simulation_failed", error=str(e))
            return {
                'success': False,
                'error': str(e)
            }