"""FastAPI application factory."""
from __future__ import annotations

import structlog
from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from telegram.ext import Application

from app.api.routes import health, media, search, webhook
from app.bot.dispatcher import build_application, register_handlers
from app.config import get_settings
from app.exceptions import AppError, ValidationError, QuotaExceededError
from app.logging_config import configure_logging
from app.services.queue import QueueService

log = structlog.get_logger(__name__)


def create_app() -> FastAPI:
    settings = get_settings()
    configure_logging(settings.log_level, settings.log_format)

    app = FastAPI(
        title="My Media API",
        description="Telegram media intelligence system",
        version="0.1.0",
        docs_url="/docs" if not settings.is_production else None,
        redoc_url=None,
    )

    # ── CORS ──────────────────────────────────────────────────────────────────
    app.add_middleware(
        CORSMiddleware,
        allow_origins=["*"] if not settings.is_production else [],
        allow_methods=["GET", "POST", "DELETE", "PATCH"],
        allow_headers=["Authorization", "Content-Type", "X-Request-ID"],
    )

    # ── Request ID middleware ─────────────────────────────────────────────────
    @app.middleware("http")
    async def request_id_middleware(request: Request, call_next):  # type: ignore[no-untyped-def]
        import uuid
        import structlog.contextvars
        request_id = request.headers.get("X-Request-ID", str(uuid.uuid4()))
        structlog.contextvars.bind_contextvars(request_id=request_id)
        response = await call_next(request)
        response.headers["X-Request-ID"] = request_id
        structlog.contextvars.unbind_contextvars("request_id")
        return response

    # ── Exception handlers ────────────────────────────────────────────────────
    @app.exception_handler(ValidationError)
    async def validation_error_handler(request: Request, exc: ValidationError) -> JSONResponse:
        return JSONResponse(
            status_code=422,
            content={"error": {"code": "VALIDATION_ERROR", "message": str(exc)}},
        )

    @app.exception_handler(QuotaExceededError)
    async def quota_error_handler(request: Request, exc: QuotaExceededError) -> JSONResponse:
        return JSONResponse(
            status_code=429,
            content={"error": {"code": "QUOTA_EXCEEDED", "message": str(exc)}},
        )

    @app.exception_handler(AppError)
    async def app_error_handler(request: Request, exc: AppError) -> JSONResponse:
        return JSONResponse(
            status_code=500,
            content={"error": {"code": "APP_ERROR", "message": str(exc)}},
        )

    # ── Lifespan ──────────────────────────────────────────────────────────────
    @app.on_event("startup")
    async def startup() -> None:
        log.info("app.startup", env=settings.app_env)

        # Build and initialize Telegram bot
        bot_app: Application = build_application(settings)
        register_handlers(bot_app)
        await bot_app.initialize()
        app.state.bot_app = bot_app

        # Set webhook
        await bot_app.bot.set_webhook(
            url=settings.telegram_webhook_url,
            secret_token=settings.telegram_webhook_secret.get_secret_value(),
        )
        log.info("bot.webhook_set", url=settings.telegram_webhook_url)

        # Ensure Redis Streams consumer groups exist
        import redis.asyncio as aioredis
        redis_client = aioredis.from_url(
            settings.redis_url.get_secret_value(),
            decode_responses=True,
        )
        queue = QueueService(redis_client)
        await queue.ensure_consumer_groups()
        app.state.redis = redis_client

    @app.on_event("shutdown")
    async def shutdown() -> None:
        if hasattr(app.state, "bot_app") and app.state.bot_app:
            await app.state.bot_app.shutdown()
        if hasattr(app.state, "redis"):
            await app.state.redis.aclose()
        log.info("app.shutdown")

    # ── Routers ───────────────────────────────────────────────────────────────
    app.include_router(health.router)
    app.include_router(webhook.router, prefix="/api/v1")
    app.include_router(media.router, prefix="/api/v1")
    app.include_router(search.router, prefix="/api/v1")

    return app


app = create_app()
