"""Schema-first view-model helpers.

The frontend renders portals/workspaces by consuming JSON shaped by
these helpers. Keeping the shapes here (instead of inline in DRF
serializers) means:

  * the same shape is used by HTML/CSR/SSR/MCP/etc;
  * the contract is testable in isolation;
  * a future Tauri/React-Native shell ingests the same JSON.

These functions are **pure** — they take resolved domain objects + a
RequestContext and return plain dicts. No DB queries beyond what's
already loaded.
"""

from __future__ import annotations

from typing import TYPE_CHECKING, Any

if TYPE_CHECKING:
    from simorgh.apps.workspaces.models import NavigationItem, Workspace
    from simorgh.core.context import RequestContext


# ---------------------------------------------------------------------------
# Actor / scope
# ---------------------------------------------------------------------------
def serialize_actor(ctx: RequestContext) -> dict[str, Any]:
    user = ctx.actor
    if user is None:
        return {"is_authenticated": False}
    return {
        "is_authenticated": True,
        "id": getattr(user, "public_id", None) and str(user.public_id),
        "username": getattr(user, "username", ""),
        "email": getattr(user, "email", ""),
        "full_name": getattr(user, "get_full_name", lambda: "")() or "",
        "is_superuser": bool(getattr(user, "is_superuser", False)),
    }


def serialize_scope(ctx: RequestContext) -> dict[str, Any]:
    tenant = ctx.tenant
    return {
        "tenant": {
            "slug": getattr(tenant, "slug", None),
            "name": getattr(tenant, "name", None),
        } if tenant is not None else None,
        "permissions": sorted(ctx.permissions),
        "organization_node_ids": sorted(ctx.org_node_ids),
    }


# ---------------------------------------------------------------------------
# Workspace
# ---------------------------------------------------------------------------
def serialize_workspace(ws: Workspace) -> dict[str, Any]:
    return {
        "id": str(ws.public_id),
        "slug": ws.slug,
        "name": ws.name,
        "kind": ws.kind,
        "icon": ws.icon,
        "theme": dict(ws.theme or {}),
        "default_module": ws.default_module,
        "is_active": ws.is_active,
    }


# ---------------------------------------------------------------------------
# Navigation tree
# ---------------------------------------------------------------------------
def _serialize_nav_item(item: NavigationItem, children: list[dict[str, Any]]) -> dict[str, Any]:
    return {
        "id": str(item.public_id),
        "label_key": item.label_key,
        "icon": item.icon,
        "route": item.route,
        "module": item.module,
        "features": list(item.features.values_list("code", flat=True)),
        "permission": item.permission,
        "order": item.order,
        "children": children,
    }


def build_navigation_tree(
    items: list[NavigationItem],
    *,
    visible_ids: set[int] | None = None,
) -> list[dict[str, Any]]:
    """Turn a flat list of NavigationItem rows into a nested tree.

    `visible_ids` (optional) restricts the output to the listed item ids
    and their visible ancestors; used by permission/feature filtering.
    """
    by_parent: dict[int | None, list[NavigationItem]] = {}
    for item in items:
        by_parent.setdefault(item.parent_id, []).append(item)

    for siblings in by_parent.values():
        siblings.sort(key=lambda i: (i.order, i.label_key))

    def walk(parent_id: int | None) -> list[dict[str, Any]]:
        out: list[dict[str, Any]] = []
        for item in by_parent.get(parent_id, ()):
            children = walk(item.id)
            if visible_ids is not None and item.id not in visible_ids and not children:
                continue
            out.append(_serialize_nav_item(item, children))
        return out

    return walk(None)


__all__ = (
    "build_navigation_tree",
    "serialize_actor",
    "serialize_scope",
    "serialize_workspace",
)
