from __future__ import annotations

from fastapi import APIRouter, Depends, HTTPException, Request
from sqlalchemy.exc import SQLAlchemyError

from govoplan_core.admin.models import SystemSettings
from govoplan_core.admin.settings import SYSTEM_SETTINGS_ID
from govoplan_core.auth import ApiPrincipal, get_api_principal
from govoplan_core.core.maintenance import saved_maintenance_mode
from govoplan_core.core.modules import FrontendModule, FrontendRoute, ModuleManifest, NavItem, PublicFrontendRoute
from govoplan_core.core.registry import PlatformRegistry, manifest_view_surfaces
from govoplan_core.core.views import (
    VIEW_SURFACE_CONTRACT_VERSION,
    ViewSurface,
    navigation_view_surface_id,
    route_view_surface_id,
)
from govoplan_core.db.session import get_database
from govoplan_core.i18n import system_i18n_payload


def _registry(request: Request) -> PlatformRegistry:
    registry = getattr(request.app.state, "govoplan_registry", None)
    if not isinstance(registry, PlatformRegistry):
        raise HTTPException(status_code=500, detail="GovOPlaN module registry is not configured")
    return registry


def _nav_item_payload(item: NavItem, module_id: str | None = None) -> dict[str, object]:
    return {
        "path": item.path,
        "label": item.label,
        "icon": item.icon,
        "section": item.section,
        "required_all": list(item.required_all),
        "required_any": list(item.required_any),
        "order": item.order,
        "surface_id": (
            item.surface_id or navigation_view_surface_id(module_id, item.path)
            if module_id
            else item.surface_id
        ),
    }


def _frontend_route_payload(route: FrontendRoute, module_id: str | None = None) -> dict[str, object]:
    return {
        "path": route.path,
        "component": route.component,
        "required_all": list(route.required_all),
        "required_any": list(route.required_any),
        "order": route.order,
        "surface_id": (
            route.surface_id or route_view_surface_id(module_id, route.path)
            if module_id
            else route.surface_id
        ),
    }


def _public_frontend_route_payload(route: PublicFrontendRoute) -> dict[str, object]:
    return {
        "path": route.path,
        "component": route.component,
        "order": route.order,
    }


def _view_surface_payload(surface: ViewSurface) -> dict[str, object]:
    return {
        "id": surface.id,
        "module_id": surface.module_id,
        "kind": surface.kind,
        "label": surface.label,
        "parent_id": surface.parent_id,
        "description": surface.description,
        "order": surface.order,
        "default_visible": surface.default_visible,
        "required": surface.required,
    }


def _frontend_view_surfaces(manifest: ModuleManifest) -> list[dict[str, object]]:
    return [
        _view_surface_payload(surface)
        for surface in manifest_view_surfaces(manifest)
    ]


def _frontend_payload(manifest: ModuleManifest) -> dict[str, object] | None:
    frontend = manifest.frontend
    if frontend is None:
        return None
    return {
        "module_id": frontend.module_id,
        "package_name": frontend.package_name,
        "asset_manifest": frontend.asset_manifest,
        "asset_manifest_signature": frontend.asset_manifest_signature,
        "asset_manifest_public_key_id": frontend.asset_manifest_public_key_id,
        "asset_manifest_integrity": frontend.asset_manifest_integrity,
        "asset_manifest_contract_version": frontend.asset_manifest_contract_version,
        "routes": [_frontend_route_payload(route, manifest.id) for route in frontend.routes],
        "public_routes": [
            _public_frontend_route_payload(route) for route in frontend.public_routes
        ],
        "nav": [_nav_item_payload(item, manifest.id) for item in frontend.nav_items],
        "settings_routes": [_frontend_route_payload(route, manifest.id) for route in frontend.settings_routes],
        "view_surface_contract_version": VIEW_SURFACE_CONTRACT_VERSION,
        "view_surfaces": _frontend_view_surfaces(manifest),
    }


def _public_frontend_payload(frontend: FrontendModule) -> dict[str, object]:
    """Return only assets and routes explicitly approved for signed-out use."""

    return {
        "module_id": frontend.module_id,
        "package_name": frontend.package_name,
        "asset_manifest": frontend.asset_manifest,
        "asset_manifest_signature": frontend.asset_manifest_signature,
        "asset_manifest_public_key_id": frontend.asset_manifest_public_key_id,
        "asset_manifest_integrity": frontend.asset_manifest_integrity,
        "asset_manifest_contract_version": frontend.asset_manifest_contract_version,
        "public_routes": [
            _public_frontend_route_payload(route) for route in frontend.public_routes
        ],
    }


def _runtime_ui_capabilities(manifest_id: str, settings: object | None, registry: PlatformRegistry) -> list[str]:
    capabilities: list[str] = []
    if manifest_id == "mail":
        app_env = str(getattr(settings, "app_env", "dev")).lower()
        if app_env == "dev" and bool(getattr(settings, "dev_mailbox_api_enabled", False)) and registry.has_module("mail"):
            capabilities.append("mail.devMailbox")
    return capabilities


def create_platform_router(settings: object | None = None) -> APIRouter:
    router = APIRouter(prefix="/platform", tags=["platform"])

    @router.get("/status")
    def status():
        try:
            with get_database().session() as session:
                maintenance_mode = saved_maintenance_mode(session)
                settings_item = session.get(SystemSettings, SYSTEM_SETTINGS_ID)
        except (RuntimeError, SQLAlchemyError):
            maintenance_mode = None
            settings_item = None
        return {
            "maintenance_mode": maintenance_mode.as_dict() if maintenance_mode is not None else {"enabled": False, "message": None},
            "i18n": system_i18n_payload(settings_item),
        }

    @router.get("/modules")
    def modules(
        request: Request,
        _principal: ApiPrincipal = Depends(get_api_principal),
    ):
        registry = _registry(request)
        return {
            "modules": [
                {
                    "id": manifest.id,
                    "name": manifest.name,
                    "version": manifest.version,
                    "dependencies": list(manifest.dependencies),
                    "optional_dependencies": list(manifest.optional_dependencies),
                    "enabled": True,
                    "architecture": (
                        manifest.architecture.to_dict()
                        if manifest.architecture is not None
                        else None
                    ),
                    "external_providers": [
                        declaration.to_dict()
                        for declaration in manifest.external_providers
                    ],
                    "runtime_ui_capabilities": _runtime_ui_capabilities(manifest.id, settings, registry),
                    "nav": [_nav_item_payload(item, manifest.id) for item in manifest.nav_items],
                    "frontend": _frontend_payload(manifest),
                }
                for manifest in registry.manifests()
            ]
        }

    @router.get("/public-modules")
    def public_modules(request: Request):
        registry = _registry(request)
        return {
            "modules": [
                {
                    "id": manifest.id,
                    "name": manifest.name,
                    "version": manifest.version,
                    "frontend": _public_frontend_payload(manifest.frontend),
                }
                for manifest in registry.manifests()
                if manifest.frontend is not None
                and manifest.frontend.public_routes
            ]
        }

    @router.get("/permissions")
    def permissions(
        request: Request,
        _principal: ApiPrincipal = Depends(get_api_principal),
    ):
        registry = _registry(request)
        return {
            "permissions": [
                {
                    "scope": item.scope,
                    "module_id": item.module_id,
                    "resource": item.resource,
                    "action": item.action,
                    "label": item.label,
                    "description": item.description,
                    "category": item.category,
                    "level": item.level,
                    "deprecated": item.deprecated,
                }
                for item in registry.permissions()
            ]
        }

    @router.get("/navigation")
    def navigation(
        request: Request,
        _principal: ApiPrincipal = Depends(get_api_principal),
    ):
        registry = _registry(request)
        return {
            "items": [_nav_item_payload(item) for item in registry.nav_items()]
        }

    return router
