import logging
import time
from contextlib import asynccontextmanager

from fastapi import FastAPI, Request
from fastapi.exceptions import RequestValidationError
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse

from app.api.v1.router import api_router
from app.core.config import get_settings
from app.core.exceptions import AppError
from app.core.logging_config import configure_logging
from app.scheduler import shutdown_scheduler, start_scheduler

settings = get_settings()
configure_logging(
    debug=settings.debug,
    use_json=settings.use_json_logging,
    log_dir=settings.log_dir if settings.log_errors_to_file else None,
)
logger = logging.getLogger("app")


@asynccontextmanager
async def lifespan(_: FastAPI):
    if settings.scheduler_enabled:
        start_scheduler()
    yield
    if settings.scheduler_enabled:
        shutdown_scheduler()


app = FastAPI(title=settings.app_name, version="1.0.0", lifespan=lifespan)

app.add_middleware(
    CORSMiddleware,
    allow_origins=settings.cors_origin_list,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)


@app.middleware("http")
async def request_logging_middleware(request: Request, call_next):
    start = time.perf_counter()
    client_ip = request.client.host if request.client else "unknown"
    response = await call_next(request)
    duration_ms = round((time.perf_counter() - start) * 1000, 2)
    path = request.url.path
    if (
        settings.debug
        and not path.endswith("/health")
        and not path.startswith("/docs")
        and path not in {"/openapi.json", "/redoc"}
        and not path.endswith("/unread-count")
    ):
        log_record = logger.makeRecord(
            logger.name,
            logging.DEBUG,
            __file__,
            0,
            f"{request.method} {path}",
            (),
            None,
        )
        log_record.method = request.method
        log_record.path = path
        log_record.status_code = response.status_code
        log_record.duration_ms = duration_ms
        log_record.client_ip = client_ip
        logger.handle(log_record)
    return response


@app.exception_handler(RequestValidationError)
async def request_validation_handler(request: Request, exc: RequestValidationError):
    details = []
    for err in exc.errors():
        loc = err.get("loc") or ()
        field = next((part for part in reversed(loc) if part not in {"body", "query", "path"}), None)
        if field is not None:
            details.append(
                {
                    "field": str(field),
                    "error": err.get("type", "invalid"),
                    "message": err.get("msg", "Invalid value"),
                }
            )
    logger.warning(
        "Request validation failed",
        extra={"method": request.method, "path": request.url.path, "errors": details},
    )
    return JSONResponse(
        status_code=422,
        content={
            "code": "VALIDATION_ERROR",
            "message": "Please fix the highlighted fields",
            "details": details,
        },
    )


@app.exception_handler(AppError)
async def app_error_handler(request: Request, exc: AppError):
    logger.warning(
        "AppError: %s",
        exc.message,
        extra={
            "error_code": exc.code,
            "method": request.method,
            "path": request.url.path,
            "status_code": exc.status_code,
        },
    )
    return JSONResponse(
        status_code=exc.status_code,
        content={"code": exc.code, "message": exc.message, "details": exc.details},
    )


@app.exception_handler(Exception)
async def unhandled_exception_handler(request: Request, exc: Exception):
    logger.exception(
        "Unhandled error on %s %s: %s",
        request.method,
        request.url.path,
        exc,
        extra={
            "method": request.method,
            "path": request.url.path,
            "status_code": 500,
        },
    )
    return JSONResponse(
        status_code=500,
        content={
            "code": "INTERNAL_ERROR",
            "message": "An unexpected error occurred. Check server logs for details.",
            "details": [],
        },
    )


app.include_router(api_router, prefix="/api/v1")
