import json
import logging

from django.contrib.admin.views.decorators import staff_member_required
from django.contrib.auth.decorators import login_required
from django.http import HttpResponse, JsonResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.utils.decorators import method_decorator
from django.views import View
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST

from escrow.models import EscrowTransaction, EscrowLog
from escrow.services import escrow_service
from escrow.services.paystack import verify_webhook_signature

logger = logging.getLogger(__name__)


# ─────────────────────────────────────────────────────────────────────────────
# Payment Callback (user returns from Paystack)
# ─────────────────────────────────────────────────────────────────────────────

def payment_callback(request):
    """
    Paystack redirects here after payment attempt.
    We verify the payment and show the result.
    """
    reference = request.GET.get("reference", "").strip()
    if not reference:
        return redirect("/")

    try:
        success = escrow_service.confirm_payment(reference)
    except Exception as exc:
        logger.error("Payment callback error for %s: %s", reference, exc)
        success = False

    escrow = EscrowTransaction.objects.filter(
        paystack_reference=reference
    ).select_related("order", "vendor").first()

    if not escrow:
        return render(request, "escrow/payment_result.html", {
            "success": False,
            "message": "We could not find your payment. Please contact support.",
        })

    if success:
        return render(request, "escrow/payment_result.html", {
            "success": True,
            "escrow": escrow,
            "order": escrow.order,
        })
    else:
        return render(request, "escrow/payment_result.html", {
            "success": False,
            "message": "Payment verification failed. Please contact support with your reference.",
            "reference": reference,
        })


# ─────────────────────────────────────────────────────────────────────────────
# Paystack Webhook
# ─────────────────────────────────────────────────────────────────────────────

@csrf_exempt
def paystack_webhook(request):
    """
    Paystack sends events here. Verify signature and process.
    """
    if request.method != "POST":
        return HttpResponse(status=405)

    signature = request.headers.get("X-Paystack-Signature", "")
    payload_bytes = request.body

    if not verify_webhook_signature(payload_bytes, signature):
        logger.warning("Paystack webhook: invalid signature")
        return HttpResponse(status=400)

    try:
        event = json.loads(payload_bytes)
    except json.JSONDecodeError:
        return HttpResponse(status=400)

    event_type = event.get("event", "")
    data = event.get("data", {})

    logger.info("Paystack webhook received: %s", event_type)

    if event_type == "charge.success":
        reference = data.get("reference", "")
        if reference:
            try:
                escrow_service.confirm_payment(reference)
            except Exception as exc:
                logger.error("Webhook confirm_payment error: %s", exc)

    elif event_type == "transfer.success":
        transfer_code = data.get("transfer_code", "")
        if transfer_code:
            escrow = EscrowTransaction.objects.filter(
                paystack_transfer_code=transfer_code
            ).first()
            if escrow and escrow.status == EscrowTransaction.STATUS_RELEASED:
                from django.utils import timezone
                escrow.payout_completed_at = timezone.now()
                escrow.save(update_fields=["payout_completed_at", "updated_at"])
                escrow_service._log(
                    escrow, "payout_completed", EscrowTransaction.STATUS_RELEASED,
                    EscrowTransaction.STATUS_RELEASED,
                    f"Paystack transfer.success received for {transfer_code}"
                )

    elif event_type == "transfer.failed":
        transfer_code = data.get("transfer_code", "")
        if transfer_code:
            escrow = EscrowTransaction.objects.filter(
                paystack_transfer_code=transfer_code
            ).first()
            if escrow:
                escrow_service._log(
                    escrow, "payout_initiated", escrow.status, escrow.status,
                    f"Paystack transfer FAILED for code {transfer_code}"
                )

    return HttpResponse(status=200)


# ─────────────────────────────────────────────────────────────────────────────
# Buyer Confirmation Page
# ─────────────────────────────────────────────────────────────────────────────

def buyer_confirm(request, token):
    """
    Landing page from the SMS link.
    GET: Show confirmation page with delivery photos.
    POST: Confirm (yes) or dispute (no).
    """
    escrow = get_object_or_404(EscrowTransaction, buyer_confirmation_token=token)

    allowed_statuses = (
        EscrowTransaction.STATUS_DELIVERY_UPLOADED,
        EscrowTransaction.STATUS_DISPUTED,
        EscrowTransaction.STATUS_BUYER_CONFIRMED,
        EscrowTransaction.STATUS_AUTO_RELEASED,
        EscrowTransaction.STATUS_RELEASED,
    )
    if escrow.status not in allowed_statuses:
        return render(request, "escrow/confirm_error.html", {
            "message": "This confirmation link is not valid at this time.",
            "escrow": escrow,
        })

    # Already actioned
    if escrow.status in (
        EscrowTransaction.STATUS_BUYER_CONFIRMED,
        EscrowTransaction.STATUS_AUTO_RELEASED,
        EscrowTransaction.STATUS_RELEASED,
    ):
        return render(request, "escrow/confirm_already_done.html", {"escrow": escrow})

    if request.method == "POST":
        action = request.POST.get("action", "")
        reason = request.POST.get("dispute_reason", "").strip()

        if action == "confirm":
            try:
                escrow = escrow_service.buyer_confirm_receipt(str(token))
                return render(request, "escrow/confirm_success.html", {"escrow": escrow})
            except Exception as exc:
                logger.error("Buyer confirm error: %s", exc)
                return render(request, "escrow/confirm_error.html", {
                    "message": "Something went wrong. Please contact support.",
                    "escrow": escrow,
                })

        elif action == "dispute":
            try:
                escrow = escrow_service.buyer_dispute(str(token), reason=reason)
                return render(request, "escrow/dispute_submitted.html", {"escrow": escrow})
            except Exception as exc:
                logger.error("Buyer dispute error: %s", exc)
                return render(request, "escrow/confirm_error.html", {
                    "message": "Something went wrong. Please contact support.",
                    "escrow": escrow,
                })

    photos = escrow.delivery_photos.all()
    return render(request, "escrow/buyer_confirm.html", {
        "escrow": escrow,
        "photos": photos,
        "token": token,
    })


# ─────────────────────────────────────────────────────────────────────────────
# Vendor: Mark Delivery
# ─────────────────────────────────────────────────────────────────────────────

@login_required
def vendor_mark_delivered(request, escrow_id):
    """Vendor marks an order as delivered and uploads photos."""
    from vendors.models import Vendor
    try:
        vendor = request.user.vendor_profile
    except Exception:
        return redirect("vendors:register")

    escrow = get_object_or_404(
        EscrowTransaction, id=escrow_id, vendor=vendor,
        status=EscrowTransaction.STATUS_HELD
    )

    if request.method == "POST":
        note = request.POST.get("delivery_note", "").strip()
        photos = request.FILES.getlist("delivery_photos")

        errors = []
        if not photos:
            errors.append("Please upload at least one delivery photo.")
        for ph in photos:
            if not ph.content_type.startswith("image/"):
                errors.append(f"'{ph.name}' is not a valid image.")
            if ph.size > 10 * 1024 * 1024:
                errors.append(f"'{ph.name}' exceeds the 10 MB limit.")

        if errors:
            return render(request, "escrow/vendor_delivery.html", {
                "escrow": escrow, "errors": errors
            })

        try:
            escrow = escrow_service.vendor_mark_delivered(
                str(escrow_id), note, photos, request
            )
            return render(request, "escrow/delivery_submitted.html", {"escrow": escrow})
        except Exception as exc:
            logger.error("vendor_mark_delivered error: %s", exc)
            return render(request, "escrow/vendor_delivery.html", {
                "escrow": escrow,
                "errors": [f"Error: {exc}"]
            })

    return render(request, "escrow/vendor_delivery.html", {"escrow": escrow})


# ─────────────────────────────────────────────────────────────────────────────
# Admin Escrow Dashboard
# ─────────────────────────────────────────────────────────────────────────────

@staff_member_required
def admin_escrow_dashboard(request):
    """Overview of all escrow transactions for admin."""
    status_filter = request.GET.get("status", "")
    qs = EscrowTransaction.objects.select_related(
        "order", "vendor", "vendor_order"
    ).order_by("-created_at")

    if status_filter:
        qs = qs.filter(status=status_filter)

    # Aggregate stats
    from django.db.models import Sum
    stats = {
        "total_held": EscrowTransaction.objects.filter(
            status=EscrowTransaction.STATUS_HELD
        ).aggregate(t=Sum("amount"))["t"] or 0,
        "total_released": EscrowTransaction.objects.filter(
            status__in=[EscrowTransaction.STATUS_RELEASED, EscrowTransaction.STATUS_AUTO_RELEASED]
        ).aggregate(t=Sum("seller_payout"))["t"] or 0,
        "disputed": EscrowTransaction.objects.filter(
            status=EscrowTransaction.STATUS_DISPUTED
        ).count(),
        "pending_delivery": EscrowTransaction.objects.filter(
            status=EscrowTransaction.STATUS_HELD
        ).count(),
    }

    return render(request, "escrow/admin_dashboard.html", {
        "transactions": qs[:100],
        "stats": stats,
        "status_choices": EscrowTransaction.STATUS_CHOICES,
        "status_filter": status_filter,
    })


@staff_member_required
def admin_escrow_detail(request, escrow_id):
    """Admin detail view for a single escrow."""
    escrow = get_object_or_404(
        EscrowTransaction.objects.select_related("order", "vendor", "vendor_order"),
        id=escrow_id
    )
    logs = escrow.logs.order_by("created_at")
    photos = escrow.delivery_photos.all()

    if request.method == "POST":
        action = request.POST.get("action", "")
        if action == "force_release":
            try:
                escrow = escrow_service.admin_force_release(str(escrow_id), request.user)
                return redirect("escrow:admin_detail", escrow_id=escrow_id)
            except Exception as exc:
                return render(request, "escrow/admin_detail.html", {
                    "escrow": escrow, "logs": logs, "photos": photos,
                    "error": str(exc)
                })
        elif action == "refund":
            try:
                escrow = escrow_service.admin_refund(str(escrow_id), request.user)
                return redirect("escrow:admin_detail", escrow_id=escrow_id)
            except Exception as exc:
                return render(request, "escrow/admin_detail.html", {
                    "escrow": escrow, "logs": logs, "photos": photos,
                    "error": str(exc)
                })
        elif action == "retry_payout":
            try:
                escrow = escrow_service.admin_retry_payout(str(escrow_id), request.user)
                return redirect("escrow:admin_detail", escrow_id=escrow_id)
            except Exception as exc:
                return render(request, "escrow/admin_detail.html", {
                    "escrow": escrow, "logs": logs, "photos": photos,
                    "error": str(exc)
                })

    return render(request, "escrow/admin_detail.html", {
        "escrow": escrow,
        "logs": logs,
        "photos": photos,
    })


# ─────────────────────────────────────────────────────────────────────────────
# Vendor Payout Account Setup
# ─────────────────────────────────────────────────────────────────────────────

@login_required
def vendor_payout_setup(request):
    """Vendor sets up their payout account (bank or mobile money)."""
    try:
        vendor = request.user.vendor_profile
    except Exception:
        return redirect("vendors:register")

    from escrow.models import VendorPayoutAccount
    from escrow.services.paystack import (
        create_transfer_recipient_bank,
        create_transfer_recipient_mobile_money,
        list_banks_ghana,
    )

    try:
        payout_acct = vendor.payout_account
    except VendorPayoutAccount.DoesNotExist:
        payout_acct = None

    if request.method == "POST":
        account_type = request.POST.get("account_type", "nuban")
        account_name = request.POST.get("account_name", "").strip()
        account_number = request.POST.get("account_number", "").strip()
        bank_code = request.POST.get("bank_code", "").strip()
        mobile_network = request.POST.get("mobile_network", "").strip()

        errors = []
        if not account_name:
            errors.append("Account name is required.")
        if not account_number:
            errors.append("Account number / phone is required.")
        if account_type == "nuban" and not bank_code:
            errors.append("Please select a bank.")
        if account_type == "mobile_money" and not mobile_network:
            errors.append("Please select a mobile network.")

        if not errors:
            try:
                if account_type == "nuban":
                    recipient_code = create_transfer_recipient_bank(
                        account_name, account_number, bank_code
                    )
                else:
                    recipient_code = create_transfer_recipient_mobile_money(
                        account_name, account_number, mobile_network
                    )

                obj, _ = VendorPayoutAccount.objects.update_or_create(
                    vendor=vendor,
                    defaults={
                        "account_type": account_type,
                        "account_name": account_name,
                        "account_number": account_number,
                        "bank_code": bank_code,
                        "mobile_network": mobile_network,
                        "paystack_recipient_code": recipient_code,
                    },
                )
                return render(request, "escrow/payout_setup.html", {
                    "vendor": vendor,
                    "payout_acct": obj,
                    "saved": True,
                    "banks": [],
                })
            except Exception as exc:
                errors.append(f"Paystack error: {exc}")

        try:
            banks = list_banks_ghana()
        except Exception:
            banks = []

        return render(request, "escrow/payout_setup.html", {
            "vendor": vendor, "payout_acct": payout_acct,
            "errors": errors, "banks": banks,
        })

    try:
        banks = list_banks_ghana()
    except Exception:
        banks = []

    return render(request, "escrow/payout_setup.html", {
        "vendor": vendor,
        "payout_acct": payout_acct,
        "banks": banks,
    })