"""Read-only query selectors for the organizations app."""

from __future__ import annotations

from django.db.models import QuerySet

from simorgh.apps.organizations.models import OrganizationNode

__all__ = [
    "get_node_by_id",
    "get_org_tree",
    "get_root_nodes",
    "get_children",
    "get_ancestors",
]


def get_node_by_id(node_id: int, tenant_id: int) -> OrganizationNode:
    """Return the OrganizationNode with the given PK, scoped to the tenant.

    Raises ``OrganizationNode.DoesNotExist`` when not found.
    """
    return OrganizationNode.objects.get(pk=node_id, tenant_id=tenant_id)


def get_org_tree(tenant_id: int, *, active_only: bool = True) -> QuerySet:
    """Return all OrganizationNodes for a tenant, ordered by materialized path.

    This gives a depth-first traversal order suitable for rendering a tree.
    Filter ``active_only=False`` to include inactive/archived nodes.
    """
    qs = OrganizationNode.objects.filter(tenant_id=tenant_id)
    if active_only:
        qs = qs.filter(is_active=True)
    return qs.order_by("path_string")


def get_root_nodes(tenant_id: int) -> QuerySet:
    """Return the top-level (depth=0) OrganizationNodes for a tenant."""
    return OrganizationNode.objects.filter(
        tenant_id=tenant_id, parent__isnull=True, is_active=True
    ).order_by("path_string")


def get_children(node: OrganizationNode, *, active_only: bool = True) -> QuerySet:
    """Return the direct children of an OrganizationNode."""
    qs = OrganizationNode.objects.filter(tenant_id=node.tenant_id, parent=node)
    if active_only:
        qs = qs.filter(is_active=True)
    return qs.order_by("path_string")


def get_ancestors(node: OrganizationNode) -> QuerySet:
    """Return all ancestor nodes of the given node, from root to direct parent.

    Uses the materialized ``path_string`` (e.g. ``/1/4/12/``) to derive the
    ancestor PKs without recursive queries.
    """
    if not node.path_string:
        return OrganizationNode.objects.none()

    # path_string format: "/pk1/pk2/.../self_pk/" — split and drop empty strings.
    parts = [p for p in node.path_string.strip("/").split("/") if p]
    # Last element is the node itself; ancestors are everything before it.
    ancestor_pks = [int(p) for p in parts[:-1]]
    if not ancestor_pks:
        return OrganizationNode.objects.none()

    # Preserve materialized path order (root first).
    from django.db.models import Case, IntegerField, Value, When

    ordering = Case(
        *[When(pk=pk, then=Value(i)) for i, pk in enumerate(ancestor_pks)],
        output_field=IntegerField(),
    )
    return OrganizationNode.objects.filter(pk__in=ancestor_pks).order_by(ordering)
