from __future__ import annotations

from fastapi import APIRouter

from govoplan_core.core.lifecycle import ModuleLifecycleManager
from govoplan_core.core.module_management import load_startup_enabled_modules, startup_candidate_module_ids
from govoplan_core.db.session import configure_database
from govoplan_core.server.config import GovoplanServerConfig, load_server_config
from govoplan_core.server.fastapi import create_govoplan_app
from govoplan_core.server.platform import create_platform_router
from govoplan_core.server.credentials import router as credential_router
from govoplan_core.server.ownership import router as ownership_router
from govoplan_core.server.registry import available_module_manifests, build_platform_registry
from govoplan_core.server.route_validation import validate_no_route_collisions


def create_app(config: GovoplanServerConfig | str | None = None):
    server_config = _server_config(config)
    _configure_app_database(server_config)
    registry, available_modules = _server_module_registry(server_config)
    api_router = _server_api_router(server_config, registry)
    lifecycle = _server_lifecycle(server_config, registry=registry, available_modules=available_modules)
    lifecycle.configure_runtime()

    app = create_govoplan_app(
        title=server_config.title,
        version=server_config.version,
        registry=registry,
        api_router=api_router,
        lifespan=server_config.lifespan,
        cors_origins=server_config.cors_origins,
    )
    lifecycle.attach_app(app)
    lifecycle.apply_enabled_modules(tuple(manifest.id for manifest in registry.manifests()), migrate=False)
    for configurator in server_config.app_configurators:
        configurator(app, registry, server_config.settings)
    return app


def _server_config(config: GovoplanServerConfig | str | None) -> GovoplanServerConfig:
    if isinstance(config, str) or config is None:
        return load_server_config(config)
    return config


def _configure_app_database(server_config: GovoplanServerConfig) -> None:
    database_url = getattr(server_config.settings, "database_url", None) if server_config.settings is not None else None
    if database_url:
        dispose_previous = getattr(server_config.settings, "app_env", None) == "test"
        configure_database(str(database_url), dispose_previous=dispose_previous)


def _server_module_registry(server_config: GovoplanServerConfig):
    raw_enabled_modules = load_startup_enabled_modules(server_config.enabled_modules)
    candidate_modules = startup_candidate_module_ids(server_config.enabled_modules, raw_enabled_modules)
    startup_available_modules = available_module_manifests(
        server_config.manifest_factories,
        enabled_modules=candidate_modules,
        ignore_load_errors=True,
    )
    available_modules = available_module_manifests(server_config.manifest_factories, ignore_load_errors=True)
    enabled_modules = load_startup_enabled_modules(server_config.enabled_modules, available=startup_available_modules)
    registry = build_platform_registry(enabled_modules, manifest_factories=server_config.manifest_factories)
    return registry, available_modules


def _server_api_router(server_config: GovoplanServerConfig, registry) -> APIRouter:
    api_router = APIRouter(prefix=server_config.api_prefix)
    for router in server_config.base_routers:
        api_router.include_router(router)
    api_router.include_router(create_platform_router(settings=server_config.settings))
    api_router.include_router(credential_router)
    api_router.include_router(ownership_router)
    for router in server_config.post_module_routers:
        api_router.include_router(router)
    for contribution in server_config.extra_routers:
        if contribution.should_include(server_config.settings, registry):
            api_router.include_router(contribution.router)
    validate_no_route_collisions(api_router, owner="server startup routes")
    return api_router


def _server_lifecycle(
    server_config: GovoplanServerConfig,
    *,
    registry,
    available_modules,
) -> ModuleLifecycleManager:
    return ModuleLifecycleManager(
        registry=registry,
        available_modules=available_modules,
        settings=server_config.settings,
        api_prefix=server_config.api_prefix,
        manifest_factories=tuple(server_config.manifest_factories),
        module_context_data=server_config.module_context_data,
    )


app = create_app()
