"""Customer-facing (JWT-authenticated) endpoints for the mobile app:
notifications feed and a funds/activity summary derived from deals + ledger.
"""

from decimal import Decimal

from django.db.models import Q, Sum
from django.utils import timezone
from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response

from escrow.models import Deal, Transaction

from .agreements import active_agreement
from .models import DeviceToken, Notification
from .views import _get_authenticated_user


@api_view(["GET"])
def get_active_agreement(request):
    """Current Terms & Agreement — shown to both buyer and seller before they
    create/join a deal (see escrow.views' accept_terms requirement). Public:
    no auth required, since terms should be readable before committing to
    anything."""
    agreement = active_agreement()
    if agreement is None:
        return Response({"detail": "No agreement has been published yet."}, status=status.HTTP_404_NOT_FOUND)
    return Response({
        "version": agreement.version,
        "title": agreement.title,
        "body": agreement.body,
        "effective_from": agreement.effective_from,
    })


@api_view(["GET"])
def get_kyc_gating(request):
    """Current seller-KYC gate (enabled + required level) — lets the app show
    a "complete KYC first" nudge before the seller even opens the create-deal
    form, instead of only finding out from the 403 on submit. Read-only,
    no secrets, safe for any authenticated user."""
    user, auth_error = _get_authenticated_user(request)
    if auth_error:
        return Response({"detail": auth_error}, status=status.HTTP_401_UNAUTHORIZED)

    from escrow.views import _seller_kyc_gating_settings

    enabled, min_level = _seller_kyc_gating_settings()
    return Response({"enabled": enabled, "min_level": min_level})


_VALID_PLATFORMS = {c[0] for c in DeviceToken.Platform.choices}


@api_view(["POST"])
def register_device(request):
    """Registers (or re-activates) an FCM device token for push notifications
    — see core.push / core.notify.push_notification. A token can only belong
    to one user at a time (the same physical device may log into a different
    account later), so registering it here deactivates it under any other
    user first."""
    user, err = _get_authenticated_user(request)
    if err:
        return Response({"detail": err}, status=status.HTTP_401_UNAUTHORIZED)

    token = str(request.data.get("token") or "").strip()
    platform = str(request.data.get("platform") or "").strip().lower()
    if not token:
        return Response({"detail": "token is required."}, status=status.HTTP_400_BAD_REQUEST)
    if platform and platform not in _VALID_PLATFORMS:
        return Response({"detail": f"platform must be one of {sorted(_VALID_PLATFORMS)}."}, status=status.HTTP_400_BAD_REQUEST)

    DeviceToken.objects.filter(token=token).exclude(user=user).update(is_active=False)
    DeviceToken.objects.update_or_create(
        token=token,
        defaults={
            "user": user,
            "platform": platform or DeviceToken.Platform.UNKNOWN,
            "is_active": True,
            "last_seen_at": timezone.now(),
        },
    )
    return Response({"detail": "Device registered."}, status=status.HTTP_200_OK)


@api_view(["POST"])
def unregister_device(request):
    """Called on logout so a shared/reset device stops receiving push for
    the account that just signed out."""
    user, err = _get_authenticated_user(request)
    if err:
        return Response({"detail": err}, status=status.HTTP_401_UNAUTHORIZED)

    token = str(request.data.get("token") or "").strip()
    if not token:
        return Response({"detail": "token is required."}, status=status.HTTP_400_BAD_REQUEST)

    DeviceToken.objects.filter(user=user, token=token).update(is_active=False)
    return Response({"detail": "Device unregistered."}, status=status.HTTP_200_OK)


def _serialize_notification(n: Notification):
    return {
        "id": str(n.id),
        "title": n.title,
        "body": n.body,
        "channel": n.channel,
        "is_read": n.is_read,
        "deal_id": str(n.deal_id) if n.deal_id else None,
        "created_at": n.created_at,
    }


@api_view(["GET"])
def list_notifications(request):
    user, err = _get_authenticated_user(request)
    if err:
        return Response({"detail": err}, status=status.HTTP_401_UNAUTHORIZED)
    qs = Notification.objects.filter(user=user).order_by("-created_at")[:100]
    return Response({
        "unread": Notification.objects.filter(user=user, is_read=False).count(),
        "results": [_serialize_notification(n) for n in qs],
    })


@api_view(["POST"])
def mark_notification_read(request, notification_id):
    user, err = _get_authenticated_user(request)
    if err:
        return Response({"detail": err}, status=status.HTTP_401_UNAUTHORIZED)
    n = Notification.objects.filter(id=notification_id, user=user).first()
    if not n:
        return Response({"detail": "Notification not found."}, status=status.HTTP_404_NOT_FOUND)
    if not n.is_read:
        n.is_read = True
        n.sent_at = n.sent_at or timezone.now()
        n.save(update_fields=["is_read", "sent_at"])
    return Response(_serialize_notification(n))


@api_view(["POST"])
def mark_all_notifications_read(request):
    user, err = _get_authenticated_user(request)
    if err:
        return Response({"detail": err}, status=status.HTTP_401_UNAUTHORIZED)
    Notification.objects.filter(user=user, is_read=False).update(is_read=True, sent_at=timezone.now())
    return Response({"detail": "All marked read.", "unread": 0})


@api_view(["GET"])
def funds_summary(request):
    """Protected funds, pending payouts/refunds and recent transactions for the
    signed-in user — a derived 'Funds / Activity' view (never a stored wallet)."""
    user, err = _get_authenticated_user(request)
    if err:
        return Response({"detail": err}, status=status.HTTP_401_UNAUTHORIZED)

    held_states = [Deal.Status.FUNDED, Deal.Status.IN_TRANSIT, Deal.Status.CONFIRMING, Deal.Status.DISPUTED]

    # As a buyer: money currently protected in escrow.
    protected = Deal.objects.filter(buyer=user, status__in=held_states).aggregate(
        t=Sum("amount"))["t"] or Decimal("0")
    # As a seller: money awaiting release for deals in flight.
    pending_payout = Deal.objects.filter(seller=user, status__in=held_states).aggregate(
        t=Sum("net_amount"))["t"] or Decimal("0")
    # Lifetime received (completed as seller) and refunded (as buyer).
    received = Deal.objects.filter(seller=user, status=Deal.Status.COMPLETED).aggregate(
        t=Sum("net_amount"))["t"] or Decimal("0")
    refunded = Deal.objects.filter(buyer=user, status=Deal.Status.REFUNDED).aggregate(
        t=Sum("amount"))["t"] or Decimal("0")

    txns = (
        Transaction.objects.filter(Q(user=user) | Q(deal__seller=user) | Q(deal__buyer=user))
        .select_related("deal")
        .distinct()
        .order_by("-created_at")[:50]
    )
    tx_list = [
        {
            "id": str(t.id),
            "deal_code": t.deal.code if t.deal_id else None,
            "amount": str(t.amount),
            "currency": t.currency,
            "event_type": t.event_type,
            "reference": t.reference,
            "created_at": t.created_at,
        }
        for t in txns
    ]

    return Response({
        "currency": "TZS",
        "protected_amount": str(protected),
        "pending_payout": str(pending_payout),
        "total_received": str(received),
        "total_refunded": str(refunded),
        "active_deals": Deal.objects.filter(Q(buyer=user) | Q(seller=user), status__in=held_states).count(),
        "completed_deals": Deal.objects.filter(Q(buyer=user) | Q(seller=user), status=Deal.Status.COMPLETED).count(),
        "transactions": tx_list,
    })
