"""Organizations API views."""
from __future__ import annotations

from rest_framework import status
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated
from rest_framework.request import Request
from rest_framework.response import Response

from simorgh.apps.organizations import selectors as sel
from simorgh.apps.organizations.models import OrganizationNode, OrganizationNodeType
from simorgh.apps.organizations.services import HierarchyError, create_node


# ---------------------------------------------------------------------------
# Serializer helpers
# ---------------------------------------------------------------------------

def _serialize_node(node: OrganizationNode, *, children: list | None = None) -> dict:
    data: dict = {
        "id": node.pk,
        "public_id": str(node.public_id),
        "tenant_id": node.tenant_id,
        "parent_id": node.parent_id,
        "type": node.type,
        "name": node.name,
        "code": node.code,
        "path_string": node.path_string,
        "depth": node.depth,
        "is_active": node.is_active,
    }
    if children is not None:
        data["children"] = children
    return data


def _build_tree(nodes: list[OrganizationNode]) -> list[dict]:
    """Convert a flat queryset (ordered by path_string) into a nested tree."""
    by_pk: dict[int, dict] = {}
    roots: list[dict] = []
    for node in nodes:
        serialized = _serialize_node(node, children=[])
        by_pk[node.pk] = serialized
        if node.parent_id and node.parent_id in by_pk:
            by_pk[node.parent_id]["children"].append(serialized)
        else:
            roots.append(serialized)
    return roots


# ---------------------------------------------------------------------------
# Views
# ---------------------------------------------------------------------------

@api_view(["GET"])
@permission_classes([IsAuthenticated])
def org_tree(request: Request) -> Response:
    """GET /api/v1/organizations/tree/

    Returns the full organization tree for the current tenant as a nested
    JSON structure.  Active nodes only by default; pass ``?include_inactive=1``
    to include archived nodes.
    """
    tenant = getattr(request, "tenant", None)
    if tenant is None:
        return Response({"detail": "Tenant not found."}, status=status.HTTP_404_NOT_FOUND)

    include_inactive = request.query_params.get("include_inactive") == "1"
    nodes = list(sel.get_org_tree(tenant.pk, active_only=not include_inactive))
    return Response({"results": _build_tree(nodes)})


@api_view(["GET"])
@permission_classes([IsAuthenticated])
def org_node_detail(request: Request, pk: int) -> Response:
    """GET /api/v1/organizations/nodes/<pk>/"""
    tenant = getattr(request, "tenant", None)
    if tenant is None:
        return Response({"detail": "Tenant not found."}, status=status.HTTP_404_NOT_FOUND)
    try:
        node = sel.get_node_by_id(pk, tenant.pk)
    except OrganizationNode.DoesNotExist:
        return Response({"detail": "Not found."}, status=status.HTTP_404_NOT_FOUND)

    children = list(sel.get_children(node))
    return Response(_serialize_node(node, children=[_serialize_node(c) for c in children]))


@api_view(["GET", "POST"])
@permission_classes([IsAuthenticated])
def org_node_create(request: Request) -> Response:
    """GET: flat list of org nodes. POST: create a new node.

    GET query params: ?flat=1&include_inactive=1
    POST Body: { name, type?, code?, parent_id? }
    """
    tenant = getattr(request, "tenant", None)
    if tenant is None:
        return Response({"detail": "Tenant not found."}, status=status.HTTP_404_NOT_FOUND)

    if request.method == "GET":
        include_inactive = request.query_params.get("include_inactive") == "1"
        qs = OrganizationNode.objects.filter(tenant=tenant)
        if not include_inactive:
            qs = qs.filter(is_active=True)
        nodes = qs.order_by("path_string")
        return Response({"results": [_serialize_node(n) for n in nodes]})

    # POST
    if not request.user.is_staff:
        return Response({"detail": "Forbidden."}, status=status.HTTP_403_FORBIDDEN)

    tenant = getattr(request, "tenant", None)
    if tenant is None:
        return Response({"detail": "Tenant not found."}, status=status.HTTP_404_NOT_FOUND)

    name = str(request.data.get("name", "")).strip()
    if not name:
        return Response({"error": {"code": "missing_name", "message": "name is required."}},
                        status=status.HTTP_400_BAD_REQUEST)

    node_type = str(request.data.get("type", OrganizationNodeType.ORGANIZATION)).strip()
    code = str(request.data.get("code", "")).strip()
    parent_id = request.data.get("parent_id")

    parent: OrganizationNode | None = None
    if parent_id is not None:
        try:
            parent = sel.get_node_by_id(int(parent_id), tenant.pk)
        except (OrganizationNode.DoesNotExist, ValueError):
            return Response({"error": {"code": "invalid_parent", "message": "Parent node not found."}},
                            status=status.HTTP_400_BAD_REQUEST)

    try:
        node = create_node(tenant_id=tenant.pk, name=name, type=node_type, parent=parent, code=code)
    except HierarchyError as exc:
        return Response({"error": {"code": "hierarchy_error", "message": str(exc)}},
                        status=status.HTTP_400_BAD_REQUEST)

    return Response(_serialize_node(node), status=status.HTTP_201_CREATED)


@api_view(["PATCH"])
@permission_classes([IsAuthenticated])
def org_node_update(request: Request, pk: int) -> Response:
    """PATCH /api/v1/organizations/nodes/<pk>/

    Updatable fields: name, type, code, is_active.
    """
    if not request.user.is_staff:
        return Response({"detail": "Forbidden."}, status=status.HTTP_403_FORBIDDEN)

    tenant = getattr(request, "tenant", None)
    if tenant is None:
        return Response({"detail": "Tenant not found."}, status=status.HTTP_404_NOT_FOUND)

    try:
        node = sel.get_node_by_id(pk, tenant.pk)
    except OrganizationNode.DoesNotExist:
        return Response({"detail": "Not found."}, status=status.HTTP_404_NOT_FOUND)

    update_fields: list[str] = []
    allowed = {"name": str, "type": str, "code": str, "is_active": bool}
    for field, cast in allowed.items():
        if field in request.data:
            setattr(node, field, cast(request.data[field]))
            update_fields.append(field)

    if update_fields:
        node.save(update_fields=update_fields)

    return Response(_serialize_node(node))
