import json
import logging
import os
from decimal import Decimal

from django.conf import settings
from django.core.exceptions import ValidationError
from django.http import Http404, JsonResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse
from django.views.decorators.csrf import csrf_exempt

from .models import Package, Transaction
from .mpesa_api import (
    get_megapay_transaction_status,
    initiate_megapay_payment,
    parse_megapay_webhook,
)

logger = logging.getLogger(__name__)


def download_app(request):
    apk_file_path = os.path.join(settings.BASE_DIR, "startl", "downloads", "app-release.apk")
    if os.path.exists(apk_file_path):
        with open(apk_file_path, "rb") as fh:
            response = JsonResponse({}, status=200)
            response.content = fh.read()
            response["Content-Type"] = "application/vnd.android.package-archive"
            response["Content-Disposition"] = "attachment; filename=" + os.path.basename(apk_file_path)
            return response
    raise Http404


def package_list(request):
    packages = Package.objects.all()
    return render(request, "packages.html", {"packages": packages})


def format_phone_number(phone_number):
    phone_number = (phone_number or "").strip().replace(" ", "")
    if phone_number.startswith("254"):
        return phone_number
    if phone_number.startswith("0") and len(phone_number) == 10:
        return "254" + phone_number[1:]
    return phone_number


def lipa_na_mpesa(request, package_id):
    """Initiate a MegaPay M-Pesa STK Push for the selected package."""
    package = get_object_or_404(Package, id=package_id)

    if request.method == "POST":
        phone_number = format_phone_number(request.POST.get("phone_number"))
        if not phone_number:
            return render(request, "error.html", {"error": "Enter a valid M-Pesa phone number."})

        transaction = Transaction.objects.create(
            phone_number=phone_number,
            package=package,
            amount=package.price,
            status="pending",
        )
        try:
            data = initiate_megapay_payment(
                reference=str(transaction.id),
                phone_number=phone_number,
                amount=package.price,
            )
            logger.info("MegaPay STK response: %s", data)
            request_id = data.get("transaction_request_id")
            success_code = str(data.get("success", "")).strip()
            if success_code == "200" and request_id:
                transaction.mpesa_code = request_id
                transaction.save(update_fields=["mpesa_code"])
                return redirect(reverse("verify_payment", args=[transaction.id]))

            transaction.status = "failed"
            transaction.save(update_fields=["status"])
            return render(
                request,
                "error.html",
                {
                    "error": (
                        data.get("massage")
                        or data.get("message")
                        or data.get("ResponseDescription")
                        or f"MegaPay rejected the request (HTTP {data.get('_http_status', 'unknown')})."
                    )
                },
            )
        except Exception:
            logger.exception("MegaPay STK initiation failed for phone ending %s", phone_number[-4:])
            transaction.status = "failed"
            transaction.save(update_fields=["status"])
            return render(request, "error.html", {"error": "Error processing payment request. Please try again."})

    return render(request, "checkout.html", {"package": package})


def verify_payment(request, transaction_id):
    """Check the MegaPay transaction status and show the result."""
    transaction = get_object_or_404(Transaction, id=transaction_id)
    if transaction.status == "successful":
        return render(request, "success.html", {"transaction": transaction})

    try:
        data = get_megapay_transaction_status(transaction.mpesa_code)
        logger.info("MegaPay status response: %s", data)
        if str(data.get("TransactionCode")) == "0" or data.get("TransactionStatus") == "Completed":
            transaction.status = "successful"
            transaction.mpesa_code = data.get("TransactionReceipt") or transaction.mpesa_code
            transaction.save(update_fields=["status", "mpesa_code"])
            return render(request, "success.html", {"transaction": transaction})
        if data.get("TransactionStatus") in {"Failed", "Cancelled", "Expired"}:
            transaction.status = "failed"
            transaction.save(update_fields=["status"])
            return render(request, "failure.html", {"transaction": transaction})
        return render(request, "error.html", {"error": "Payment is still processing. Check again shortly."})
    except Exception:
        logger.exception("MegaPay status check failed")
        return render(request, "error.html", {"error": "Error verifying payment. Please try again."})


@csrf_exempt
def payment_callback(request):
    """Receive and persist MegaPay webhook notifications."""
    if request.method != "POST":
        return JsonResponse({"error": "Invalid callback request."}, status=405)
    try:
        data = json.loads(request.body or "{}")
        parsed = parse_megapay_webhook(data)
        logger.info("MegaPay webhook: %s", data)
        reference = parsed["reference"]
        transaction = None
        if reference:
            try:
                transaction = Transaction.objects.filter(id=reference).first()
            except (ValueError, TypeError, ValidationError):
                transaction = None
        if transaction is None and parsed["request_id"]:
            transaction = Transaction.objects.filter(mpesa_code=parsed["request_id"]).first()
        if transaction is None:
            return JsonResponse({"status": "ignored", "message": "Transaction not found"}, status=200)

        transaction.status = "successful" if parsed["successful"] else "failed"
        if parsed["receipt"]:
            transaction.mpesa_code = parsed["receipt"]
        transaction.save(update_fields=["status", "mpesa_code"])
        return JsonResponse({"status": "received"}, status=200)
    except (json.JSONDecodeError, ValueError, TypeError):
        return JsonResponse({"error": "Invalid JSON payload"}, status=400)
    except Exception:
        logger.exception("MegaPay webhook processing failed")
        return JsonResponse({"error": "Webhook processing failed"}, status=500)
