"""FastAPI app factory for mdash — unified explorer."""

from __future__ import annotations

import logging
from contextlib import asynccontextmanager
from pathlib import Path

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles

from .db_adapter import BigQueryAdapter, PostgresAdapter
from .redis_client import create_redis_client
from .s3_client import create_s3_client
from .routes import router

logger = logging.getLogger("mdash")

FRONTEND_DIR = Path(__file__).parent / "static"


def create_app(
    pg_instances: list[tuple[str, str]] | None = None,
    redis_instances: list[tuple[str, str]] | None = None,
    s3_instances: list[dict] | None = None,
    bq_instances: list[tuple[str, str, str, str | None]] | None = None,
    page_size: int = 100,
) -> FastAPI:
    """Create the mdash FastAPI app.

    Args:
        pg_instances: List of (label, database_url) tuples.
        redis_instances: List of (label, redis_url) tuples.
        s3_instances: List of dicts with keys: label, endpoint_url, access_key, secret_key, region, profile.
        bq_instances: List of (label, project, dataset, credentials_path) tuples.
        page_size: Default page size.
    """
    pg_instances = pg_instances or []
    redis_instances = redis_instances or []
    s3_instances = s3_instances or []
    bq_instances = bq_instances or []

    @asynccontextmanager
    async def lifespan(app: FastAPI):
        # Database adapters (Postgres, BigQuery, etc.)
        app.state.db_adapters = {}
        for label, url in pg_instances:
            try:
                adapter = PostgresAdapter(url)
                await adapter.connect()
                app.state.db_adapters[label] = adapter
                logger.info(f"PostgreSQL '{label}' connected")
            except Exception as e:
                logger.warning(f"PostgreSQL '{label}' failed: {e}")

        for label, project, dataset, creds_path in bq_instances:
            try:
                adapter = BigQueryAdapter(project, dataset, creds_path)
                await adapter.connect()
                app.state.db_adapters[label] = adapter
                logger.info(f"BigQuery '{label}' connected")
            except Exception as e:
                logger.warning(f"BigQuery '{label}' failed: {e}")

        # Redis clients
        app.state.redis_clients = {}
        app.state.redis_labels = {}
        app.state.redis_key_caches = {}
        for label, url in redis_instances:
            try:
                app.state.redis_clients[label] = await create_redis_client(url)
                app.state.redis_labels[label] = label
                app.state.redis_key_caches[label] = {}
                logger.info(f"Redis '{label}' connected")
            except Exception as e:
                logger.warning(f"Redis '{label}' failed: {e}")

        # S3 clients
        app.state.s3_clients = {}
        app.state.s3_labels = {}
        app.state._s3_ctxs = {}
        app.state.s3_failed = set()
        for cfg in s3_instances:
            label = cfg["label"]
            try:
                ctx = create_s3_client(
                    endpoint_url=cfg.get("endpoint_url"),
                    access_key=cfg.get("access_key"),
                    secret_key=cfg.get("secret_key"),
                    region=cfg.get("region", "us-east-1"),
                    profile=cfg.get("profile"),
                )
                app.state._s3_ctxs[label] = ctx
                app.state.s3_clients[label] = await ctx.__aenter__()
                app.state.s3_labels[label] = label
                logger.info(f"S3 '{label}' connected")
            except Exception as e:
                app.state.s3_failed.add(label)
                logger.warning(f"S3 '{label}' failed: {e}")

        yield

        for label, adapter in app.state.db_adapters.items():
            try:
                await adapter.close()
            except Exception:
                pass
        for label, client in app.state.redis_clients.items():
            try:
                await client.aclose()
            except Exception:
                pass
        for label, ctx in app.state._s3_ctxs.items():
            try:
                await ctx.__aexit__(None, None, None)
            except Exception:
                pass

    app = FastAPI(title="mdash", lifespan=lifespan)

    app.add_middleware(
        CORSMiddleware,
        allow_origins=["*"],
        allow_methods=["*"],
        allow_headers=["*"],
    )

    app.state.page_size = page_size

    app.include_router(router)

    if FRONTEND_DIR.exists():
        app.mount("/", StaticFiles(directory=str(FRONTEND_DIR), html=True), name="frontend")
    else:
        logger.warning(f"Frontend dist not found at {FRONTEND_DIR}, skipping static file serving")

    return app
