"""Admin portal endpoints. Mirrors testing_app.views admin rules exactly:
reason codes required for suspend/deactivate, decline needs a note,
operator suspend cascades to future trips, manual refunds are audited."""
from django.db.models import Count, Q, Sum
from django.utils import timezone
from rest_framework.decorators import api_view

from testing_app.models import (AdminAudit, Booking, Bus, City, LoginLog,
                                Operator, Route, Schedule, User)
from testing_app.views import COMMISSION_RATE, REASON_CODES, _refund_booking

from .auth import api_audit, body, err, jwt_required, ok
from .serializers import (audit_dict, booking_dict, bus_dict, city_dict,
                          login_log_dict, operator_dict, user_dict)


@api_view(['GET'])
@jwt_required('ADMIN')
def summary(request):
    now = timezone.now()
    paid = Booking.objects.exclude(booking_status__in=['CANCELLED', 'REFUNDED', 'PENDING_PAYMENT', 'EXPIRED'])
    money = paid.aggregate(commission=Sum('commission_amount'), gross=Sum('total_amount'))
    recent = paid.select_related(
        'user', 'schedule__bus__operator', 'schedule__route__source_city',
        'schedule__route__destination_city').order_by('-booked_at')[:8]
    return ok({
        'total_users': User.objects.count(),
        'total_passengers': User.objects.filter(role__name='PASSENGER').count(),
        'total_operators': Operator.objects.count(),
        'verified_operators': Operator.objects.filter(verified=True).count(),
        'pending_operators': Operator.objects.filter(verified=False).count(),
        'total_buses': Bus.objects.count(),
        'pending_buses': Bus.objects.filter(approval_status='PENDING').count(),
        'approved_buses': Bus.objects.filter(approval_status='APPROVED').count(),
        'total_bookings': paid.count(),
        'platform_revenue': float(money['commission'] or 0),
        'gross_sales': float(money['gross'] or 0),
        'active_trips': Schedule.objects.filter(status='ACTIVE', departure_time__gte=now).count(),
        'recent_bookings': [booking_dict(b) for b in recent],
    })


@api_view(['GET'])
@jwt_required('ADMIN')
def reason_codes(request):
    return ok(REASON_CODES)


# ---------------------------------------------------------------------------
# Operators (access / suspend with cascade)
# ---------------------------------------------------------------------------

@api_view(['GET'])
@jwt_required('ADMIN')
def operators(request):
    rows = Operator.objects.select_related('user').order_by('-id')
    return ok([operator_dict(o) for o in rows])


@api_view(['POST'])
@jwt_required('ADMIN')
def operator_action(request, operator_id):
    op = Operator.objects.select_related('user').filter(id=operator_id).first()
    if op is None:
        return err('Operator not found.', status=404)
    data = body(request)
    action = data.get('action')
    reason = str(data.get('reason_code', '') or '').strip()

    if action == 'verify':
        op.verified = True
        op.save(update_fields=['verified'])
        api_audit(request, 'OPERATOR_APPROVE', op.company_name)
        return ok(operator_dict(op), message=f'Access granted to {op.company_name}.')

    if action == 'revoke':
        op.verified = False
        op.save(update_fields=['verified'])
        api_audit(request, 'OPERATOR_REVOKE', op.company_name)
        return ok(operator_dict(op), message=f'Access revoked from {op.company_name}.')

    if action == 'suspend':
        if not reason:
            return err('A reason code is required to suspend an operator.')
        op.verified = False
        op.save(update_fields=['verified'])
        hidden = Schedule.objects.filter(
            bus__operator=op, status='ACTIVE', departure_time__gte=timezone.now()
        ).update(status='CANCELLED')
        api_audit(request, 'OPERATOR_SUSPEND', op.company_name, reason_code=reason,
                  note=f'{hidden} future trip(s) hidden from search')
        return ok(operator_dict(op),
                  message=f'{op.company_name} suspended. {hidden} future trip(s) hidden. History retained.')

    return err('Unknown action.')


# ---------------------------------------------------------------------------
# Bus approvals
# ---------------------------------------------------------------------------

@api_view(['GET'])
@jwt_required('ADMIN')
def buses(request):
    status = request.GET.get('status', '')
    rows = Bus.objects.select_related('operator').order_by('-id')
    if status:
        rows = rows.filter(approval_status=status)
    return ok([bus_dict(b) for b in rows[:300]])


@api_view(['POST'])
@jwt_required('ADMIN')
def bus_review(request, bus_id):
    b = Bus.objects.select_related('operator').filter(id=bus_id).first()
    if b is None:
        return err('Bus not found.', status=404)
    data = body(request)
    action = data.get('action')
    note = str(data.get('note', '') or '').strip()

    if action == 'approve':
        b.approval_status = 'APPROVED'
        b.review_note = note or 'Approved by admin.'
    elif action == 'reject':
        if not note:
            return err('Please write a reason before declining a bus.')
        b.approval_status = 'REJECTED'
        b.review_note = note
    else:
        return err('Unknown action.')

    b.reviewed_at = timezone.now()
    b.reviewed_by = request.api_user
    b.save()
    api_audit(request, f'BUS_{action.upper()}', f'{b.bus_name} ({b.registration_no})', note=b.review_note)
    verb = 'approved - it is now bookable' if action == 'approve' else 'declined. The operator will see your reason'
    return ok(bus_dict(b), message=f'{b.bus_name} {verb}.')


# ---------------------------------------------------------------------------
# User management (passengers)
# ---------------------------------------------------------------------------

@api_view(['GET'])
@jwt_required('ADMIN')
def users(request):
    q = request.GET.get('q', '').strip()
    rows = User.objects.select_related('role').filter(role__name='PASSENGER')
    if q:
        rows = rows.filter(Q(full_name__icontains=q) | Q(email__icontains=q))
    return ok([user_dict(u) for u in rows.order_by('-id')[:100]])


@api_view(['POST'])
@jwt_required('ADMIN')
def user_action(request, user_id):
    target = User.objects.select_related('role').filter(id=user_id).first()
    if target is None:
        return err('User not found.', status=404)
    if target.role.name == 'ADMIN':
        return err('Admin accounts cannot be changed here.', status=403)
    data = body(request)
    action = data.get('action')
    reason = str(data.get('reason_code', '') or '').strip()

    if action in ('DEACTIVATE', 'SUSPEND') and not reason:
        return err('A reason code is required to deactivate or suspend a user.')

    if action == 'ACTIVATE':
        target.status = 'ACTIVE'
        api_audit(request, 'USER_ACTIVATE', target.email, note='Account re-activated')
        msg = f'{target.full_name} is now ACTIVE.'
    elif action == 'DEACTIVATE':
        target.status = 'INACTIVE'
        api_audit(request, 'USER_DEACTIVATE', target.email, reason_code=reason)
        msg = f'{target.full_name} set to INACTIVE.'
    elif action == 'SUSPEND':
        target.status = 'SUSPENDED'
        api_audit(request, 'USER_SUSPEND', target.email, reason_code=reason)
        msg = f'{target.full_name} SUSPENDED.'
    else:
        return err('Unknown action.')

    target.save(update_fields=['status'])
    return ok(user_dict(target), message=msg)


# ---------------------------------------------------------------------------
# Cities
# ---------------------------------------------------------------------------

@api_view(['GET', 'POST'])
@jwt_required('ADMIN')
def cities(request):
    if request.method == 'GET':
        rows = City.objects.order_by('province', 'city_name')
        return ok([city_dict(c) for c in rows])
    if request.method == 'POST':
        data = body(request)
        name = str(data.get('city_name', '')).strip()
        province = str(data.get('province', '') or 'OTHER').strip().upper()
        if not name:
            return err('Enter a city name.')
        if province not in dict(City.PROVINCE_CHOICES):
            return err('Pick a valid province.')
        if City.objects.filter(city_name__iexact=name).exists():
            return err('That city already exists.')
        c = City.objects.create(city_name=name, province=province)
        api_audit(request, 'CITY_ADD', f'{name} ({c.get_province_display()})')
        return ok(city_dict(c), message=f'City "{name}" added.')
    return err('Method not allowed.', status=405)


@api_view(['GET'])
@jwt_required('ADMIN')
def province_choices(request):
    return ok([{'value': v, 'label': l} for v, l in City.PROVINCE_CHOICES])


@api_view(['DELETE'])
@jwt_required('ADMIN')
def city_delete(request, city_id):
    c = City.objects.filter(id=city_id).first()
    if c is None:
        return err('City not found.', status=404)
    used = Route.objects.filter(Q(source_city=c) | Q(destination_city=c)).exists()
    if used:
        return err(f'Cannot delete "{c.city_name}" - it is used by a route.')
    name = c.city_name
    c.delete()
    api_audit(request, 'CITY_DELETE', name)
    return ok(message=f'City "{name}" deleted.')


# ---------------------------------------------------------------------------
# Booking oversight + manual refund
# ---------------------------------------------------------------------------

@api_view(['GET'])
@jwt_required('ADMIN')
def bookings(request):
    q = request.GET.get('q', '').strip()
    rows = Booking.objects.select_related(
        'user', 'schedule__bus__operator', 'schedule__route__source_city',
        'schedule__route__destination_city')
    if q:
        rows = rows.filter(Q(booking_code__icontains=q) | Q(user__full_name__icontains=q))
    return ok([booking_dict(b) for b in rows.order_by('-booked_at')[:100]])


@api_view(['POST'])
@jwt_required('ADMIN')
def booking_refund(request, booking_id):
    b = Booking.objects.select_related('user').filter(id=booking_id).first()
    if b is None:
        return err('Booking not found.', status=404)
    if b.booking_status in ['CANCELLED', 'REFUNDED']:
        return err('This booking is already cancelled/refunded.')
    note = str(body(request).get('note', '') or '').strip()
    if not note:
        return err('A justification note is required for a manual refund.')
    _refund_booking(b, f'Admin manual refund: {note}', cancelled_by='ADMIN')
    api_audit(request, 'MANUAL_REFUND', b.booking_code, note=note)
    return ok(message=f'Booking {b.booking_code} refunded.')


# ---------------------------------------------------------------------------
# Commission / logs / audit
# ---------------------------------------------------------------------------

@api_view(['GET'])
@jwt_required('ADMIN')
def commission(request):
    qs = Booking.objects.exclude(booking_status__in=['CANCELLED', 'REFUNDED', 'PENDING_PAYMENT', 'EXPIRED'])
    totals = qs.aggregate(tickets=Count('id'), seats=Sum('seats_count'),
                          sales=Sum('total_amount'), commission=Sum('commission_amount'),
                          operator=Sum('operator_earning'))
    per_operator = list(qs.values('schedule__bus__operator__company_name').annotate(
        tickets=Count('id'), sales=Sum('total_amount'), commission=Sum('commission_amount')
    ).order_by('-commission'))
    for row in per_operator:
        row['operator'] = row.pop('schedule__bus__operator__company_name')
        row['sales'] = float(row['sales'] or 0)
        row['commission'] = float(row['commission'] or 0)
    recent = qs.select_related(
        'user', 'schedule__bus__operator', 'schedule__route__source_city',
        'schedule__route__destination_city').order_by('-booked_at')[:50]
    return ok({
        'rate': float(COMMISSION_RATE),
        'totals': {
            'tickets': totals['tickets'] or 0,
            'seats': totals['seats'] or 0,
            'sales': float(totals['sales'] or 0),
            'commission': float(totals['commission'] or 0),
            'operator': float(totals['operator'] or 0),
        },
        'per_operator': per_operator,
        'recent': [booking_dict(b) for b in recent],
    })


@api_view(['GET'])
@jwt_required('ADMIN')
def login_logs(request):
    logs = LoginLog.objects.select_related('user').order_by('-created_at')[:100]
    stats = {
        'total': LoginLog.objects.count(),
        'success': LoginLog.objects.filter(status='SUCCESS').count(),
        'failed': LoginLog.objects.filter(status='FAILED').count(),
        'locked': LoginLog.objects.filter(status='LOCKED').count(),
        'logout': LoginLog.objects.filter(status='LOGOUT').count(),
    }
    return ok({'stats': stats, 'logs': [login_log_dict(l) for l in logs]})


@api_view(['GET'])
@jwt_required('ADMIN')
def audit(request):
    rows = AdminAudit.objects.select_related('admin').order_by('-created_at')[:100]
    return ok([audit_dict(a) for a in rows])


def _activity_dict(a):
    return {'id': a.id, 'user': a.user.full_name if a.user else '(deleted account)',
            'email': a.user.email if a.user else '', 'action': a.action, 'detail': a.detail,
            'amount': float(a.amount) if a.amount is not None else None,
            'created_at': timezone.localtime(a.created_at).strftime('%Y-%m-%d %H:%M:%S')}


@api_view(['GET'])
@jwt_required('ADMIN')
def activity_passenger(request):
    from testing_app.models import ActivityLog
    rows = ActivityLog.objects.filter(role='PASSENGER').select_related('user')[:200]
    return ok([_activity_dict(a) for a in rows])


@api_view(['GET'])
@jwt_required('ADMIN')
def activity_operator(request):
    from testing_app.models import ActivityLog
    rows = ActivityLog.objects.filter(role='OPERATOR').select_related('user')[:200]
    return ok([_activity_dict(a) for a in rows])

@api_view(['POST'])
@jwt_required('ADMIN')
def operator_set_password(request, operator_id):
    """Give (or reset) a password for an operator's account.
    Lets Google-created operators use the email+password+OTP login too."""
    from django.contrib.auth.hashers import make_password

    op = Operator.objects.select_related('user').filter(id=operator_id).first()
    if op is None:
        return err('Operator not found.', status=404)
    password = str(body(request).get('password', ''))
    if len(password) < 6:
        return err('Password must be at least 6 characters.')
    u = op.user
    u.password_hash = make_password(password)
    u.password_changed_at = timezone.now()
    u.failed_login_attempts = 0
    u.account_locked_until = None
    u.save(update_fields=['password_hash', 'password_changed_at',
                          'failed_login_attempts', 'account_locked_until'])
    api_audit(request, 'PASSWORD_SET', u.email, note=f'Password set for operator {op.company_name}')
    return ok(message=f'Password set. {u.full_name} can now login with email + password + OTP.')

# ---------------------------------------------------------------------------
# FR-27: operator document review
# ---------------------------------------------------------------------------

def _doc_dict(request, d):
    return {'id': d.id, 'doc_type': d.doc_type, 'status': d.status,
            'company': d.operator.company_name, 'operator_id': d.operator_id,
            'file_url': request.build_absolute_uri(d.file.url) if d.file else '',
            'file_name': (d.file.name or '').split('/')[-1],
            'review_note': d.review_note or '',
            'uploaded_at': timezone.localtime(d.uploaded_at).strftime('%Y-%m-%d %H:%M')}


# ---------------------------------------------------------------------------
# FR-27: bus document review - grouped by company, then by bus
# ---------------------------------------------------------------------------

@api_view(['GET'])
@jwt_required('ADMIN')
def documents(request):
    """Grouped Company -> Buses -> 4 document slots, so the page scales to
    thousands of operators without becoming one giant flat table."""
    from testing_app.models import BusDocument
    companies = []
    for op in Operator.objects.select_related('user').order_by('company_name'):
        buses = Bus.objects.filter(operator=op).prefetch_related('documents').order_by('-id')
        if not buses:
            continue
        bus_rows = []
        pending_count = 0
        for b in buses:
            existing = {d.doc_type: d for d in b.documents.all()}
            slots = []
            for doc_type, label in BusDocument.DOC_TYPE_CHOICES:
                d = existing.get(doc_type)
                if d and d.status == 'PENDING':
                    pending_count += 1
                slots.append({
                    'doc_type': doc_type, 'label': label,
                    'status': d.status if d else 'MISSING',
                    'doc_id': d.id if d else None,
                })
            bus_rows.append({
                'bus_id': b.id, 'bus_name': b.bus_name, 'registration_no': b.registration_no,
                'approval_status': b.approval_status, 'is_bookable': b.is_bookable(), 'slots': slots,
            })
        companies.append({
            'operator_id': op.id, 'company': op.company_name, 'verified': op.verified,
            'buses': bus_rows, 'pending_count': pending_count,
        })
    # Companies with pending document reviews float to the top
    companies.sort(key=lambda c: (-c['pending_count'], c['company']))
    return ok(companies)


@api_view(['GET'])
@jwt_required('ADMIN')
def bus_documents(request, bus_id):
    """The same 4-slot detail view the operator sees, plus review actions."""
    from testing_app.models import BusDocument
    b = Bus.objects.select_related('operator').filter(id=bus_id).first()
    if b is None:
        return err('Bus not found.', status=404)
    existing = {d.doc_type: d for d in b.documents.all()}
    slots = []
    for doc_type, label in BusDocument.DOC_TYPE_CHOICES:
        d = existing.get(doc_type)
        if d:
            slots.append({
                'doc_id': d.id, 'doc_type': doc_type, 'label': label, 'status': d.status,
                'file_url': request.build_absolute_uri(d.file.url) if d.file else '',
                'file_name': (d.file.name or '').split('/')[-1] if d.file else '',
                'review_note': d.review_note or '',
                'uploaded_at': timezone.localtime(d.uploaded_at).strftime('%Y-%m-%d %H:%M'),
            })
        else:
            slots.append({'doc_id': None, 'doc_type': doc_type, 'label': label, 'status': 'MISSING',
                         'file_url': '', 'file_name': '', 'review_note': '', 'uploaded_at': None})
    return ok({'bus_id': b.id, 'bus_name': b.bus_name, 'company': b.operator.company_name,
              'approval_status': b.approval_status, 'is_fully_verified': b.is_fully_verified(),
              'is_bookable': b.is_bookable(), 'slots': slots})


@api_view(['POST'])
@jwt_required('ADMIN')
def bus_document_review(request, doc_id):
    from testing_app.models import BusDocument
    d = BusDocument.objects.select_related('bus__operator').filter(id=doc_id).first()
    if d is None:
        return err('Document not found.', status=404)
    data = body(request)
    action = str(data.get('action', '')).lower()
    note = str(data.get('note', '') or '').strip()
    if action not in ('verify', 'reject'):
        return err("action must be 'verify' or 'reject'.")
    if action == 'reject' and not note:
        return err('A note is required when rejecting a document.')
    d.status = 'VERIFIED' if action == 'verify' else 'REJECTED'
    d.review_note = note or None
    d.reviewed_at = timezone.now()
    d.reviewed_by = request.api_user
    d.save(update_fields=['status', 'review_note', 'reviewed_at', 'reviewed_by'])
    b = d.bus
    api_audit(request, f'BUS_DOCUMENT_{action.upper()}',
             f'{b.operator.company_name} · {b.bus_name} · {d.doc_type}', note=note or None)

    from testing_app.realtime import notify_operator
    notify_operator(b.operator_id, 'bus_document_reviewed', {
        'bus_id': b.id, 'bus_name': b.bus_name, 'doc_type': d.doc_type, 'status': d.status,
    })
    if action == 'verify' and b.is_fully_verified():
        notify_operator(b.operator_id, 'bus_fully_verified', {'bus_id': b.id, 'bus_name': b.bus_name})

    return ok({'status': d.status, 'is_fully_verified': b.is_fully_verified(), 'is_bookable': b.is_bookable()},
              message=f'Document {d.status.lower()}.')


# ---------------------------------------------------------------------------
# FR-30: payment oversight
# ---------------------------------------------------------------------------

def _payment_dict(p):
    return {'id': p.id, 'transaction_uuid': p.transaction_uuid, 'gateway': p.gateway,
            'amount': float(p.amount), 'status': p.status, 'ref_id': p.ref_id or '',
            'booking_code': p.booking.booking_code, 'passenger': p.booking.user.full_name,
            'created_at': timezone.localtime(p.created_at).strftime('%Y-%m-%d %H:%M')}


@api_view(['GET'])
@jwt_required('ADMIN')
def payments(request):
    from testing_app.models import Payment
    qs = Payment.objects.select_related('booking__user').order_by('-created_at')
    stats = {row['status']: row['n'] for row in qs.values('status').annotate(n=Count('id'))}
    complete = qs.filter(status='COMPLETE').aggregate(total=Sum('amount'))
    return ok({
        'stats': stats,
        'collected': float(complete['total'] or 0),
        'success_rate': round(
            (stats.get('COMPLETE', 0) / max(sum(stats.values()), 1)) * 100, 1),
        'payments': [_payment_dict(p) for p in qs[:200]],
    })


@api_view(['POST'])
@jwt_required('ADMIN')
def payment_recheck(request, payment_id):
    """Live status-check against eSewa (FR-30 reconciliation helper)."""
    from testing_app.models import Payment
    from testing_app.payments import check_status
    p = Payment.objects.select_related('booking').filter(id=payment_id).first()
    if p is None:
        return err('Payment not found.', status=404)
    enquiry = check_status(p)
    if enquiry is None:
        return err('Could not reach eSewa - check the server\'s internet connection.', status=502)
    new_status = enquiry.get('status', '')
    if new_status in dict(Payment.STATUS_CHOICES):
        p.status = new_status
        p.ref_id = enquiry.get('ref_id') or p.ref_id
        p.raw_response = str(enquiry)
        p.save(update_fields=['status', 'ref_id', 'raw_response'])
        if new_status == 'COMPLETE' and p.booking.booking_status == 'PENDING_PAYMENT':
            p.booking.booking_status = 'CONFIRMED'
            p.booking.save(update_fields=['booking_status'])
    api_audit(request, 'PAYMENT_RECHECK', p.transaction_uuid, note=f'eSewa said: {new_status}')
    return ok(_payment_dict(p), message=f'eSewa reports: {new_status or "unknown"}.')


@api_view(['GET'])
@jwt_required('ADMIN')
def webhook_logs(request):
    from testing_app.models import PaymentWebhookLog
    qs = PaymentWebhookLog.objects.order_by('-created_at')[:100]
    return ok([{'id': l.id, 'transaction_uuid': l.transaction_uuid,
                'signature_valid': l.signature_valid,
                'raw': l.raw_payload[:160],
                'created_at': timezone.localtime(l.created_at).strftime('%Y-%m-%d %H:%M:%S')} for l in qs])


# ---------------------------------------------------------------------------
# FR-17 (admin): reported-issue queue    ·    FR-31: platform CSV export
# ---------------------------------------------------------------------------

@api_view(['GET'])
@jwt_required('ADMIN')
def rating_issues(request):
    from testing_app.models import Rating
    qs = Rating.objects.select_related('user', 'operator', 'booking').order_by('-created_at')
    return ok({
        'issues': [{'id': r.id, 'stars': r.stars, 'issue_note': r.issue_note,
                    'review': r.review, 'by': r.user.full_name,
                    'company': r.operator.company_name, 'booking_code': r.booking.booking_code,
                    'created_at': timezone.localtime(r.created_at).strftime('%Y-%m-%d')}
                   for r in qs.filter(issue_flag=True)[:100]],
        'recent': [{'stars': r.stars, 'review': r.review, 'by': r.user.full_name,
                    'company': r.operator.company_name}
                   for r in qs.filter(issue_flag=False)[:20]],
    })


@api_view(['GET'])
@jwt_required('ADMIN')
def export_bookings_csv(request):
    import csv
    from django.http import HttpResponse
    rows = Booking.objects.select_related(
        'user', 'schedule__bus__operator', 'schedule__route__source_city',
        'schedule__route__destination_city').order_by('-booked_at')[:5000]
    resp = HttpResponse(content_type='text/csv')
    resp['Content-Disposition'] = 'attachment; filename="platform-bookings.csv"'
    w = csv.writer(resp)
    w.writerow(['Booking', 'Passenger', 'Operator', 'Trip', 'Departure', 'Seats',
                'Total (Rs)', 'Commission (Rs)', 'Operator earning (Rs)', 'Status', 'Booked at'])
    for b in rows:
        w.writerow([b.booking_code, b.user.full_name, b.schedule.bus.operator.company_name,
                    f"{b.schedule.route.source_city.city_name}-{b.schedule.route.destination_city.city_name}",
                    timezone.localtime(b.schedule.departure_time).strftime('%Y-%m-%d %H:%M'),
                    b.seats_count, b.total_amount, b.commission_amount, b.operator_earning,
                    b.booking_status, timezone.localtime(b.booked_at).strftime('%Y-%m-%d %H:%M')])
    return resp


@api_view(['GET'])
@jwt_required('ADMIN')
def export_payments_csv(request):
    import csv
    from django.http import HttpResponse
    from testing_app.models import Payment
    rows = Payment.objects.select_related('booking__user').order_by('-created_at')[:5000]
    resp = HttpResponse(content_type='text/csv')
    resp['Content-Disposition'] = 'attachment; filename="platform-payments.csv"'
    w = csv.writer(resp)
    w.writerow(['Transaction UUID', 'Booking', 'Passenger', 'Gateway', 'Amount (Rs)',
                'Status', 'Ref ID', 'Created at'])
    for p in rows:
        w.writerow([p.transaction_uuid, p.booking.booking_code, p.booking.user.full_name,
                    p.gateway, p.amount, p.status, p.ref_id or '',
                    timezone.localtime(p.created_at).strftime('%Y-%m-%d %H:%M')])
    return resp
