"""FastAPI app factory.

W2-F13/F14: import-time `init_db()` 제거 + `on_event("startup")` → `lifespan` 전환.
W2-F15: default-user bootstrap을 `backend/scripts/bootstrap_default_users.py` CLI로 이관.
  - dev/test 환경: lifespan이 자동 호출 (기존 편의 유지).
  - prod 환경: 자동 호출 생략 — CLI로 명시적 초기화 필요.
W2-F17: insecure default credentials는 prod에서 Settings가 fail-fast로 거부.
  dev/test에서는 경고만 남긴다.
"""
from __future__ import annotations

import logging
from contextlib import asynccontextmanager

from fastapi import FastAPI
from sqlalchemy import text

from app.core.config import has_insecure_defaults, settings
from app.core.database import SessionLocal, init_db
from app.core.errors import AppError, app_error_handler
from app.core.logging_config import setup_logging
from app.i18n.loader import load_strings
from app.startup.default_users import bootstrap_default_users
from app.version import APP_VERSION

logger = logging.getLogger(__name__)


@asynccontextmanager
async def lifespan(app: FastAPI):
    """startup + shutdown. 전역 side effect는 모두 이곳에서만 수행."""
    setup_logging()
    load_strings()

    issues = has_insecure_defaults()
    if issues:
        logger.warning(
            "SECURITY: insecure default credentials in use — %s. "
            "Override via env vars before exposing this server.",
            ", ".join(issues),
        )

    init_db()

    # Codex W2 High 1: stale progress 복구는 env 무관하게 공통 경로.
    # crash/restart 복구는 prod에서도 필요하다 (아니면 'running' row가 영구 stuck).
    db = SessionLocal()
    try:
        from app.core.task_registry import recover_stale_progress
        recovered = recover_stale_progress(db)
        if recovered:
            logger.warning(
                "Recovered %d stale pipeline progress entries on startup", recovered
            )

        # default-user bootstrap은 dev/test에서만 자동. prod는 CLI 강제.
        if settings.environment in ("dev", "test"):
            bootstrap_default_users(db)
        else:
            logger.info(
                "ENVIRONMENT=%s — skipping automatic bootstrap. "
                "Run `backend/scripts/bootstrap_default_users.py` if needed.",
                settings.environment,
            )
    finally:
        db.close()

    yield
    # shutdown hooks (현재 없음)


def create_app() -> FastAPI:
    app = FastAPI(
        title=settings.app_name,
        docs_url="/api/docs",
        openapi_url="/api/openapi.json",
        lifespan=lifespan,
    )

    app.add_exception_handler(AppError, app_error_handler)

    # Register routers
    from app.api.v1.auth import router as auth_router
    from app.api.v1.users import router as users_router
    from app.api.v1.projects import router as projects_router, activity_router
    from app.api.v1.episodes import router as episodes_router
    from app.api.v1.entities import router as entities_router
    from app.api.v1.images import router as images_router
    from app.api.v1.exports import router as exports_router, import_router as project_import_router
    from app.api.v1.operations import router as operations_router
    from app.api.v1.prompts import router as prompts_router
    from app.api.v1.steps import router as steps_router
    from app.api.v1.pipeline import router as pipeline_router
    from app.api.v1.settings import router as settings_router
    app.include_router(auth_router)
    app.include_router(users_router)
    app.include_router(projects_router)
    app.include_router(activity_router)
    app.include_router(episodes_router)
    app.include_router(entities_router)
    app.include_router(images_router)
    app.include_router(exports_router)
    app.include_router(project_import_router)
    app.include_router(operations_router)
    app.include_router(prompts_router)
    app.include_router(steps_router)
    app.include_router(pipeline_router)
    app.include_router(settings_router)

    @app.get("/api/v1/health")
    @app.get("/api/health")  # 외부 모니터링 alias — 404 스팸 방지 (v0.5.20)
    def health_check():
        from app.modules.llm.gemini_key_pool import key_count
        db_ok = False
        try:
            db = SessionLocal()
            db.execute(text("SELECT 1"))
            db_ok = True
            db.close()
        except Exception:
            # 의도적: 헬스체크 DB probe 실패 → 응답에 db: error / status: degraded로 반영. 예외 세부는 의도적으로 노출 안 함.
            pass
        return {
            "status": "ok" if db_ok else "degraded",
            "version": APP_VERSION,
            "db": "ok" if db_ok else "error",
            "gemini_keys": key_count(),
            "openai_key": bool(_active_openai_key()),
        }

    return app


app = create_app()

def _active_openai_key() -> str:
    """활성 키 슬롯 — 1차 필드를 직접 보면 보조 키가 안 쓰인다."""
    from app.core.openai_keys import active_key

    return active_key()
