import logging
import sys
from contextlib import asynccontextmanager

from fastapi import Depends, FastAPI
from fastapi.middleware.cors import CORSMiddleware
from sqlalchemy import text
from sqlalchemy.ext.asyncio import AsyncSession

from app.config import settings
from app.database import get_db, init_db
from app.middleware.rate_limiter import RateLimiterMiddleware
from app.middleware.security_headers import SecurityHeadersMiddleware
from app.routers import auth, documents, decisions, votes, mandates, protocols, sanctuary, websocket
from app.routers import public
from app.routers import organizations
from app.routers import qualify
from app.routers import groups


# ── Structured logging setup ───────────────────────────────────────────────


def _setup_logging() -> None:
    """Configure structured logging based on environment.

    - Production/staging: JSON-formatted log lines for log aggregation.
    - Development: human-readable format with colors.
    """
    log_level = getattr(logging, settings.LOG_LEVEL.upper(), logging.INFO)

    if settings.ENVIRONMENT in ("production", "staging"):
        # JSON formatter for structured logging
        formatter = logging.Formatter(
            '{"timestamp":"%(asctime)s","level":"%(levelname)s",'
            '"logger":"%(name)s","message":"%(message)s"}',
            datefmt="%Y-%m-%dT%H:%M:%S",
        )
    else:
        # Human-readable format for development
        formatter = logging.Formatter(
            "%(asctime)s | %(levelname)-8s | %(name)s | %(message)s",
            datefmt="%H:%M:%S",
        )

    handler = logging.StreamHandler(sys.stdout)
    handler.setFormatter(formatter)

    root_logger = logging.getLogger()
    root_logger.setLevel(log_level)
    root_logger.handlers.clear()
    root_logger.addHandler(handler)

    # Reduce noise from third-party libraries
    logging.getLogger("uvicorn.access").setLevel(logging.WARNING)
    logging.getLogger("sqlalchemy.engine").setLevel(
        logging.INFO if settings.ENVIRONMENT == "development" else logging.WARNING
    )


_setup_logging()
logger = logging.getLogger(__name__)


# ── Application lifespan ───────────────────────────────────────────────────


@asynccontextmanager
async def lifespan(app: FastAPI):
    logger.info(
        "Demarrage %s (env=%s, log_level=%s)",
        settings.APP_NAME, settings.ENVIRONMENT, settings.LOG_LEVEL,
    )
    await init_db()
    yield
    logger.info("Arret %s", settings.APP_NAME)


# ── FastAPI application ───────────────────────────────────────────────────


app = FastAPI(
    title=settings.APP_NAME,
    description="Plateforme de decisions collectives pour la communaute Duniter/G1",
    version="0.5.0",
    lifespan=lifespan,
)


# ── Middleware stack ──────────────────────────────────────────────────────
# add_middleware is LIFO: last added = outermost = first to execute on request,
# last to execute on response (wraps everything inside it).
#
# Required order so CORS headers appear on ALL responses including 429:
#   CORS (outermost) → RateLimiter → SecurityHeaders → Application
#
# If RateLimiter were outside CORS, its 429 responses would have no CORS
# headers and the browser would silently discard them as network errors.

app.add_middleware(SecurityHeadersMiddleware)

# Prototype mode: use RATE_LIMIT_DEFAULT for auth so demos/testing don't hit
# the stricter RATE_LIMIT_AUTH (10/min). Set RATE_LIMIT_AUTH >= RATE_LIMIT_DEFAULT
# in .env only when going to real production.
_auth_rate_limit = settings.RATE_LIMIT_DEFAULT

app.add_middleware(
    RateLimiterMiddleware,
    rate_limit_default=settings.RATE_LIMIT_DEFAULT,
    rate_limit_auth=_auth_rate_limit,
    rate_limit_vote=settings.RATE_LIMIT_VOTE,
)

app.add_middleware(
    CORSMiddleware,
    allow_origins=settings.CORS_ORIGINS,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)


# ── Routers ──────────────────────────────────────────────────────────────

app.include_router(auth.router, prefix="/api/v1/auth", tags=["auth"])
app.include_router(documents.router, prefix="/api/v1/documents", tags=["documents"])
app.include_router(decisions.router, prefix="/api/v1/decisions", tags=["decisions"])
app.include_router(votes.router, prefix="/api/v1/votes", tags=["votes"])
app.include_router(mandates.router, prefix="/api/v1/mandates", tags=["mandates"])
app.include_router(protocols.router, prefix="/api/v1/protocols", tags=["protocols"])
app.include_router(sanctuary.router, prefix="/api/v1/sanctuary", tags=["sanctuary"])
app.include_router(websocket.router, prefix="/api/v1/ws", tags=["websocket"])
app.include_router(public.router, prefix="/api/v1/public", tags=["public"])
app.include_router(organizations.router, prefix="/api/v1/organizations", tags=["organizations"])
app.include_router(qualify.router, prefix="/api/v1/qualify", tags=["qualify"])
app.include_router(groups.router, prefix="/api/v1/groups", tags=["groups"])


# ── Health check ─────────────────────────────────────────────────────────


@app.get("/api/health")
async def health(db: AsyncSession = Depends(get_db)):
    """Health check endpoint that verifies database connectivity.

    Returns status "ok" with database connection info if healthy,
    or status "degraded" if the database is unreachable.
    """
    try:
        result = await db.execute(text("SELECT 1"))
        result.scalar()
        db_status = "connected"
    except Exception as exc:
        logger.warning("Health check: base de donnees inaccessible - %s", exc)
        db_status = "disconnected"

    overall_status = "ok" if db_status == "connected" else "degraded"

    return {
        "status": overall_status,
        "environment": settings.ENVIRONMENT,
        "database": db_status,
        "version": "0.5.0",
    }
