from __future__ import annotations

from contextlib import asynccontextmanager

from fastapi import Depends, FastAPI
from fastapi import HTTPException, status
from sqlalchemy.engine import make_url

from govoplan_core.auth import ApiPrincipal, require_scope
from govoplan_core.core.registry import PlatformRegistry
from govoplan_core.db.bootstrap import bootstrap_dev_data, create_all_tables
from govoplan_core.db.session import get_database
from govoplan_core.server.config import GovoplanServerConfig
from govoplan_core.server.runtime_agent import RuntimeNodeAgent
from govoplan_core.settings import Settings, settings


def _dev_bootstrap_needs_create_all(database_url: str) -> bool:
    try:
        return make_url(database_url).get_backend_name() == "sqlite"
    except Exception:
        return database_url.startswith("sqlite:")


@asynccontextmanager
async def lifespan(app: FastAPI):
    if settings.app_env.lower() == "dev" and settings.dev_auto_migrate_enabled:
        from govoplan_core.db.migrations import migrate_database

        migrate_database(database_url=settings.database_url)
    if settings.app_env.lower() == "dev" and settings.dev_bootstrap_enabled:
        if _dev_bootstrap_needs_create_all(settings.database_url):
            create_all_tables()
        with get_database().SessionLocal() as session:
            bootstrap_dev_data(
                session,
                api_key_secret=settings.dev_bootstrap_api_key,
                user_password=settings.dev_bootstrap_password,
            )
    lifecycle = getattr(app.state, "govoplan_lifecycle", None)
    if lifecycle is not None and hasattr(
        lifecycle,
        "reconcile_workflow_definitions",
    ):
        lifecycle.reconcile_workflow_definitions()
    registry = getattr(app.state, "govoplan_registry", None)
    module_ids = (
        tuple(manifest.id for manifest in registry.manifests())
        if registry is not None
        else ()
    )
    runtime_agent = RuntimeNodeAgent(
        settings=settings,
        software_version=app.version,
        module_ids=module_ids,
        metadata={"process": "api"},
    )
    await runtime_agent.start()
    app.state.govoplan_runtime_agent = runtime_agent
    try:
        yield
    finally:
        await runtime_agent.stop()


def _cors_origins(value: str) -> list[str]:
    return [item.strip() for item in value.split(",") if item.strip()]


def register_health_details(
    app: FastAPI, registry: PlatformRegistry, config_settings: object | None
) -> None:
    active_settings = (
        config_settings if isinstance(config_settings, Settings) else settings
    )

    @app.get("/health/details")
    def health_details(
        principal: ApiPrincipal = Depends(require_scope("system:settings:read")),
    ):
        del principal
        return {
            "status": "ok",
            "env": active_settings.app_env,
            "api": {"version": "v1", "auth": "api-key-or-session"},
            "modules": [manifest.id for manifest in registry.manifests()],
            "storage": {
                "backend": active_settings.file_storage_backend,
                "local_root": active_settings.file_storage_local_root
                if active_settings.file_storage_backend == "local"
                else None,
                "endpoint": active_settings.file_storage_s3_endpoint_url
                or active_settings.s3_endpoint_url,
                "bucket": active_settings.file_storage_s3_bucket
                or active_settings.s3_bucket,
                "region": active_settings.file_storage_s3_region
                or active_settings.s3_region,
            },
        }

    @app.get("/health/ready")
    def health_ready():
        runtime_agent = getattr(app.state, "govoplan_runtime_agent", None)
        if runtime_agent is not None and not runtime_agent.coordination_healthy:
            raise HTTPException(
                status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
                detail={
                    "status": "coordination_unavailable",
                    "node_id": runtime_agent.identity.node_id,
                },
            )
        if runtime_agent is not None and runtime_agent.draining:
            raise HTTPException(
                status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
                detail={
                    "status": "draining",
                    "node_id": runtime_agent.identity.node_id,
                },
            )
        return {"status": "ready"}


def get_server_config() -> GovoplanServerConfig:
    return GovoplanServerConfig(
        title="GovOPlaN Server",
        version="0.3.0",
        settings=settings,
        enabled_modules=settings.enabled_modules,
        api_prefix="/api/v1",
        lifespan=lifespan,
        cors_origins=_cors_origins(settings.cors_origins),
        app_configurators=(register_health_details,),
    )
