import datetime
from decimal import Decimal
import requests
import uuid
import logging
from weasyprint import HTML
from nexusmart import settings
from django.contrib import messages
from django.http import HttpResponse, JsonResponse
from django.core.mail import EmailMessage
from django.utils.translation import gettext_lazy as _, gettext
from shop.utils import _get_customer_profile
from shop.model_translator import (
    get_order_translated_data,
    current_language_code,
    is_rtl_language,
)
from .models import Order, PaymentLog, OfferRequest
from django.template.loader import render_to_string
from django.contrib.auth.decorators import login_required
from django.shortcuts import render, get_object_or_404, redirect
from weasyprint.text.fonts import BytesIO, FontConfiguration
from dateutil.relativedelta import relativedelta

from django.shortcuts import redirect, render
from django.conf import settings
import logging
import uuid

logger = logging.getLogger(__name__)

def resolve_installment_plan(plan_key):
    """Resolve either a settings key or gateway plan_code to a canonical plan tuple."""
    if plan_key is None:
        return None, None

    plan_key = str(plan_key).strip()
    if not plan_key:
        return None, None

    if plan_key in settings.FLUTTERWAVE_INSTALLMENT_PLANS:
        return plan_key, settings.FLUTTERWAVE_INSTALLMENT_PLANS[plan_key]

    normalized_key = plan_key.lower()
    for key, plan in settings.FLUTTERWAVE_INSTALLMENT_PLANS.items():
        if key.lower() == normalized_key:
            return key, plan

    for key, plan in settings.FLUTTERWAVE_INSTALLMENT_PLANS.items():
        if str(plan.get("plan_code")).strip() == plan_key:
            return key, plan

    return None, None


@login_required
def order_list(request):
    orders = Order.objects.filter(user=request.user).order_by("-created_at")

    # Fetch all offers for this user's products
    user_offers = OfferRequest.objects.filter(user=request.user).select_related(
        "product"
    )
    offers_dict = {offer.product_id: offer for offer in user_offers}

    # Translate order items for current language
    translated_orders = []
    for order in orders:
        translated_order_data = get_order_translated_data(order)
        translated_orders.append(
            {
                "order": order,
                "translated_items": translated_order_data.get("items", []),
                "language": current_language_code(),
            }
        )

    if request.headers.get("HX-Request"):
        return render(
            request,
            "shop/partials/order_rows.html",
            {"orders": translated_orders, "offers_dict": offers_dict},
        )

    return render(
        request,
        "shop/order_list.html",
        {
            "orders": translated_orders,
            "offers_dict": offers_dict,
            "is_rtl": is_rtl_language(),
            "current_language": current_language_code(),
        },
    )


@login_required
def user_dashboard(request):
    customer = request.user
    customer_profile = _get_customer_profile(request.user)
    shipping_addresses = customer_profile.shipping_addresses.all()
    recent_orders = request.user.orders.all()[:5]

    context = {
        "customer": customer,
        "shipping_addresses": shipping_addresses,
        "recent_orders": recent_orders,
    }
    return render(request, "accounts/user_dashboard.html", context)


@login_required
def order_detail(request, order_id):
    order = get_object_or_404(Order, id=order_id, user=request.user)
    items = order.items.all()
    payment_logs = order.payment_logs.all().order_by("-created_at")

    # Get translated order data with product translations
    translated_order_data = get_order_translated_data(order)
    translated_items = translated_order_data.get("items", [])

    total = sum(item.price * item.quantity for item in items)
    amount_paid = sum(log.amount for log in payment_logs if log.status == "success")
    balance = total - amount_paid

    # Example installment breakdown (monthly)
    installments = []
    num_months = 3  # Example: 3-month plan
    monthly_amount = total / num_months
    for i in range(num_months):
        due_date = order.created_at + relativedelta(months=i + 1)
        is_paid = amount_paid >= monthly_amount * (i + 1)
        installments.append(
            {
                "month": _("Month {}").format(i + 1),
                "due_date": due_date,
                "amount": monthly_amount,
                "is_paid": is_paid,
            }
        )

    compliance_percentage = int((amount_paid / total) * 100) if total > 0 else 0

    context = {
        "order": order,
        "items": items,
        "translated_items": translated_items,
        "total": total,
        "amount_paid": amount_paid,
        "balance": balance,
        "payment_logs": payment_logs,
        "installments": installments,
        "can_pay": order.status in ["Pending", "Failed"],
        "compliance_percentage": compliance_percentage,
        "installment_plans": settings.FLUTTERWAVE_INSTALLMENT_PLANS,
        "is_rtl": is_rtl_language(),
        "current_language": current_language_code(),
    }
    return render(request, "shop/order_detail.html", context)


@login_required
def order_tracking_partial(request, order_id):
    order = get_object_or_404(Order, id=order_id, user=request.user)
    return render(request, "shop/partials/order_tracking.html", {"order": order})


@login_required
def download_invoice(request, order_id):
    order = get_object_or_404(Order, id=order_id, user=request.user)
    items = order.items.all()

    # Context for the invoice template
    context = {
        "order": order,
        "items": items,
        "user": request.user,
        "today": datetime.date.today(),
    }

    # Render HTML string
    html_string = render_to_string("shop/invoice.html", context)

    # Configure fonts (optional but recommended for better PDF rendering)
    font_config = FontConfiguration()

    # Generate PDF using WeasyPrint
    html = HTML(string=html_string, base_url=request.build_absolute_uri("/"))
    pdf_bytes = html.write_pdf(font_config=font_config)

    # Create response
    response = HttpResponse(pdf_bytes, content_type="application/pdf")
    response["Content-Disposition"] = f'attachment; filename="invoice_{order.id}.pdf"'

    return response


@login_required
def email_invoice(request, order_id):
    """Send invoice PDF as email attachment"""
    order = get_object_or_404(Order, id=order_id, user=request.user)
    items = order.items.all()

    # Generate PDF
    html_string = render_to_string(
        "shop/invoice.html",
        {
            "order": order,
            "items": items,
            "user": request.user,
        },
    )
    result = BytesIO()
    pdf = pisa.pisaDocument(BytesIO(html_string.encode("UTF-8")), result)

    if pdf.err:
        return HttpResponse("Error generating PDF", status=500)

    pdf_data = result.getvalue()

    # Email content
    subject = f"Banshix Invoice #{order.id}"
    body = render_to_string("shop/email_invoice_notification.html", {"order": order})

    email = EmailMessage(
        subject=subject,
        body=body,
        from_email=settings.DEFAULT_FROM_EMAIL,
        to=[request.user.email],
    )
    email.content_subtype = "html"
    email.attach(f"invoice_{order.id}.pdf", pdf_data, "application/pdf")

    try:
        email.send(fail_silently=False)
        # Optional: Add success message
        from django.contrib import messages

        messages.success(request, f"Invoice #{order.id} has been emailed to you.")
    except Exception as e:
        from django.contrib import messages

        messages.error(request, "Failed to send email. Please try again later.")

    return redirect("shop:order_detail", order_id=order.id)


@login_required
def cancel_order(request, order_id):
    order = get_object_or_404(Order, id=order_id, user=request.user)

    is_htmx = request.headers.get("HX-Request")

    # ❌ Invalid status
    if order.status.lower() != "pending":
        message = _("You can only cancel orders that are still pending.")

        if is_htmx:
            return HttpResponse(str(message), status=400)

        messages.error(request, message)
        return redirect("shop:order_detail", order_id=order.id)

    # ✅ Handle POST (actual cancellation)
    if request.method == "POST":
        order.status = "cancelled"
        order.save()

        # Log cancellation
        PaymentLog.objects.create(
            order=order,
            amount=order.total,
            status="failed",
            message=gettext("Order cancelled by customer"),
        )

        success_msg = _("Order #{} cancelled successfully.").format(order.id)

        # 🔥 HTMX RESPONSE
        if is_htmx:
            response = render(request, "shop/partials/order_row.html", {"order": order})

            # Optional: trigger toast notification via HTMX event
            response["HX-Trigger"] = "orderCancelled"
            return response

        # 🌐 NORMAL RESPONSE
        messages.success(request, success_msg)
        return redirect("shop:order_list")

    # ✅ GET request (confirmation page)

    if is_htmx:
        return render(
            request, "shop/partials/cancel_order_confirm.html", {"order": order}
        )

    return render(request, "shop/cancel_order_confirm.html", {"order": order})

def convert_currency(amount, from_currency="USD", to_currency="NGN"):
    """Convert amount from one currency to another using exchangerate.host"""
    try:
        url = "https://api.exchangerate.host/convert"
        params = {"from": from_currency, "to": to_currency, "amount": float(amount)}
        response = requests.get(url, params=params, timeout=10)
        response.raise_for_status()
        data = response.json()

        if data.get("success") is False:
            # Fallback rate if API fails
            fallback_rates = {"NGN": 1600, "USD": 1, "EUR": 1.1, "GBP": 1.3}
            rate = fallback_rates.get(to_currency, 1600)
            return round(float(amount) * rate, 2)

        return round(data["result"], 2)

    except Exception as e:
        print(f"Currency conversion error: {e}")
        # Safe fallback to NGN (adjust rate as needed)
        return round(float(amount) * 1600, 2)


# Callback (after payment)
def flutterwave_callback(request):
    """Handle Flutterwave payment callback safely"""
    tx_ref = request.GET.get("tx_ref") or request.GET.get("reference")
    transaction_id = request.GET.get("transaction_id")
    order_id = request.GET.get("order_id")

    # === GRACEFUL HANDLING FOR CANCELLED PAYMENTS ===
    if not order_id:
        messages.warning(request, "Payment was cancelled or incomplete. Your order is still pending.")
        return redirect('shop:order_list')

    if not tx_ref:
        messages.warning(request, "Payment cancelled. Your order is still pending.")
        return redirect('shop:order_list')

    if not settings.FLW_SECRET_KEY:
        messages.error(request, "Payment verification is unavailable. Please contact support.")
        return redirect('shop:order_detail', order_id=order_id)

    # Build verification URL
    if transaction_id:
        verify_url = f"https://api.flutterwave.com/v3/transactions/{transaction_id}/verify"
    else:
        verify_url = f"https://api.flutterwave.com/v3/transactions/verify-by-reference?tx_ref={tx_ref}"

    headers = {"Authorization": f"Bearer {settings.FLW_SECRET_KEY}"}

    try:
        response = requests.get(verify_url, headers=headers, timeout=15)

        if not response.text:
            messages.warning(request, "Payment verification timed out. Your order is still pending.")
            return redirect('shop:order_detail', order_id=order_id)

        if response.status_code not in [200, 201]:
            messages.error(request, f"Payment verification failed (Status: {response.status_code}).")
            return redirect('shop:order_detail', order_id=order_id)

        verify = response.json()

    except requests.exceptions.RequestException as e:
        logger.error(f"Network error in callback: {e}")
        messages.error(request, "Network error during verification. Please contact support.")
        return redirect('shop:order_detail', order_id=order_id)

    except ValueError as e:
        logger.error(f"JSON decode error: {e}")
        messages.error(request, "Invalid response from payment gateway.")
        return redirect('shop:order_detail', order_id=order_id)

    # Process successful payment
    verify_data = verify.get("data") or {}
    
    if verify.get("status") == "success" and verify_data.get("status") == "successful":
        metadata = verify_data.get("meta") or verify_data.get("metadata") or {}
        order_id_from_meta = metadata.get("order_id")

        order = Order.objects.filter(id=order_id_from_meta).first()

        if order:
            order.status = "Paid"
            order.save(update_fields=["status"])

            PaymentLog.objects.create(
                order=order,
                amount=Decimal(str(verify_data.get("amount", order.total))),
                status="success",
                message="Flutterwave payment confirmed",
                flutterwave_reference=transaction_id or tx_ref,
            )

            messages.success(request, "🎉 Payment successful! Your order has been confirmed.")
            return render(request, "shop/payment_success.html", {
                "reference": transaction_id or tx_ref,
                "order": order
            })

    # === FAILED OR CANCELLED PAYMENT ===
    messages.warning(request, "Payment could not be completed. Your order remains pending.")

    # Safe redirect back to order detail
    if order_id:
        return redirect('shop:order_detail', order_id=order_id)
    
    return redirect('shop:order_list')


@login_required
def flutterwave_initialize(request, order_id):
    """Handles BOTH Full Payment and Installment Payment"""
    order = get_object_or_404(Order, id=order_id, user=request.user)

    if order.status.lower() != "pending":
        messages.error(request, "This order cannot be paid at this time.")
        return redirect('shop:order_detail', order_id=order.id)

    payment_type = request.GET.get("payment_type", "full")   # 'full' or 'installment'
    plan_key = request.GET.get("plan_code")

    # Convert to NGN (Flutterwave requirement)
    user_currency = request.session.get("currency", "USD")
    ngn_total = convert_currency(
        amount=order.total, 
        from_currency=user_currency, 
        to_currency="NGN"
    )

    tx_ref = f"bx-{payment_type}-{order.id}-{uuid.uuid4().hex[:10]}"

    if payment_type == "installment":
        resolved_plan_key, plan_details = resolve_installment_plan(plan_key)
        if not plan_details:
            messages.error(request, "Invalid installment plan selected.")
            return redirect('shop:order_detail', order_id=order.id)

        plan_key = resolved_plan_key
        months = plan_details.get("months", 1)
        payment_amount = round(ngn_total / months, 2)
        # Ensure amount does not exceed gateway max
        try:
            max_limit = int(getattr(settings, 'FLUTTERWAVE_MAX_CHARGE_NGN', 0))
        except Exception:
            max_limit = 0

        if max_limit and payment_amount > max_limit:
            messages.error(
                request,
                f"Charge amount must not exceed NGN {max_limit:,}. Please split the order or contact support."
            )
            return redirect('shop:order_detail', order_id=order.id)
        title = f"Installment for Order #{order.id}"
        description = f"First installment (1 of {months})"

        payload = {
            "tx_ref": tx_ref,
            "amount": str(payment_amount),
            "currency": "NGN",
            "payment_plan": plan_details["plan_code"],   # Enables auto-recurring
            "redirect_url": request.build_absolute_uri(
                f"/flutterwave/callback/?order_id={order.id}&payment_type=installment"
            ),
            "customer": {
                "email": request.user.email,
                "name": request.user.get_full_name() or request.user.username,
            },
            "meta": {
                "order_id": order.id,
                "payment_type": "installment",
                "total_amount": float(order.total),
                "months": months,
            },
            "customizations": {
                "title": title,
                "description": description,
            }
        }

    else:  # Full Payment
        payment_amount = ngn_total
        # If full amount exceeds gateway limit, auto-split into chunks and
        # initiate payment for the first chunk. The rest can be processed
        # subsequently (via callback or scheduled job) using metadata/session.
        try:
            max_limit = int(getattr(settings, 'FLUTTERWAVE_MAX_CHARGE_NGN', 0))
        except Exception:
            max_limit = 0

        if max_limit and payment_amount > max_limit:
            from shop.utils import split_amount_into_chunks

            chunks = split_amount_into_chunks(payment_amount, max_limit)
            # Store split info in session so callback or follow-up flows can resume
            request.session['split_payment'] = {
                'order_id': order.id,
                'amounts': [float(a) for a in chunks],
                'current_index': 0,
            }

            # Use the first chunk as the hosted checkout amount
            payment_amount = float(chunks[0])
        title = f"Banshix Order #{order.id}"
        description = "Full Payment"

        payload = {
            "tx_ref": tx_ref,
            "amount": str(payment_amount),
            "currency": "NGN",
            "redirect_url": request.build_absolute_uri(
                f"/flutterwave/callback/?order_id={order.id}&payment_type=full"
            ),
            "customer": {
                "email": request.user.email,
                "name": request.user.get_full_name() or request.user.username,
            },
            "meta": {
                "order_id": order.id,
                "payment_type": "full",
                "total_amount": float(order.total),
                "split_payment": bool(request.session.get('split_payment')),
                "split_index": (request.session.get('split_payment', {}).get('current_index', 0) + 1) if request.session.get('split_payment') else None,
                "split_total": len(request.session.get('split_payment', {}).get('amounts', [])) if request.session.get('split_payment') else None,
                "split_amounts": request.session.get('split_payment', {}).get('amounts') if request.session.get('split_payment') else None,
            },
            "customizations": {
                "title": title,
                "description": description,
            }
        }

    # Make API call
    headers = {
        "Authorization": f"Bearer {settings.FLW_SECRET_KEY}",
        "Content-Type": "application/json"
    }

    try:
        response = requests.post(
            "https://api.flutterwave.com/v3/payments",
            json=payload,
            headers=headers,
            timeout=15
        )
        data = response.json()

        if response.status_code in [200, 201] and data.get("status") == "success":
            return redirect(data["data"]["link"])

        else:
            error_msg = data.get("message", "Payment initialization failed")
            messages.error(request, f"Failed: {error_msg}")

    except Exception as e:
        messages.error(request, "Network error. Please try again later.")
        print(f"Flutterwave Error: {e}")

    return redirect('shop:order_detail', order_id=order.id)


@login_required
def initialize_installment(request, order_id):
    order = get_object_or_404(Order, id=order_id, user=request.user)

    if order.status.lower() != "pending":
        messages.error(request, "This order is not eligible for installments.")
        return redirect("shop:order_detail", order_id=order.id)

    plan_key = request.GET.get("plan_code")  # e.g., '6_months' or gateway plan_code
    resolved_plan_key, plan_details = resolve_installment_plan(plan_key)

    if not plan_details:
        messages.error(request, "Invalid installment plan.")
        return redirect("shop:order_detail", order_id=order.id)

    plan_key = resolved_plan_key

    # Convert to NGN (Flutterwave prefers Naira)
    user_currency = request.session.get("currency", "USD")
    ngn_total = convert_currency(
        order.total, from_currency=user_currency, to_currency="NGN"
    )

    months = plan_details["months"]
    first_installment = round(ngn_total / months, 2)

    tx_ref = f"bx-install-{order.id}-{uuid.uuid4().hex[:10]}"

    payload = {
        "tx_ref": tx_ref,
        "amount": str(first_installment),
        "currency": "NGN",
        "payment_plan": plan_details["plan_code"],  # Important for auto-recurring
        "redirect_url": request.build_absolute_uri(
            f"/flutterwave/callback/?order_id={order.id}"
        ),
        "customer": {
            "email": request.user.email,
            "name": request.user.get_full_name() or request.user.username,
        },
        "meta": {
            "order_id": order.id,
            "payment_type": "installment",
            "total_amount": float(order.total),
            "months": months,
        },
        "customizations": {
            "title": f"Installment for Order #{order.id}",
            "description": f"{months} months plan - First payment",
        },
    }

    headers = {
        "Authorization": f"Bearer {settings.FLW_SECRET_KEY}",
        "Content-Type": "application/json",
    }
    # Validate amount against gateway limit before calling API
    try:
        max_limit = int(getattr(settings, 'FLUTTERWAVE_MAX_CHARGE_NGN', 0))
    except Exception:
        max_limit = 0

    if max_limit and first_installment > max_limit:
        messages.error(
            request,
            f"Charge amount must not exceed NGN {max_limit:,}. Please choose a different plan or contact support."
        )
        return redirect("shop:order_detail", order_id=order.id)

    response = requests.post(
        "https://api.flutterwave.com/v3/payments", json=payload, headers=headers
    )
    data = response.json()

    if response.status_code in (200, 201) and data.get("status") == "success":
        return redirect(data["data"]["link"])

    messages.error(request, "Failed to start installment. Please try again.")
    return redirect("shop:order_detail", order_id=order.id)


