"""Shipper/transporter billing & wallet HTTP handlers (plan §7, transporter earnings)."""
from decimal import Decimal

from django.contrib.auth import get_user_model
from django.db.models import Sum, Q
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated

from accounts.models import UserPushDevice
from billing.models import Invoice, LedgerEntry, Payment, WithdrawalRequest
from billing.services import (
    confirm_card_collection,
    confirm_cash_collection,
    create_withdrawal_request,
    get_or_create_wallet,
    process_shipper_trip_payment,
    user_can_confirm_trip_payment,
)
from core.models import Shipment, Trip, Notification
from .notification_filters import auth_profile_notifications_queryset
from .permissions import IsShipper, IsTransporter
from .notify import notify_users
from .response import api_response
from .serializers import (
    WalletSerializer,
    PaymentSerializer,
    InvoiceSerializer,
    LedgerEntrySerializer,
    WithdrawalRequestSerializer,
    WithdrawalCreateSerializer,
    TripPaySerializer,
    FcmTokenSerializer,
    ShipmentSerializer,
)

User = get_user_model()


@api_view(['GET'])
@permission_classes([IsAuthenticated, IsShipper])
def shipper_wallet(request):
    w = get_or_create_wallet(request.user)
    return api_response(200, 'Wallet retrieved successfully.', data=WalletSerializer(w).data)


@api_view(['POST'])
@permission_classes([IsAuthenticated, IsShipper])
def shipper_trip_pay(request, trip_pk):
    try:
        trip = Trip.objects.select_related('shipment', 'accepted_bid').get(pk=trip_pk, shipment__shipper=request.user)
    except Trip.DoesNotExist:
        return api_response(404, 'Trip not found.', error='Not found.')
    ser = TripPaySerializer(data=request.data)
    if not ser.is_valid():
        return api_response(400, 'Validation failed.', error=ser.errors)
    method = ser.validated_data['method']
    try:
        result = process_shipper_trip_payment(trip=trip, shipper=request.user, method=method, request=request)
    except ValueError as exc:
        return api_response(400, str(exc), error=str(exc))
    out = {'payment': PaymentSerializer(result['payment']).data, 'unique_id': trip.shipment.unique_id}
    if result.get('invoice'):
        out['invoice'] = InvoiceSerializer(result['invoice']).data
    p = result['payment']
    if p.status == Payment.Status.CAPTURED and p.method in (Payment.Method.WALLET, Payment.Method.CREDIT):
        notify_users(
            [p.payee_id],
            'PAYMENT_RECEIVED',
            {'trip_id': str(p.trip_id), 'payment_id': str(p.id), 'amount': str(p.amount)},
            title='Payment received',
            body='A shipper completed payment for a trip.',
        )
    if p.status == Payment.Status.PENDING_COD and p.method == Payment.Method.CASH:
        notify_users(
            [p.payee_id],
            'PAYMENT_PENDING_COD',
            {'trip_id': str(p.trip_id), 'payment_id': str(p.id), 'amount': str(p.amount)},
            title='Cash on delivery',
            body='Collect cash for this trip when delivered.',
        )
    if p.status == Payment.Status.REQUIRES_ACTION and p.method == Payment.Method.CARD:
        notify_users(
            [p.payee_id],
            'PAYMENT_PENDING_CARD',
            {'trip_id': str(p.trip_id), 'payment_id': str(p.id), 'amount': str(p.amount)},
            title='Card payment pending',
            body='Confirm card payment when collected for this trip.',
        )
    return api_response(200, 'Payment processed.', data=out)


def _trip_for_payment_confirm(request, trip_pk):
    try:
        trip = Trip.objects.select_related('shipment').get(pk=trip_pk)
    except Trip.DoesNotExist:
        return None, api_response(404, 'Trip not found.', error='Not found.')
    if not user_can_confirm_trip_payment(trip=trip, user=request.user):
        return None, api_response(404, 'Trip not found.', error='Not found.')
    return trip, None


@api_view(['GET'])
@permission_classes([IsAuthenticated, IsShipper])
def shipper_invoice_list(request):
    qs = (
        Invoice.objects.filter(shipper=request.user)
        .select_related('trip', 'trip__shipment', 'payment')
        .order_by('-issued_at')[:200]
    )
    return api_response(200, 'Invoices retrieved successfully.', data=InvoiceSerializer(qs, many=True).data)


@api_view(['GET'])
@permission_classes([IsAuthenticated, IsShipper])
def shipper_invoice_detail(request, pk):
    try:
        inv = (
            Invoice.objects.select_related('trip', 'trip__shipment', 'payment')
            .get(pk=pk, shipper=request.user)
        )
    except Invoice.DoesNotExist:
        return api_response(404, 'Invoice not found.', error='Not found.')
    return api_response(200, 'Invoice retrieved successfully.', data=InvoiceSerializer(inv).data)


@api_view(['GET'])
@permission_classes([IsAuthenticated, IsShipper])
def shipper_shipment_history(request):
    done = (
        Shipment.Status.DELIVERED,
        Shipment.Status.COMPLETED,
        Shipment.Status.CLOSED,
    )
    qs = (
        Shipment.objects.filter(shipper=request.user, status__in=done)
        .select_related('trip', 'trip__pod')
        .prefetch_related('trip__pod__photos')
        .order_by('-updated_at')[:500]
    )
    return api_response(200, 'Shipment history retrieved successfully.', data=ShipmentSerializer(qs, many=True, context={'request': request}).data)



@api_view(['GET'])
@permission_classes([IsAuthenticated, IsTransporter])
def transporter_wallet(request):
    w = get_or_create_wallet(request.user)
    return api_response(200, 'Wallet retrieved successfully.', data=WalletSerializer(w).data)


@api_view(['GET'])
@permission_classes([IsAuthenticated, IsTransporter])
def transporter_ledger(request):
    qs = LedgerEntry.objects.filter(user=request.user).order_by('-created_at')[:500]
    return api_response(200, 'Ledger retrieved successfully.', data=LedgerEntrySerializer(qs, many=True).data)


@api_view(['GET'])
@permission_classes([IsAuthenticated, IsTransporter])
def transporter_earnings(request):
    qs = (
        LedgerEntry.objects.filter(
            user=request.user,
            entry_type__in=(
                LedgerEntry.EntryType.WALLET_CREDIT_EARNING,
                LedgerEntry.EntryType.CREDIT_EARNING,
            ),
        )
        .order_by('-created_at')[:200]
    )
    total = LedgerEntry.objects.filter(
        user=request.user,
        entry_type__in=(
            LedgerEntry.EntryType.WALLET_CREDIT_EARNING,
            LedgerEntry.EntryType.CREDIT_EARNING,
        ),
    ).aggregate(s=Sum('amount'))['s'] or Decimal('0')
    return api_response(
        200,
        'Earnings retrieved successfully.',
        data={
            'total_credited': str(total),
            'entries': LedgerEntrySerializer(qs, many=True).data,
        },
    )


@api_view(['GET', 'POST'])
@permission_classes([IsAuthenticated, IsTransporter])
def transporter_withdrawals(request):
    if request.method == 'GET':
        qs = WithdrawalRequest.objects.filter(user=request.user).order_by('-created_at')[:100]
        return api_response(200, 'Withdrawals retrieved successfully.', data=WithdrawalRequestSerializer(qs, many=True).data)
    ser = WithdrawalCreateSerializer(data=request.data)
    if not ser.is_valid():
        return api_response(400, 'Validation failed.', error=ser.errors)
    try:
        w = create_withdrawal_request(
            user=request.user,
            amount=ser.validated_data['amount'],
            note=ser.validated_data.get('note') or '',
        )
    except ValueError as exc:
        return api_response(400, str(exc), error=str(exc))
    return api_response(201, 'Withdrawal request created.', data=WithdrawalRequestSerializer(w).data)


@api_view(['POST'])
@permission_classes([IsAuthenticated, IsTransporter])
def transporter_confirm_cash(request, trip_pk):
    trip, err = _trip_for_payment_confirm(request, trip_pk)
    if err:
        return err
    try:
        confirm_cash_collection(trip=trip, transporter_user=request.user)
    except ValueError as exc:
        return api_response(400, str(exc), error=str(exc))
    notify_users(
        [trip.shipment.shipper_id],
        'PAYMENT_CAPTURED',
        {'trip_id': str(trip.id), 'shipment_id': str(trip.shipment_id)},
        title='Payment confirmed',
        body='Cash for your shipment was collected.',
    )
    return api_response(200, 'Cash collection confirmed.', data={})


@api_view(['POST'])
@permission_classes([IsAuthenticated, IsTransporter])
def transporter_confirm_card(request, trip_pk):
    trip, err = _trip_for_payment_confirm(request, trip_pk)
    if err:
        return err
    try:
        confirm_card_collection(trip=trip, transporter_user=request.user)
    except ValueError as exc:
        return api_response(400, str(exc), error=str(exc))
    notify_users(
        [trip.shipment.shipper_id],
        'PAYMENT_CAPTURED',
        {'trip_id': str(trip.id), 'shipment_id': str(trip.shipment_id)},
        title='Payment confirmed',
        body='Card payment for your shipment was confirmed.',
    )
    return api_response(200, 'Card payment confirmed.', data={})


@api_view(['PATCH'])
@permission_classes([IsAuthenticated])
def auth_fcm_token(request):
    ser = FcmTokenSerializer(data=request.data)
    if not ser.is_valid():
        return api_response(400, 'Validation failed.', error=ser.errors)
    from django.utils import timezone
    dev, _ = UserPushDevice.objects.get_or_create(user=request.user)
    dev.fcm_token = ser.validated_data['fcm_token'].strip()
    dev.fcm_token_updated_at = timezone.now()
    dev.save(update_fields=['fcm_token', 'fcm_token_updated_at'])
    return api_response(200, 'FCM token saved successfully.', data={'fcm_token_updated': True})


@api_view(['GET'])
@permission_classes([IsAuthenticated])
def auth_notifications(request):
    user_id_param = request.query_params.get('user_id')
    offset_param = request.query_params.get('offset')

    if user_id_param in (None, ''):
        return api_response(400, 'user_id is required.', error='Missing user_id.')

    try:
        offset = int(offset_param) if offset_param not in (None, '') else 0
    except (TypeError, ValueError):
        return api_response(400, 'offset must be a valid integer.', error='Invalid offset.')
    if offset < 0:
        return api_response(400, 'offset must be zero or greater.', error='Invalid offset.')

    try:
        requested_user_id = int(user_id_param)
    except (TypeError, ValueError):
        return api_response(400, 'user_id must be a valid integer.', error='Invalid user_id.')
    # Only admins can query notifications for other users.
    if requested_user_id != request.user.id and getattr(request.user.role, 'role', None) != 'ADMIN':
        return api_response(403, 'You can only access your own notifications.', error='Forbidden.')
    target_user_id = requested_user_id

    limit = 20
    qs = auth_profile_notifications_queryset(target_user_id)
    total = qs.count()
    items = qs[offset:offset + limit]
    data = [
        {
            'id': n.id,
            'title': n.title,
            'message': n.message,
            'type': n.type,
            'user_id': n.user_id,
            'data': n.data,
            'seen': n.seen,
            'created_at': n.created_at,
            'updated_at': n.updated_at,
        }
        for n in items
    ]
    return api_response(
        200,
        'Notifications retrieved successfully.',
        data={
            'user_id': target_user_id,
            'offset': offset,
            'limit': limit,
            'total': total,
            'count': len(data),
            'results': data,
        },
    )


@api_view(['GET'])
@permission_classes([IsAuthenticated, IsTransporter])
def transporter_earnings_by_trip(request):
    rows = (
        LedgerEntry.objects.filter(
            user=request.user,
            entry_type__in=(
                LedgerEntry.EntryType.WALLET_CREDIT_EARNING,
                LedgerEntry.EntryType.CREDIT_EARNING,
            ),
            trip_id__isnull=False,
        )
        .values('trip_id')
        .annotate(total=Sum('amount'))
        .order_by('-trip_id')[:300]
    )
    data = [{'trip_id': r['trip_id'], 'total_credited': str(r['total'])} for r in rows]
    return api_response(200, 'Trip earnings retrieved successfully.', data={'by_trip': data})


@api_view(['GET'])
@permission_classes([IsAuthenticated, IsTransporter])
def transporter_trip_payment_summary(request, trip_pk):
    try:
        trip = Trip.objects.select_related('shipment').get(pk=trip_pk, transporter=request.user)
    except Trip.DoesNotExist:
        return api_response(404, 'Trip not found.', error='Not found.')
    payments = (
        Payment.objects.filter(trip=trip)
        .filter(Q(payee=request.user) | Q(payer=request.user))
        .order_by('-id')[:50]
    )
    invs = Invoice.objects.filter(trip=trip, shipper=trip.shipment.shipper).order_by('-issued_at')[:20]
    return api_response(
        200,
        'Payment summary retrieved successfully.',
        data={
            'trip_id': trip.id,
            'unique_id': trip.shipment.unique_id,
            'payments': PaymentSerializer(payments, many=True).data,
            'invoices': InvoiceSerializer(invs, many=True).data,
        },
    )
