"""Controller for provider payload optimization and forwarding.""" import logging import hashlib import json from typing import Dict, Any import httpx from fastapi import HTTPException from app.core.exceptions import ValidationError, APIException from app.services.routing.route_optimizer import RouteOptimizer from app.services import cache logger = logging.getLogger(__name__) class RouteController: """Controller for optimizing provider payloads and forwarding upstream.""" def __init__(self): self.route_optimizer = RouteOptimizer() def _hash_key(self, prefix: str, payload: Dict[str, Any]) -> str: """Create a stable cache key from a dict payload.""" # ensure deterministic json by sorting keys serialized = json.dumps(payload, sort_keys=True, separators=(",", ":")) digest = hashlib.sha256(serialized.encode("utf-8")).hexdigest() return f"routes:{prefix}:{digest}" async def optimize_and_forward_provider_payload(self, orders: list[dict], forward_url: str) -> dict: """Optimize provider payload and return it (forwarding paused). - Input: list of provider orders (dicts) - Output: {code, details, message, status} where details is the optimized array """ try: if not isinstance(orders, list) or not orders: raise ValidationError("Orders array is required", field="body") optimized = await self.route_optimizer.optimize_provider_payload(orders) # Debug sample of optimized payload (first 3 items, select keys) try: sample = [ { k: item.get(k) for k in ("orderheaderid", "orderid", "deliverycustomerid", "step", "previouskms", "cumulativekms", "eta") } for item in optimized[:3] ] logger.debug(f"Optimized payload sample: {sample}") trace = [ { "orderid": item.get("orderid"), "step": item.get("step"), "prev": item.get("previouskms"), "cum": item.get("cumulativekms"), } for item in optimized ] logger.debug(f"Optimized order trace: {trace}") except Exception: logger.debug("Optimized payload sample logging failed") # Forwarding paused: return optimized payload directly return { "code": 200, "details": optimized, "message": "Success", "status": True, } except ValidationError: raise except httpx.HTTPStatusError as e: status_code = e.response.status_code body_text = e.response.text logger.error(f"Forwarding failed: {status_code} - {body_text}") # Surface upstream details to the client for faster debugging raise APIException( status_code=502, message=f"Upstream service error (status {status_code}): {body_text}", code="UPSTREAM_ERROR" ) except Exception as e: logger.error(f"Error optimizing/forwarding provider payload: {e}", exc_info=True) raise APIException(status_code=500, message="Internal server error", code="INTERNAL_ERROR") # Batch routes removed - use single-route optimization for each pickup location