"""FastAPI application — Daily Grubs Rider Dispatch API."""

import logging
import os
import sys
import time
from contextlib import asynccontextmanager

# Load .env (DB_*, REDIS_*, GOOGLE_MAPS_API_KEY) into the environment early,
# before any service reads os.getenv at import time.
try:
    from dotenv import load_dotenv
    load_dotenv()
except Exception:
    pass

from fastapi import FastAPI, Request
from fastapi.exceptions import RequestValidationError
from fastapi.middleware.cors import CORSMiddleware
from fastapi.middleware.gzip import GZipMiddleware
from starlette.exceptions import HTTPException as StarletteHTTPException

from app.core.exception_handlers import (
    api_exception_handler,
    general_exception_handler,
    http_exception_handler,
    validation_exception_handler,
)
from app.core.exceptions import APIException
from app.middleware.request_id import RequestIDMiddleware
from app.routes import cache_router, health_router, ml_router, ml_web_router, optimization_router, batch_analytics_router, riders_router

# ---------------------------------------------------------------------------
# Logging
# ---------------------------------------------------------------------------
from app.core.log_context import RequestIdFilter

_log_level = getattr(logging, os.getenv("LOG_LEVEL", "INFO").upper(), logging.INFO)
_handler = logging.StreamHandler(sys.stdout)
_handler.addFilter(RequestIdFilter())
logging.basicConfig(
    level=_log_level,
    format="%(asctime)s - %(name)s - %(levelname)s - [req:%(request_id)s] - [%(filename)s:%(lineno)d] - %(message)s",
    datefmt="%Y-%m-%d %H:%M:%S",
    handlers=[_handler],
)
logger = logging.getLogger(__name__)
for _lib in ("httpx", "uvicorn", "uvicorn.error", "uvicorn.access"):
    logging.getLogger(_lib).setLevel(_log_level)


# ---------------------------------------------------------------------------
# Lifespan (startup / shutdown)
# ---------------------------------------------------------------------------

@asynccontextmanager
async def lifespan(app: FastAPI):
    logger.info("[START] Route Optimization API starting up...")

    # Warm up the delivery-history FAISS store in the background
    try:
        from app.services.vector.delivery_history_store import get_delivery_history_store
        store = get_delivery_history_store()
        logger.info(
            f"[DeliveryHistory] FAISS store ready — {store.record_count()} historical records loaded."
        )
    except Exception as e:
        logger.warning(f"[DeliveryHistory] Store warm-up failed (non-fatal): {e}")

    # Warm up the customer-coord FAISS store
    try:
        from app.services.vector.faiss_customer_store import get_faiss_store
        cs = get_faiss_store()
        logger.info(f"[FAISSCoords] Customer coord store ready — {cs.record_count()} records.")
    except Exception as e:
        logger.warning(f"[FAISSCoords] Store warm-up failed (non-fatal): {e}")

    # Log how many historical assignment events we have for analytics
    try:
        from app.services.ml.ml_data_collector import get_collector
        n = get_collector().count_records()
        logger.info(f"[Analytics] {n} assignment events in the analytics DB.")
    except Exception as e:
        logger.warning(f"[Analytics] DB check failed (non-fatal): {e}")

    # Start the autonomous empirical-ETA sync agent (daemon thread).
    # It mirrors completed deliveries locally and rebuilds learned ETAs on a
    # schedule, so the request path never touches Postgres.
    try:
        from app.services.routing.delivery_history_service import get_delivery_history_service
        from app.config.dynamic_config import get_config
        cfg = get_config()
        get_delivery_history_service().ensure_background_sync(
            interval_hours=int(cfg.get("eta_sync_interval_hours", 6)),
            days=int(cfg.get("eta_history_days", 14)),
        )
        logger.info("[ETA-Agent] autonomous empirical-ETA sync scheduled.")
    except Exception as e:
        logger.warning(f"[ETA-Agent] Sync agent start failed (non-fatal): {e}")

    # Start the autonomous road-sequencing decision agent. It measures road vs
    # aerial sequencing on real batches and turns routing_use_road_distance on/off
    # by itself — no manual flag flip needed.
    try:
        from app.services.routing.road_sequencing_agent import get_road_agent
        from app.config.dynamic_config import get_config
        get_road_agent().ensure_background_agent(
            interval_hours=int(get_config().get("routing_eval_interval_hours", 24)),
        )
        logger.info("[RoadAgent] autonomous road-sequencing decision agent scheduled.")
    except Exception as e:
        logger.warning(f"[RoadAgent] Decision agent start failed (non-fatal): {e}")

    logger.info("[OK] Application ready.")
    yield
    logger.info("[STOP] Route Optimization API shutting down.")


# ---------------------------------------------------------------------------
# App
# ---------------------------------------------------------------------------

app = FastAPI(
    title="Route Optimization API",
    version="2.0.0",
    docs_url="/docs",
    redoc_url="/redoc",
    openapi_url="/api/v1/openapi.json",
    lifespan=lifespan,
)

app.add_middleware(RequestIDMiddleware)
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"],
    allow_headers=["*"],
    expose_headers=["X-Request-ID", "X-Process-Time"],
)
app.add_middleware(GZipMiddleware, minimum_size=1000)


@app.middleware("http")
async def add_process_time_header(request: Request, call_next):
    start = time.time()
    response = await call_next(request)
    response.headers["X-Process-Time"] = str(round(time.time() - start, 4))
    response.headers["X-API-Version"]  = "2.0.0"
    return response


app.add_exception_handler(APIException,            api_exception_handler)
app.add_exception_handler(StarletteHTTPException,  http_exception_handler)
app.add_exception_handler(RequestValidationError,  validation_exception_handler)
app.add_exception_handler(Exception,               general_exception_handler)

app.include_router(optimization_router)
app.include_router(health_router)
app.include_router(cache_router)
app.include_router(ml_router)
app.include_router(ml_web_router)
app.include_router(batch_analytics_router)
app.include_router(riders_router)


@app.get("/", tags=["Root"])
async def root(request: Request):
    return {
        "service": "Route Optimization API",
        "version": "2.0.0",
        "status": "operational",
        "docs": "/docs",
    }


if __name__ == "__main__":
    import uvicorn
    uvicorn.run("app.main:app", host="0.0.0.0", port=8002, reload=True)
