"""Delegation Engine API views.

Endpoints
---------
GET    /delegations/                          list delegation rules
POST   /delegations/                          create delegation rule
GET    /delegations/my/                       my delegations
GET    /delegations/active/                   check active delegations
GET    /delegations/{public_id}/              retrieve delegation rule
PATCH  /delegations/{public_id}/              update delegation rule
DELETE /delegations/{public_id}/              delete delegation rule
POST   /delegations/{public_id}/activate/     activate delegation
POST   /delegations/{public_id}/deactivate/   deactivate delegation
GET    /delegations/{public_id}/logs/         delegation logs
"""

from __future__ import annotations

from django.shortcuts import get_object_or_404
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.delegations import selectors as sel
from simorgh.apps.delegations import services
from simorgh.apps.delegations.models import DelegationRule


def _get_tenant_id(request: Request) -> int | None:
    tenant = getattr(request, "tenant", None)
    return tenant.pk if tenant else None


def _get_user_id(request: Request) -> int | None:
    return request.user.pk if request.user and request.user.is_authenticated else None


def _serialize_rule(rule: DelegationRule) -> dict:
    return {
        "id":              rule.pk,
        "public_id":       str(rule.public_id),
        "delegator_id":    rule.delegator_id,
        "delegate_to_id":  rule.delegate_to_id,
        "delegate_role_id": rule.delegate_role_id,
        "start_date":      rule.start_date.isoformat() if rule.start_date else None,
        "end_date":        rule.end_date.isoformat() if rule.end_date else None,
        "is_active":       rule.is_active,
        "scope":           rule.scope,
        "reason":          rule.reason,
        "created_by_id":   rule.created_by_id,
        "created_at":      rule.created_at.isoformat(),
        "updated_at":      rule.updated_at.isoformat(),
    }


def _serialize_log(log: DelegationLog) -> dict:
    return {
        "id":           log.pk,
        "public_id":    str(log.public_id),
        "rule_id":      log.rule_id,
        "action":       log.action,
        "content_type_id": log.content_type_id,
        "object_id":    log.object_id,
        "metadata":     log.metadata,
        "created_at":   log.created_at.isoformat(),
    }


@api_view(["GET", "POST"])
@permission_classes([IsAuthenticated])
def delegation_list_create(request: Request) -> Response:
    tenant_id = _get_tenant_id(request)
    if tenant_id is None:
        return Response({"detail": "Tenant not found."}, status=status.HTTP_400_BAD_REQUEST)

    if request.method == "GET":
        delegator_filter = request.query_params.get("delegator_id")
        scope_filter = request.query_params.get("scope")
        active_only = request.query_params.get("active_only", "").lower() == "true"

        qs = sel.rules_for_tenant(tenant_id)
        if delegator_filter:
            qs = qs.filter(delegator_id=delegator_filter)
        if scope_filter:
            qs = qs.filter(scope=scope_filter)
        if active_only:
            qs = qs.filter(is_active=True)

        results = [_serialize_rule(r) for r in qs]
        return Response(results)

    if request.method == "POST":
        data = request.data
        delegator_id = data.get("delegator_id", _get_user_id(request))
        delegate_to_id = data.get("delegate_to_id")

        if not delegate_to_id:
            return Response(
                {"detail": "delegate_to_id is required."},
                status=status.HTTP_400_BAD_REQUEST,
            )

        try:
            from django.utils.dateparse import parse_datetime
            start_date = parse_datetime(data["start_date"]) if data.get("start_date") else None
            end_date = parse_datetime(data["end_date"]) if data.get("end_date") else None
        except (ValueError, TypeError):
            return Response(
                {"detail": "Invalid date format."},
                status=status.HTTP_400_BAD_REQUEST,
            )

        from simorgh.apps.organizations.models import OrganizationNode
        org_node = OrganizationNode.objects.filter(tenant_id=tenant_id).order_by("lft").first()
        if org_node is None:
            return Response(
                {"detail": "Organization not found for tenant."},
                status=status.HTTP_400_BAD_REQUEST,
            )

        rule = services.create_delegation_rule(
            tenant_id=tenant_id,
            organization_node_id=org_node.pk,
            delegator_id=delegator_id,
            delegate_to_id=delegate_to_id,
            start_date=start_date,
            end_date=end_date,
            delegate_role_id=data.get("delegate_role_id"),
            scope=data.get("scope", "all"),
            reason=data.get("reason", ""),
            created_by_id=_get_user_id(request),
        )
        return Response(_serialize_rule(rule), status=status.HTTP_201_CREATED)


@api_view(["GET"])
@permission_classes([IsAuthenticated])
def delegation_my(request: Request) -> Response:
    tenant_id = _get_tenant_id(request)
    user_id = _get_user_id(request)

    if tenant_id is None or user_id is None:
        return Response({"detail": "Authentication required."}, status=status.HTTP_400_BAD_REQUEST)

    given = [_serialize_rule(r) for r in sel.rules_for_delegator(tenant_id, user_id)]
    received = [_serialize_rule(r) for r in sel.rules_for_delegate(tenant_id, user_id)]

    return Response({"given": given, "received": received})


@api_view(["GET"])
@permission_classes([IsAuthenticated])
def delegation_active(request: Request) -> Response:
    tenant_id = _get_tenant_id(request)
    user_id = request.query_params.get("user_id")
    if user_id is None:
        user_id = _get_user_id(request)

    if tenant_id is None:
        return Response({"detail": "Tenant not found."}, status=status.HTTP_400_BAD_REQUEST)

    scope = request.query_params.get("scope")
    delegate = sel.active_delegate_for_user(tenant_id, user_id, scope=scope)

    if delegate is None:
        return Response({"active": False, "delegate": None})

    return Response({
        "active": True,
        "delegate": {
            "delegate_to_id": delegate.delegate_to_id,
            "delegate_role_id": delegate.delegate_role_id,
            "rule_public_id": str(delegate.public_id),
            "scope": delegate.scope,
            "end_date": delegate.end_date.isoformat() if delegate.end_date else None,
        },
    })


@api_view(["GET", "PATCH", "DELETE"])
@permission_classes([IsAuthenticated])
def delegation_detail(request: Request, public_id: str) -> Response:
    tenant_id = _get_tenant_id(request)
    if tenant_id is None:
        return Response({"detail": "Tenant not found."}, status=status.HTTP_400_BAD_REQUEST)

    rule = get_object_or_404(DelegationRule, tenant_id=tenant_id, public_id=public_id)

    if request.method == "GET":
        return Response(_serialize_rule(rule))

    if request.method == "PATCH":
        data = request.data
        delegate_to_id = data.get("delegate_to_id")
        delegate_role_id = data.get("delegate_role_id", ...)  # type: ignore[assignment]
        start_date = None
        end_date = None

        if "start_date" in data:
            from django.utils.dateparse import parse_datetime
            start_date = parse_datetime(data["start_date"])
        if "end_date" in data:
            from django.utils.dateparse import parse_datetime
            end_date = parse_datetime(data["end_date"])

        rule = services.update_delegation_rule(
            rule,
            delegate_to_id=delegate_to_id,
            delegate_role_id=delegate_role_id,
            start_date=start_date,
            end_date=end_date,
            scope=data.get("scope"),
            reason=data.get("reason"),
            is_active=data.get("is_active"),
        )
        return Response(_serialize_rule(rule))

    if request.method == "DELETE":
        services.delete_delegation_rule(rule)
        return Response(status=status.HTTP_204_NO_CONTENT)


@api_view(["POST"])
@permission_classes([IsAuthenticated])
def delegation_activate(request: Request, public_id: str) -> Response:
    tenant_id = _get_tenant_id(request)
    if tenant_id is None:
        return Response({"detail": "Tenant not found."}, status=status.HTTP_400_BAD_REQUEST)

    rule = get_object_or_404(DelegationRule, tenant_id=tenant_id, public_id=public_id)
    rule = services.activate_delegation_rule(rule)
    return Response(_serialize_rule(rule))


@api_view(["POST"])
@permission_classes([IsAuthenticated])
def delegation_deactivate(request: Request, public_id: str) -> Response:
    tenant_id = _get_tenant_id(request)
    if tenant_id is None:
        return Response({"detail": "Tenant not found."}, status=status.HTTP_400_BAD_REQUEST)

    rule = get_object_or_404(DelegationRule, tenant_id=tenant_id, public_id=public_id)
    rule = services.deactivate_delegation_rule(rule)
    return Response(_serialize_rule(rule))


@api_view(["GET"])
@permission_classes([IsAuthenticated])
def delegation_logs(request: Request, public_id: str) -> Response:
    tenant_id = _get_tenant_id(request)
    if tenant_id is None:
        return Response({"detail": "Tenant not found."}, status=status.HTTP_400_BAD_REQUEST)

    rule = get_object_or_404(DelegationRule, tenant_id=tenant_id, public_id=public_id)
    logs = sel.logs_for_rule(tenant_id, rule.pk)
    return Response([_serialize_log(log) for log in logs])
