import asyncio
import traceback
import uuid
from contextlib import asynccontextmanager

from brotli_asgi import BrotliMiddleware
from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from fastapi_pagination import add_pagination
from sqlalchemy import text
from starlette.middleware.base import BaseHTTPMiddleware

from src.apps.agent_ui.socket import ui_assistant_bot_app
from src.config.async_db_setting import get_master_db_context
from src.config.logger_manager import get_logger, setup_logging
from src.config.pub_sub_manager import listen_to_redis, redis_manager
from src.config.settings import settings_instance
from src.routers import router

# setup_logging()
logger = get_logger(__name__)


# ====================================================
# Request ID Middleware
# ====================================================
class RequestIDMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        request_id = request.headers.get("X-Request-ID", str(uuid.uuid4()))
        response = await call_next(request)
        response.headers["X-Request-ID"] = request_id
        return response


# ====================================================
# Application Lifecycle Management
# ====================================================
@asynccontextmanager
async def lifespan(app: FastAPI):
    # Re-attach file handlers after uvicorn's own dictConfig has run
    setup_logging()
    logger.info("Starting application...")
    redis_task = None
    try:
        await redis_manager.connect_to_redis()
        redis_task = asyncio.create_task(listen_to_redis())
        logger.info("Application startup complete")
    except Exception as e:
        logger.warning(f"Redis unavailable at startup (continuing without it): {e}")

    yield

    if redis_task:
        redis_task.cancel()
    await redis_manager.close_redis()
    logger.info("Application shutdown complete")


# ====================================================
# App Configuration
# ====================================================
app = FastAPI(
    title=settings_instance.PROJECT_TITLE,
    license_info={"name": "Braintip AI"},
    # docs_url="/docs" if settings_instance.DEBUG else None,
    # redoc_url="/redoc" if settings_instance.DEBUG else None,
    lifespan=lifespan,
)

app.add_middleware(RequestIDMiddleware)
app.add_middleware(BrotliMiddleware, minimum_size=500)
app.add_middleware(
    CORSMiddleware,
    allow_origins=r"^https?:\/\/.*$",
    allow_credentials=True,
    allow_methods=["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"],
    allow_headers=["*"],
)


# ====================================================
# Global Error Handlers
# ====================================================
@app.exception_handler(Exception)
async def unhandled_exception_handler(request: Request, exc: Exception) -> JSONResponse:
    request_id = request.headers.get("X-Request-ID", "unknown")
    logger.error(
        "Unhandled 500 error | request_id=%s | method=%s | path=%s\n%s",
        request_id,
        request.method,
        request.url.path,
        traceback.format_exc(),
    )
    return JSONResponse(
        status_code=400,
        content={
            "detail": "Something went wrong. Please try again later.",
            "request_id": request_id,
        },
    )


# ====================================================
# Health Check
# ====================================================
@app.get("/health")
async def health():
    """Health check — verifies Redis and database connectivity."""
    redis_ok = False
    db_ok = False

    try:
        if redis_manager.redis_client:
            await redis_manager.redis_client.ping()
            redis_ok = True
    except Exception as e:
        logger.warning(f"Health check: Redis unavailable: {e}")

    try:
        async with get_master_db_context() as db:
            await db.execute(text("SELECT 1"))
        db_ok = True
    except Exception as e:
        logger.warning(f"Health check: DB unavailable: {e}")

    overall_ok = redis_ok and db_ok
    return JSONResponse(
        status_code=200 if overall_ok else 503,
        content={
            "status": "ok" if overall_ok else "degraded",
            "redis": redis_ok,
            "db": db_ok,
        },
    )


# ====================================================
# Include Routers
# ====================================================
app.include_router(router)
app.include_router(ui_assistant_bot_app)

add_pagination(app)
