"""VIN decode router - Gemini-powered VIN string decoding.""" import logging import re import time from fastapi import APIRouter, HTTPException from app.engines.gemini_engine import ( GeminiEngine, GeminiProcessingError, GeminiUnavailableError, ) from app.models import VinDecodeRequest, VinDecodeResponse logger = logging.getLogger(__name__) router = APIRouter(prefix="/decode", tags=["decode"]) _VIN_REGEX = re.compile(r"^[A-HJ-NPR-Z0-9]{17}$") # Shared engine instance (lazy init on first request) _gemini_engine = GeminiEngine() @router.post("/vin", response_model=VinDecodeResponse) async def decode_vin(request: VinDecodeRequest) -> VinDecodeResponse: """Decode a VIN string into structured vehicle data using Gemini. Accepts a 17-character VIN and returns year, make, model, trim, etc. """ vin = request.vin.upper().strip() if not _VIN_REGEX.match(vin): raise HTTPException( status_code=400, detail=f"Invalid VIN format: must be 17 alphanumeric characters (excluding I, O, Q). Got: {vin}", ) start_ms = time.monotonic_ns() // 1_000_000 try: result = _gemini_engine.decode_vin(vin) except GeminiUnavailableError as exc: logger.error("Gemini unavailable for VIN decode: %s", exc) raise HTTPException(status_code=503, detail=str(exc)) from exc except GeminiProcessingError as exc: logger.error("Gemini processing error for VIN %s: %s", vin, exc) raise HTTPException(status_code=422, detail=str(exc)) from exc elapsed_ms = (time.monotonic_ns() // 1_000_000) - start_ms return VinDecodeResponse( success=True, vin=vin, year=result.year, make=result.make, model=result.model, trimLevel=result.trim_level, bodyType=result.body_type, driveType=result.drive_type, fuelType=result.fuel_type, engine=result.engine, transmission=result.transmission, confidence=result.confidence, processingTimeMs=elapsed_ms, error=None, )