"""
SasaPay callback views and initiation endpoints.

URL patterns (added to payments/urls.py):
  /payments/sasapay/ipn/                  ← C2B IPN (incoming payments)
  /payments/sasapay/stk-callback/         ← STK push result callback
  /payments/sasapay/disbursement-callback/ ← B2C disbursement result callback
  /payments/sasapay/initiate-stk/         ← Staff: trigger STK push
  /payments/sasapay/disburse/             ← Staff: trigger loan disbursement
  /payments/sasapay/dashboard/            ← Staff: SasaPay overview
  /payments/sasapay/unknown-payments/     ← Staff: review unknown payments
"""

import json
import logging

from django.http import JsonResponse
from django.views import View
from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator
from django.contrib.auth.decorators import login_required
from django.contrib.admin.views.decorators import staff_member_required
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib import messages
from django.core.paginator import Paginator
from django.db.models import Sum, Count

from .sasapay_service import (
    initiate_stk_push,
    disburse_loan,
    process_ipn_callback,
    process_stk_callback,
    process_disbursement_callback,
    get_access_token,
)
from .sasapay_models import (
    SasaPayIPNLog,
    SasaPaySTKResult,
    SasaPayDisbursementResult,
    SasaPayUnknownPayment,
)

logger = logging.getLogger(__name__)


# ---------------------------------------------------------------------------
# Callback views (no auth — called by SasaPay servers)
# ---------------------------------------------------------------------------

@method_decorator(csrf_exempt, name='dispatch')
class SasaPayIPNView(View):
    """
    Receive C2B IPN (incoming payment notification) from SasaPay.
    Endpoint registered with SasaPay as the IPN URL.
    """

    def post(self, request):
        try:
            data = json.loads(request.body.decode('utf-8'))
            logger.info(f"SasaPay IPN received from {request.META.get('REMOTE_ADDR')}: {data}")
            result = process_ipn_callback(data)
            return JsonResponse({'ResultCode': '0', 'ResultDesc': 'Accepted'})
        except json.JSONDecodeError:
            logger.error("SasaPay IPN: invalid JSON")
            return JsonResponse({'ResultCode': '1', 'ResultDesc': 'Invalid JSON'}, status=400)
        except Exception as exc:
            logger.error(f"SasaPay IPN error: {exc}", exc_info=True)
            # Always acknowledge to prevent SasaPay retries flooding the server
            return JsonResponse({'ResultCode': '0', 'ResultDesc': 'Received'})

    def get(self, request):
        """Health-check endpoint so SasaPay can verify the URL is reachable."""
        return JsonResponse({'status': 'ok', 'service': 'SasaPay IPN'})


@method_decorator(csrf_exempt, name='dispatch')
class SasaPaySTKCallbackView(View):
    """Receive STK push result callback from SasaPay."""

    def post(self, request):
        try:
            data = json.loads(request.body.decode('utf-8'))
            logger.info(f"SasaPay STK callback: {data}")
            result = process_stk_callback(data)
            return JsonResponse({'ResultCode': '0', 'ResultDesc': 'Received'})
        except Exception as exc:
            logger.error(f"SasaPay STK callback error: {exc}", exc_info=True)
            return JsonResponse({'ResultCode': '0', 'ResultDesc': 'Received'})

    def get(self, request):
        return JsonResponse({'status': 'ok', 'service': 'SasaPay STK Callback'})


@method_decorator(csrf_exempt, name='dispatch')
class SasaPayDisbursementCallbackView(View):
    """Receive B2C disbursement result callback from SasaPay."""

    def post(self, request):
        try:
            data = json.loads(request.body.decode('utf-8'))
            logger.info(f"SasaPay disbursement callback: {data}")
            result = process_disbursement_callback(data)
            return JsonResponse({'ResultCode': '0', 'ResultDesc': 'Received'})
        except Exception as exc:
            logger.error(f"SasaPay disbursement callback error: {exc}", exc_info=True)
            return JsonResponse({'ResultCode': '0', 'ResultDesc': 'Received'})

    def get(self, request):
        return JsonResponse({'status': 'ok', 'service': 'SasaPay Disbursement Callback'})


# ---------------------------------------------------------------------------
# Staff-facing initiation views
# ---------------------------------------------------------------------------

@login_required
@staff_member_required
def initiate_stk_view(request):
    """
    Staff view to manually trigger an STK push to a customer.
    POST params: phone_number, amount, loan_number, description (optional)
    """
    if request.method == 'POST':
        phone = request.POST.get('phone_number', '').strip()
        amount = request.POST.get('amount', '').strip()
        loan_number = request.POST.get('loan_number', '').strip()
        description = request.POST.get('description', 'Loan Payment').strip()

        if not phone or not amount or not loan_number:
            messages.error(request, 'Phone number, amount and loan number are required.')
            return redirect('payments:sasapay_dashboard')

        try:
            result = initiate_stk_push(
                phone_number=phone,
                amount=float(amount),
                account_reference=loan_number,
                transaction_desc=description,
            )
            if result.get('status') or result.get('CheckoutRequestID'):
                messages.success(
                    request,
                    f"STK push sent to {phone}. "
                    f"CheckoutID: {result.get('CheckoutRequestID', 'N/A')}"
                )
            else:
                messages.warning(request, f"STK push response: {result}")
        except Exception as exc:
            messages.error(request, f"STK push failed: {exc}")

        return redirect('payments:sasapay_dashboard')

    return redirect('payments:sasapay_dashboard')


@login_required
@staff_member_required
def initiate_disbursement_view(request):
    """
    Staff view to manually trigger a loan disbursement via SasaPay B2C.
    POST params: phone_number, amount, loan_number
    """
    if request.method == 'POST':
        phone = request.POST.get('phone_number', '').strip()
        amount = request.POST.get('amount', '').strip()
        loan_number = request.POST.get('loan_number', '').strip()

        if not phone or not amount or not loan_number:
            messages.error(request, 'Phone number, amount and loan number are required.')
            return redirect('payments:sasapay_dashboard')

        try:
            result = disburse_loan(
                phone_number=phone,
                amount=float(amount),
                loan_reference=loan_number,
            )
            if result.get('status') or result.get('SasaPayTransactionID'):
                messages.success(
                    request,
                    f"Disbursement initiated to {phone} for loan {loan_number}. "
                    f"Response: {result}"
                )
            else:
                messages.warning(request, f"Disbursement response: {result}")
        except Exception as exc:
            messages.error(request, f"Disbursement failed: {exc}")

        return redirect('payments:sasapay_dashboard')

    return redirect('payments:sasapay_dashboard')


# ---------------------------------------------------------------------------
# Dashboard & management views
# ---------------------------------------------------------------------------

@login_required
@staff_member_required
def sasapay_dashboard(request):
    """SasaPay overview dashboard."""
    from django.utils import timezone
    today = timezone.now().date()

    # IPN stats
    ipn_today = SasaPayIPNLog.objects.filter(created_at__date=today).count()
    ipn_total = SasaPayIPNLog.objects.count()

    # STK stats
    stk_today = SasaPaySTKResult.objects.filter(created_at__date=today).count()
    stk_success = SasaPaySTKResult.objects.filter(result_code='0').count()

    # Disbursement stats
    disb_today = SasaPayDisbursementResult.objects.filter(created_at__date=today).count()
    disb_success = SasaPayDisbursementResult.objects.filter(result_code='0').count()
    disb_amount = SasaPayDisbursementResult.objects.filter(
        result_code='0'
    ).aggregate(total=Sum('trans_amount'))['total'] or 0

    # Unknown payments
    unknown_pending = SasaPayUnknownPayment.objects.filter(resolved=False).count()

    # Recent activity
    recent_ipn = SasaPayIPNLog.objects.order_by('-created_at')[:5]
    recent_stk = SasaPaySTKResult.objects.order_by('-created_at')[:5]
    recent_disb = SasaPayDisbursementResult.objects.order_by('-created_at')[:5]

    # Active loans for STK/disbursement forms
    from loans.models import Loan
    active_loans = Loan.active_objects.filter(
        status__in=['active', 'approved']
    ).select_related('borrower').order_by('loan_number')[:100]

    from django.conf import settings
    site_url = getattr(settings, 'SITE_URL', 'https://uzuriapps.xyz')

    context = {
        'stats': {
            'ipn_today': ipn_today,
            'ipn_total': ipn_total,
            'stk_today': stk_today,
            'stk_success': stk_success,
            'disb_today': disb_today,
            'disb_success': disb_success,
            'disb_amount': disb_amount,
            'unknown_pending': unknown_pending,
        },
        'recent_ipn': recent_ipn,
        'recent_stk': recent_stk,
        'recent_disb': recent_disb,
        'active_loans': active_loans,
        'callback_urls': {
            'ipn': f"{site_url}/payments/sasapay/ipn/",
            'stk': f"{site_url}/payments/sasapay/stk-callback/",
            'disbursement': f"{site_url}/payments/sasapay/disbursement-callback/",
        },
    }
    return render(request, 'payments/sasapay_dashboard.html', context)


@login_required
@staff_member_required
def sasapay_unknown_payments(request):
    """List and resolve unknown SasaPay payments."""
    payments = SasaPayUnknownPayment.objects.order_by('-created_at')

    resolved_filter = request.GET.get('resolved')
    if resolved_filter == 'true':
        payments = payments.filter(resolved=True)
    elif resolved_filter == 'false' or not resolved_filter:
        payments = payments.filter(resolved=False)

    paginator = Paginator(payments, 25)
    page_obj = paginator.get_page(request.GET.get('page'))

    context = {
        'page_obj': page_obj,
        'resolved_filter': resolved_filter,
    }
    return render(request, 'payments/sasapay_unknown_payments.html', context)


@login_required
@staff_member_required
def resolve_unknown_payment(request, payment_id):
    """Mark an unknown payment as resolved."""
    payment = get_object_or_404(SasaPayUnknownPayment, id=payment_id)
    if request.method == 'POST':
        payment.resolved = True
        payment.save(update_fields=['resolved'])
        messages.success(request, f"Payment of KES {payment.amount} marked as resolved.")
    return redirect('payments:sasapay_unknown_payments')


@login_required
@staff_member_required
def test_sms_view(request):
    """
    Staff view to send a test SMS via Africa's Talking.
    Sends ONLY to the single phone number entered — not to all admin numbers.
    POST params: phone_number, message
    """
    if request.method == 'POST':
        phone = request.POST.get('phone_number', '').strip()
        message = request.POST.get('message', 'Test SMS from HAVEN GRAZURI').strip()

        if not phone:
            messages.error(request, 'Phone number is required.')
            return redirect('payments:sasapay_dashboard')

        try:
            from .sms_service import send_sms
            # sms_type='test' so it's clearly labelled in SMS Logs
            # Only sends to the ONE phone number entered — not admin broadcast
            result = send_sms([phone], message, sms_type='test')
            recips = result.get('SMSMessageData', {}).get('Recipients', [])
            if recips:
                status = recips[0].get('status', 'Unknown')
                cost   = recips[0].get('cost', 'KES 0.80')
                messages.success(request, f"Test SMS sent to {phone}: {status} (cost: {cost})")
            else:
                messages.warning(request, f"SMS response: {result}")
        except Exception as exc:
            messages.error(request, f"SMS failed: {exc}")

        return redirect('payments:sasapay_dashboard')

    return redirect('payments:sasapay_dashboard')


@login_required
@staff_member_required
def test_sasapay_token_view(request):
    """Quick test to verify SasaPay credentials work."""
    try:
        token = get_access_token(force_refresh=True)
        messages.success(request, f"SasaPay token obtained successfully. Token: {token[:20]}...")
    except Exception as exc:
        messages.error(request, f"SasaPay token failed: {exc}")
    return redirect('payments:sasapay_dashboard')
