from decimal import Decimal, InvalidOperation
import logging

import requests

from django.conf import settings
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.db import transaction
from django.db.models import Sum
from django.http import JsonResponse
from django.shortcuts import redirect, render
from django.template.loader import render_to_string
from django.urls import reverse
from django.utils.timezone import now
from django.views.decorators.http import require_GET, require_POST

from product_module.models import Product
from project_utils.shipping_utils import check_is_cod

from .models import (
    Address,
    City,
    Coupon,
    Order,
    OrderDetail,
    Payment,
    Province,
    ShippingMethod,
    ShippingOption,
)



logger = logging.getLogger(__name__)

def to_int(value):
    if value is None:
        return 0
    return int(value)
    
def get_order_total_items(order):
    """
    مجموع تعداد محصولات موجود در سفارش.

    مثال:
    محصول اول: 2 عدد
    محصول دوم: 3 عدد
    خروجی: 5
    """
    if order is None or order.pk is None:
        return 0

    result = order.orderdetail_set.aggregate(
        total=Sum("count")
    )

    return int(result["total"] or 0)
    
    
def is_cod_shipping(method, option):
    """
    تشخیص یکپارچه ارسال پس‌کرایه.
    اولویت با تابع استاندارد پروژه است تا هم method و هم option بررسی شوند.
    """
    try:
        return bool(check_is_cod(method, option))
    except Exception:
        logger.exception(
            "Error checking COD status: method_id=%s, option_id=%s",
            getattr(method, "id", None),
            getattr(option, "id", None),
        )
        return False
@login_required
@require_POST
def request_payment(request):
    """
    ایجاد درخواست پرداخت زرین‌پال برای آخرین سفارش پرداخت‌نشده
    و غیرخالی کاربر.
    """

    current_order = (
        Order.objects
        .filter(
            user=request.user,
            is_paid=False,
            orderdetail__isnull=False,
        )
        .select_related(
            "address",
            "address__city",
            "shipping_method",
            "shipping_option",
            "coupon",
        )
        .prefetch_related("orderdetail_set__product")
        .order_by("-id")
        .distinct()
        .first()
    )

    if current_order is None:
        unpaid_orders = list(
            Order.objects
            .filter(
                user=request.user,
                is_paid=False,
            )
            .order_by("-id")
            .values_list("id", flat=True)[:10]
        )

        logger.warning(
            "Payment rejected: no non-empty order found. "
            "user_id=%s, unpaid_order_ids=%s, post=%s",
            request.user.id,
            unpaid_orders,
            dict(request.POST),
        )

        messages.warning(
            request,
            "سبد خرید شما خالی است یا سفارش فعالی یافت نشد.",
        )
        return redirect("order:cart_detail")

    order_items = current_order.orderdetail_set.all()

    if not order_items.exists():
        logger.warning(
            "Payment rejected: selected order has no details. "
            "order_id=%s, user_id=%s",
            current_order.id,
            request.user.id,
        )
        messages.warning(request, "سبد خرید شما خالی است.")
        return redirect("order:cart_detail")

    logger.info(
        "Payment started: user_id=%s, order_id=%s, item_count=%s",
        request.user.id,
        current_order.id,
        order_items.count(),
    )

    def clean_post_value(*names):
        for name in names:
            value = (request.POST.get(name) or "").strip()
            if value and value.lower() not in {"undefined", "null", "none"}:
                return value
        return None

    post_address_id = clean_post_value("address_id")
    post_shipping_method_id = clean_post_value("shipping_method_id", "method_id")
    post_shipping_option_id = clean_post_value("shipping_option_id", "option_id")

    # ---------------------------------------------------------
    # انتخاب آدرس
    # ---------------------------------------------------------
    if post_address_id:
        selected_address = (
            Address.objects
            .select_related("city")
            .filter(
                pk=post_address_id,
                user=request.user,
            )
            .first()
        )

        if selected_address is None:
            messages.error(request, "آدرس انتخاب‌شده معتبر نیست.")
            return redirect("order:checkout")

        current_order.address = selected_address

    # ---------------------------------------------------------
    # انتخاب روش و گزینه ارسال
    # ---------------------------------------------------------
    selected_method = None
    selected_option = None

    # ۱. اعتبارسنجی روش ارسال در صورت ارسال شدن از POST
    if post_shipping_method_id:
        selected_method = (
            ShippingMethod.objects
            .filter(
                pk=post_shipping_method_id,
                active=True,
            )
            .first()
        )

        if selected_method is None:
            messages.error(request, "روش ارسال انتخاب‌شده معتبر نیست.")
            return redirect("order:checkout")

        current_order.shipping_method = selected_method
    else:
        selected_method = current_order.shipping_method

    # ۲. اعتبارسنجی گزینه ارسال
    if post_shipping_option_id:
        selected_option = (
            ShippingOption.objects
            .select_related("method")
            .filter(
                pk=post_shipping_option_id,
                active=True,
                method__active=True,
            )
            .first()
        )

        if selected_option is None:
            messages.error(request, "گزینه ارسال انتخاب‌شده معتبر نیست.")
            return redirect("order:checkout")

        if selected_method is not None and selected_option.method_id != selected_method.id:
            messages.error(request, "گزینه ارسال با روش انتخاب‌شده مطابقت ندارد.")
            return redirect("order:checkout")

        current_order.shipping_method = selected_option.method
        current_order.shipping_option = selected_option
    else:
        # اگر کاربر شناسه Option نفرستاده (برای روش‌های بدون Option)،
        # بررسی می‌کنیم آیا روش انتخاب‌شده اصلاً Option فعالی دارد یا خیر.
        if selected_method is not None:
            has_available_options = ShippingOption.objects.filter(
                method=selected_method,
                active=True,
            ).exists()

            if has_available_options:
                # فقط اگر این روش گزینه فرعی داشت ولی انتخاب نشده بود خطا دهد
                if not current_order.shipping_option_id:
                    messages.error(request, "لطفاً گزینه ارسال را انتخاب کنید.")
                    return redirect("order:checkout")
            else:
                # روش ارسال بدون گزینه فرعی است
                current_order.shipping_option = None

    current_order.save(
        update_fields=[
            "address",
            "shipping_method",
            "shipping_option",
        ]
    )

    current_order.refresh_from_db()

    # ---------------------------------------------------------
    # اعتبارسنجی سفارش
    # ---------------------------------------------------------
    if not current_order.address_id:
        messages.error(request, "لطفاً ابتدا آدرس خود را انتخاب کنید.")
        return redirect("order:checkout")

    if not current_order.address.city_id:
        messages.error(
            request,
            "شهر مقصد برای محاسبه هزینه ارسال مشخص نیست.",
        )
        return redirect("order:checkout")

    if not current_order.shipping_method_id:
        messages.error(request, "لطفاً ابتدا روش ارسال را انتخاب کنید.")
        return redirect("order:checkout")

    # ---------------------------------------------------------
    # محاسبه هزینه ارسال و مبلغ نهایی
    # ---------------------------------------------------------
    try:
        is_cod = is_cod_shipping(
            current_order.shipping_method,
            current_order.shipping_option,
        )

        if is_cod:
            # پس‌کرایه در پرداخت آنلاین محاسبه نمی‌شود.
            shipping_cost = Decimal("0")
        else:
            calculated_cost = (
                current_order.calculate_shipping_cost_for_option(
                    method=current_order.shipping_method,
                    option=current_order.shipping_option,
                )
            )

            if calculated_cost is None:
                logger.warning(
                    "No shipping tariff found: order_id=%s, "
                    "method_id=%s, option_id=%s, address_id=%s",
                    current_order.id,
                    current_order.shipping_method_id,
                    current_order.shipping_option_id,
                    current_order.address_id,
                )

                messages.error(
                    request,
                    "برای روش ارسال و مقصد انتخاب‌شده تعرفه‌ای پیدا نشد.",
                )
                return redirect("order:checkout")

            shipping_cost = Decimal(str(calculated_cost))

        if shipping_cost < Decimal("0"):
            messages.error(request, "هزینه ارسال نامعتبر است.")
            return redirect("order:checkout")

        current_order.shipping_cost = shipping_cost
        current_order.save(update_fields=["shipping_cost"])
        current_order.refresh_from_db()

        final_price = current_order.get_final_price()
        amount = int(Decimal(str(final_price)))

        logger.info(
            "Payment amount calculated: order_id=%s, "
            "products_price=%s, shipping=%s, final_price=%s, "
            "amount=%s, is_cod=%s",
            current_order.id,
            current_order.get_products_price(),
            shipping_cost,
            final_price,
            amount,
            is_cod,
        )

    except Exception:
        logger.exception(
            "Payment amount calculation failed: order_id=%s",
            current_order.id,
        )
        messages.error(request, "خطا در محاسبه مبلغ سفارش.")
        return redirect("order:checkout")

    if amount <= 0:
        logger.error(
            "Invalid payment amount: order_id=%s, amount=%s",
            current_order.id,
            amount,
        )
        messages.error(request, "مبلغ سفارش نامعتبر است.")
        return redirect("order:checkout")

    # ---------------------------------------------------------
    # تنظیمات زرین‌پال
    # ---------------------------------------------------------
    merchant_id = getattr(
        settings,
        "ZARINPAL_MERCHANT_ID",
        "",
    )

    sandbox = bool(
        getattr(
            settings,
            "ZARINPAL_SANDBOX",
            False,
        )
    )

    if not merchant_id:
        logger.error("ZARINPAL_MERCHANT_ID is empty.")
        messages.error(request, "تنظیمات درگاه پرداخت کامل نیست.")
        return redirect("order:checkout")

    callback_url = request.build_absolute_uri(reverse("order:verify_payment"))
    if callback_url.startswith("http://") and "localhost" not in callback_url and "127.0.0.1" not in callback_url:
        callback_url = callback_url.replace("http://", "https://", 1)

    print("ZARINPAL CALLBACK URL:", callback_url)

    user_mobile = str(
        getattr(request.user, "phone", None)
        or getattr(request.user, "mobile", None)
        or ""
    ).strip()
    
    user_email = str(
        getattr(request.user, "email", None)
        or ""
    ).strip()
    
    payload = {
        "merchant_id": merchant_id,
        "amount": amount,
        "callback_url": callback_url,
        "description": f"سفارش شماره #{current_order.id}",
    }

    logger.warning(
        "Zarinpal payload debug: order_id=%s, mobile=%r, mobile_type=%s, email=%r",
        current_order.id,
        user_mobile,
        type(user_mobile).__name__,
        user_email,
    )

    request_url = (
        "https://sandbox.zarinpal.com/pg/v4/payment/request.json"
        if sandbox
        else "https://api.zarinpal.com/pg/v4/payment/request.json"
    )

    # ---------------------------------------------------------
    # ارسال درخواست به زرین‌پال
    # ---------------------------------------------------------
    try:
        response = requests.post(
            request_url,
            json=payload,
            headers={
                "Content-Type": "application/json",
                "Accept": "application/json",
            },
            timeout=20,
        )

        logger.info(
            "Zarinpal response: order_id=%s, http_status=%s, body=%s",
            current_order.id,
            response.status_code,
            response.text[:3000],
        )

        try:
            result = response.json()
        except ValueError:
            logger.exception(
                "Zarinpal returned invalid JSON: order_id=%s",
                current_order.id,
            )
            messages.error(
                request,
                "پاسخ نامعتبر از درگاه پرداخت دریافت شد.",
            )
            return redirect("order:checkout")

        if not response.ok:
            logger.error(
                "Zarinpal HTTP error: order_id=%s, status=%s, result=%s",
                current_order.id,
                response.status_code,
                result,
            )
            messages.error(
                request,
                "درگاه پرداخت درخواست را قبول نکرد. "
                "تنظیمات درگاه را بررسی کنید.",
            )
            return redirect("order:checkout")

    except requests.Timeout:
        logger.exception(
            "Zarinpal timeout: order_id=%s",
            current_order.id,
        )
        messages.error(
            request,
            "زمان ارتباط با درگاه پرداخت به پایان رسید.",
        )
        return redirect("order:checkout")

    except requests.ConnectionError:
        logger.exception(
            "Zarinpal connection error: order_id=%s",
            current_order.id,
        )
        messages.error(
            request,
            "ارتباط با درگاه پرداخت برقرار نشد.",
        )
        return redirect("order:checkout")

    except requests.RequestException:
        logger.exception(
            "Zarinpal request error: order_id=%s",
            current_order.id,
        )
        messages.error(
            request,
            "خطا در ارتباط با درگاه پرداخت.",
        )
        return redirect("order:checkout")

    # ---------------------------------------------------------
    # بررسی پاسخ زرین‌پال
    # ---------------------------------------------------------
    gateway_data = result.get("data") or {}
    gateway_errors = result.get("errors") or {}

    authority = gateway_data.get("authority")
    gateway_code = gateway_data.get("code")

    if str(gateway_code) == "100" and authority:
        current_order.payment_authority = authority
        current_order.save(update_fields=["payment_authority"])

        redirect_url = (
            f"https://sandbox.zarinpal.com/pg/StartPay/{authority}"
            if sandbox
            else f"https://www.zarinpal.com/pg/StartPay/{authority}"
        )

        logger.info(
            "Zarinpal authority created: order_id=%s, authority=%s",
            current_order.id,
            authority,
        )

        return redirect(redirect_url)

    logger.error(
        "Zarinpal rejected payment: order_id=%s, code=%s, "
        "data=%s, errors=%s",
        current_order.id,
        gateway_code,
        gateway_data,
        gateway_errors,
    )

    gateway_message = (
        gateway_errors.get("message")
        or gateway_data.get("message")
        or "امکان ایجاد تراکنش در درگاه پرداخت فراهم نشد."
    )

    messages.error(request, str(gateway_message))
    return redirect("order:checkout")


@login_required
def verify_payment(request):
    authority = request.GET.get("Authority")
    status = request.GET.get("Status")

    if status != "OK":
        messages.error(request, "پرداخت ناموفق بود.")
        return redirect("order:checkout")

    try:
        current_order = Order.objects.get(payment_authority=authority, is_paid=False)
    except Order.DoesNotExist:
        messages.error(request, "سفارش یافت نشد یا قبلاً تأیید شده است.")
        return redirect("order:checkout")

    data = {
        "merchant_id": settings.ZARINPAL_MERCHANT_ID,
        "amount": int(current_order.get_final_price()),
        "authority": authority,
    }
    
    verify_url = "https://sandbox.zarinpal.com/pg/v4/payment/verify.json" if settings.ZARINPAL_SANDBOX else "https://api.zarinpal.com/pg/v4/payment/verify.json"

    try:
        response = requests.post(verify_url, json=data, timeout=15)
        result = response.json()
    except Exception as e:
        messages.error(request, "خطا در تایید تراکنش.")
        return redirect("order:checkout")

    code = result.get("data", {}).get("code")
    if code in [100, 101]:
        current_order.is_paid = True
        current_order.save(update_fields=["is_paid"])
        messages.success(request, "پرداخت با موفقیت انجام شد.")
        return redirect("order:checkout")

    messages.error(request, "پرداخت تأیید نشد.")
    return redirect("order:checkout")

@login_required
@require_POST
def add_product_to_order(request, product_id):
    """
    افزودن یک واحد محصول به آخرین سفارش پرداخت‌نشده کاربر.
    """

    product = (
        Product.objects
        .filter(
            pk=product_id,
            is_active=True,
        )
        .first()
    )

    if product is None:
        return JsonResponse(
            {
                "status": "error",
                "message": "محصول یافت نشد یا غیرفعال است.",
            },
            status=404,
        )

    try:
        with transaction.atomic():
            # آخرین سفارش پرداخت‌نشده کاربر را می‌گیریم.
            # استفاده از get_or_create در صورت وجود چند سفارش باز
            # می‌تواند باعث MultipleObjectsReturned شود.
            order = (
                Order.objects
                .select_for_update()
                .filter(
                    user=request.user,
                    is_paid=False,
                )
                .order_by("-id")
                .first()
            )

            if order is None:
                order = Order.objects.create(
                    user=request.user,
                    is_paid=False,
                )

            detail = (
                OrderDetail.objects
                .select_for_update()
                .filter(
                    order=order,
                    product=product,
                )
                .first()
            )

            product_stock = int(product.stock or 0)

            if detail is not None:
                current_count = int(detail.count or 0)

                if current_count + 1 > product_stock:
                    return JsonResponse(
                        {
                            "status": "error",
                            "message": "موجودی کافی نیست.",
                        },
                        status=400,
                    )

                detail.count = current_count + 1
                detail.save(update_fields=["count"])

            else:
                if product_stock < 1:
                    return JsonResponse(
                        {
                            "status": "error",
                            "message": "موجودی محصول تمام شده است.",
                        },
                        status=400,
                    )

                OrderDetail.objects.create(
                    order=order,
                    product=product,
                    count=1,
                    final_price=product.price,
                )

            products_price = order.get_products_price()
            tax = order.get_tax()
            shipping = order.get_shipping_cost()
            final_price = order.get_final_price()
            total_items = get_order_total_items(order)

        return JsonResponse({
            "status": "success",
            "message": "محصول به سبد خرید اضافه شد.",
            "total_items": total_items,
            "products_price": to_int(products_price),
            "tax": to_int(tax),
            "shipping": to_int(shipping),
            "final_price": to_int(final_price),
        })

    except Exception:
        logger.exception(
            "Add product to order failed: user_id=%s, product_id=%s",
            request.user.id,
            product_id,
        )

        return JsonResponse(
            {
                "status": "error",
                "message": "خطای داخلی در افزودن محصول به سبد خرید.",
            },
            status=500,
        )


@login_required
@require_POST
def remove_order_detail(request, detail_id):
    """
    حذف کامل یک ردیف از سبد خرید.
    """

    try:
        with transaction.atomic():
            detail = (
                OrderDetail.objects
                .select_related("order", "product")
                .select_for_update()
                .filter(
                    pk=detail_id,
                    order__user=request.user,
                    order__is_paid=False,
                )
                .first()
            )

            if detail is None:
                return JsonResponse(
                    {
                        "status": "error",
                        "message": "آیتم یافت نشد.",
                    },
                    status=404,
                )

            order = detail.order
            detail.delete()

            products_price = order.get_products_price()
            tax = order.get_tax()
            shipping = order.get_shipping_cost()
            final_price = order.get_final_price()
            total_items = get_order_total_items(order)

        return JsonResponse({
            "status": "success",
            "message": "محصول از سبد خرید حذف شد.",
            "products_price": to_int(products_price),
            "tax": to_int(tax),
            "shipping": to_int(shipping),
            "final_price": to_int(final_price),
            "total_items": total_items,
        })

    except Exception:
        logger.exception(
            "Remove order detail failed: user_id=%s, detail_id=%s",
            request.user.id,
            detail_id,
        )

        return JsonResponse(
            {
                "status": "error",
                "message": "خطای داخلی در حذف محصول از سبد خرید.",
            },
            status=500,
        )


@login_required
@require_POST
def change_order_detail_count(request):
    """
    افزایش یا کاهش تعداد یک محصول در سبد خرید.
    """

    detail_id = (request.POST.get("detail_id") or "").strip()
    state = (request.POST.get("state") or "").strip()

    if not detail_id or not state:
        return JsonResponse(
            {
                "status": "error",
                "message": "اطلاعات ناقص است.",
            },
            status=400,
        )

    if state not in {"1", "-1"}:
        return JsonResponse(
            {
                "status": "error",
                "message": "وضعیت نامعتبر است.",
            },
            status=400,
        )

    try:
        with transaction.atomic():
            detail = (
                OrderDetail.objects
                .select_related("order", "product")
                .select_for_update()
                .filter(
                    pk=detail_id,
                    order__user=request.user,
                    order__is_paid=False,
                )
                .first()
            )

            if detail is None:
                return JsonResponse(
                    {
                        "status": "error",
                        "message": "آیتم یافت نشد.",
                    },
                    status=404,
                )

            current_count = int(detail.count or 0)
            product_stock = int(detail.product.stock or 0)

            if state == "1":
                if current_count >= product_stock:
                    return JsonResponse(
                        {
                            "status": "error",
                            "message": "موجودی محصول کافی نیست.",
                        },
                        status=400,
                    )

                detail.count = current_count + 1

            else:
                if current_count <= 1:
                    return JsonResponse(
                        {
                            "status": "error",
                            "message": "تعداد محصول نمی‌تواند کمتر از ۱ باشد.",
                        },
                        status=400,
                    )

                detail.count = current_count - 1

            detail.save(update_fields=["count"])

            order = detail.order
            item_total = detail.get_total_price()

            products_price = order.get_products_price()
            tax = order.get_tax()
            shipping = order.get_shipping_cost()
            final_price = order.get_final_price()
            total_items = get_order_total_items(order)

        return JsonResponse({
            "status": "success",
            "new_count": detail.count,
            "new_price": to_int(item_total),
            "total_items": total_items,
            "products_price": to_int(products_price),
            "tax": to_int(tax),
            "shipping": to_int(shipping),
            "final_price": to_int(final_price),

            # برای سازگاری با JavaScript فعلی
            "count": detail.count,
            "item_total": to_int(item_total),
        })

    except Exception:
        logger.exception(
            "Change order detail count failed: "
            "user_id=%s, detail_id=%s, state=%s",
            request.user.id,
            detail_id,
            state,
        )

        return JsonResponse(
            {
                "status": "error",
                "message": "خطای داخلی در تغییر تعداد محصول.",
            },
            status=500,
        )


@login_required
def cart_detail(request):
    try:
        order = (
            Order.objects
            .filter(
                user=request.user,
                is_paid=False,
            )
            .prefetch_related("orderdetail_set__product")
            .order_by("-id")
            .first()
        )

        provinces = Province.objects.all()

        user_addresses = (
            Address.objects
            .filter(user=request.user)
            .select_related("province", "city")
        )

        if order is None:
            shipping_methods = (
                ShippingMethod.objects
                .filter(active=True)
                .exclude(code="barbari")
                .prefetch_related("options")
            )

            context = {
                "order": None,
                "order_items": [],
                "shipping_methods": shipping_methods,
                "products_price": Decimal("0"),
                "tax_price": Decimal("0"),
                "shipping_price": Decimal("0"),
                "final_price": Decimal("0"),
                "total_weight": 0,
                "provinces": provinces,
                "user_addresses": user_addresses,
            }

            return render(
                request,
                "order_module/cart_detail.html",
                context,
            )

        order_items = (
            order.orderdetail_set
            .select_related("product")
            .all()
        )

        shipping_methods = (
            order
            .get_suitable_shipping_methods()
            .prefetch_related("options")
        )

        products_price = order.get_products_price()
        tax_price = order.get_tax()
        shipping_price = order.get_shipping_cost()
        final_price = order.get_final_price()
        total_weight = order.get_total_weight()

        context = {
            "order": order,
            "order_items": order_items,
            "shipping_methods": shipping_methods,
            "products_price": products_price,
            "tax_price": tax_price,
            "shipping_price": shipping_price,
            "final_price": final_price,
            "total_weight": total_weight,
            "provinces": provinces,
            "user_addresses": user_addresses,
        }

        return render(
            request,
            "order_module/cart_detail.html",
            context,
        )

    except Exception:
        logger.exception(
            "Cart detail failed: user_id=%s, path=%s",
            request.user.id,
            request.path,
        )
        raise



def load_cities(request):
    province_id = request.GET.get("province_id")
    cities = City.objects.filter(province_id=province_id).values("id", "name")
    return JsonResponse(list(cities), safe=False)


@login_required
@require_POST
def add_address_ajax(request):
    """
    ثبت آدرس جدید کاربر با نرمال‌سازی دقیق و ایمن ارقام
    """
    
    def normalize_digits(value):
        if not value: return ""
        mapping = str.maketrans("۰۱۲۳۴۵۶۷۸۹٠١٢٣٤٥٦٧٨٩", "01234567890123456789")
        clean_val = str(value).translate(mapping)
        return "".join(filter(str.isdigit, clean_val))

    # --- اصلاح شده: تغییر نام فیلدها از province/city به province_id/city_id ---
    province_id = normalize_digits(request.POST.get("province_id", ""))
    city_id = normalize_digits(request.POST.get("city_id", ""))
    # --------------------------------------------------------------------------
    
    address_detail = request.POST.get("address_detail", "").strip()
    postal_code = normalize_digits(request.POST.get("postal_code", ""))
    receiver_name = request.POST.get("receiver_name", "").strip()
    receiver_phone = normalize_digits(request.POST.get("receiver_phone", ""))

    # اکنون این چک به درستی فیلدها را پر تشخیص می‌دهد
    if not all([province_id, city_id, address_detail, postal_code, receiver_name, receiver_phone]):
        return JsonResponse({
            "status": "error", 
            "message": "لطفاً تمامی فیلدهای ستاره‌دار را با دقت تکمیل کنید."
        }, status=400)

    # ۳. اعتبارسنجی دقیق کد پستی
    if len(postal_code) != 10:
        return JsonResponse({
            "status": "error",
            "message": "کد پستی باید دقیقاً ۱۰ رقم باشد."
        }, status=400)

    # ۴. اعتبارسنجی موبایل گیرنده
    if len(receiver_phone) != 11 or not receiver_phone.startswith("09"):
        return JsonResponse({
            "status": "error",
            "message": "شماره موبایل گیرنده باید ۱۱ رقم و با 09 شروع شود."
        }, status=400)

    try:
        # تبدیل IDها به عدد برای کوئری دیتابیس
        province = Province.objects.get(pk=int(province_id))
        city = City.objects.get(pk=int(city_id))

        if city.province_id != province.id:
            return JsonResponse({"status": "error", "message": "شهر با استان مطابقت ندارد."}, status=400)

        address = Address.objects.create(
            user=request.user,
            province=province,
            city=city,
            address_detail=address_detail,
            postal_code=postal_code,
            receiver_name=receiver_name,
            receiver_phone=receiver_phone
        )

        order = Order.objects.filter(user=request.user, is_paid=False).first()
        if order:
            order.address = address
            order.shipping_method = None
            order.shipping_option = None
            order.save(update_fields=["address", "shipping_method", "shipping_option"])

        return JsonResponse({
            "status": "success",
            "message": "آدرس جدید ثبت شد.",
            "address_id": address.id
        })

    except (Province.DoesNotExist, City.DoesNotExist):
        return JsonResponse({"status": "error", "message": "استان یا شهر معتبر نیست."}, status=400)
    except Exception as e:
        # استفاده از logger استاندارد جنگو (اطمینان حاصل کنید import logger انجام شده)
        return JsonResponse({"status": "error", "message": "خطای داخلی سرور."}, status=500)
        
    

@login_required
def checkout_view(request):
    # 1. دریافت آخرین سفارش پرداخت‌نشده کاربر
    order = (
        Order.objects
        .filter(
            user=request.user,
            is_paid=False,
            orderdetail__isnull=False,
        )
        .select_related(
            "address__province",
            "address__city",
            "shipping_method",
            "shipping_option",
            "coupon",
        )
        .prefetch_related("orderdetail_set__product")
        .order_by("-id")
        .distinct()
        .first()
    )


    # 2. در صورت نبود سفارش یا خالی بودن سبد، هدایت به صفحه سبد خرید
    if not order or not order.orderdetail_set.exists():
        messages.warning(request, "سبد خرید شما خالی است.")
        return redirect(reverse("order:cart_detail"))

    # 3. همگام‌سازی قیمت‌ها با دیتابیس محصولات
    try:
        order.sync_prices()
        order.refresh_from_db()
    except Exception as e:
        logger.warning(f"Error syncing prices for order {order.id}: {e}")

       # 4. همگام‌سازی هزینه ارسال
    # در پس‌کرایه، مبلغ ارسال آنلاین صفر است و تعرفه لازم نیست.
    if order.shipping_method:
        try:
            if not order.address_id or not order.address.city_id:
                order.shipping_method = None
                order.shipping_option = None
                order.shipping_cost = Decimal("0")
                order.save(
                    update_fields=[
                        "shipping_method",
                        "shipping_option",
                        "shipping_cost",
                    ]
                )

            else:
                is_cod = is_cod_shipping(
                    order.shipping_method,
                    order.shipping_option,
                )

                if is_cod:
                    order.shipping_cost = Decimal("0")
                    order.save(update_fields=["shipping_cost"])

                else:
                    calculated_cost = order.calculate_shipping_cost_for_option(
                        method=order.shipping_method,
                        option=order.shipping_option,
                    )

                    if calculated_cost is None:
                        # تعرفه معتبر نیست؛ انتخاب قبلی را پاک می‌کنیم.
                        order.shipping_method = None
                        order.shipping_option = None
                        order.shipping_cost = Decimal("0")
                        order.save(
                            update_fields=[
                                "shipping_method",
                                "shipping_option",
                                "shipping_cost",
                            ]
                        )
                    else:
                        order.shipping_cost = Decimal(str(calculated_cost))
                        order.save(update_fields=["shipping_cost"])

                order.refresh_from_db()

        except Exception:
            logger.exception(
                "Error updating shipping cost in checkout: order_id=%s",
                order.id,
            )


    # 5. استخراج اطلاعات جهت ارسال به قالب
    user_addresses = (
        Address.objects.filter(user=request.user)
        .select_related("province", "city")
        .order_by("-id")
    )
    provinces = Province.objects.all()
    shipping_methods = order.get_suitable_shipping_methods().prefetch_related("options")
    order_items = order.orderdetail_set.select_related("product").all()

    context = {
        "order": order,
        "order_items": order_items,
        "user_addresses": user_addresses,
        "provinces": provinces,
        "shipping_methods": shipping_methods,
        "products_price": to_int(order.get_products_price()),
        "shipping_price": to_int(order.get_shipping_cost()),
        "tax_price": to_int(order.get_tax()),
        "coupon_discount": to_int(order.get_coupon_discount()),
        "final_price": to_int(order.get_final_price()),
    }
    return render(request, "order_module/checkout.html", context)


def _build_shipping_item(order, method, option):
    """
    ساخت یک گزینه ارسال برای قالب shipping_methods.html.

    قوانین:
    - روش پس‌کرایه همیشه قابل نمایش است.
    - روش عادی فقط در صورت وجود نرخ معتبر نمایش داده می‌شود.
    - گزینه ShippingOption از فیلد title استفاده می‌کند، نه name.
    """

    if method is None or not getattr(method, "active", False):
        return None

    # اطمینان از تعلق گزینه به روش انتخاب‌شده
    if option is not None:
        if not getattr(option, "active", False):
            return None

        if option.method_id != method.pk:
            return None

    is_cod = order.is_shipping_cod(method, option)

    option_title = ""
    if option is not None:
        option_title = str(
            getattr(option, "title", "") or ""
        ).strip()

    method_title = str(
        getattr(method, "name", "") or ""
    ).strip()

    title = method_title
    if option_title:
        title = f"{method_title} - {option_title}"

    # پس‌کرایه: هزینه آنلاین صفر است و نیاز به تعرفه ندارد.
    if is_cod:
        return {
            "method": method,
            "option": option,
            "title": title,
            "is_cod": True,
            "price_available": True,
            "price": None,
            "selected": False,
        }

    try:
        cost = order.calculate_shipping_cost_for_option(
            method=method,
            option=option,
        )
    except Exception:
        logger.exception(
            "Shipping cost calculation failed: order_id=%s, "
            "method_id=%s, option_id=%s",
            getattr(order, "id", None),
            getattr(method, "id", None),
            getattr(option, "id", None),
        )
        return None

    # روش عادی بدون نرخ معتبر نباید نمایش داده شود.
    if cost is None:
        return None

    try:
        cost = Decimal(str(cost))
    except (InvalidOperation, TypeError, ValueError):
        logger.warning(
            "Invalid shipping cost: order_id=%s, method_id=%s, option_id=%s",
            getattr(order, "id", None),
            getattr(method, "id", None),
            getattr(option, "id", None),
        )
        return None

    if cost < Decimal("0"):
        return None

    return {
        "method": method,
        "option": option,
        "title": title,
        "is_cod": False,
        "price_available": True,
        "price": cost,
        "selected": False,
    }


@login_required
@require_GET
def get_shipping_methods(request):
    address_id = request.GET.get("address_id")
    context = {}

    address = None
    if address_id:
        address = Address.objects.filter(
            pk=address_id,
            user=request.user,
        ).first()

    if address_id and address is None:
        context["error"] = "آدرس انتخاب‌شده معتبر نیست."
        html = render_to_string(
            "order_module/includes/shipping_methods.html",
            context,
            request=request,
        )
        return JsonResponse({"success": False, "html": html})

    order = (
        Order.objects
        .filter(user=request.user, is_paid=False)
        .order_by("-id")
        .first()
    )

    if order is None:
        context["error"] = "سبد خرید یافت نشد."
        html = render_to_string(
            "order_module/includes/shipping_methods.html",
            context,
            request=request,
        )
        return JsonResponse({"success": False, "html": html})

    if address is not None and order.address_id != address.id:
        order.address = address
        order.shipping_method = None
        order.shipping_option = None
        order.shipping_cost = Decimal("0")
    
        order.save(
            update_fields=[
                "address",
                "shipping_method",
                "shipping_option",
                "shipping_cost",
            ]
        )


    shipping_items = []

    for method in order.get_suitable_shipping_methods():
        options = list(method.options.filter(active=True).order_by("id"))

        if options:
            for option in options:
                item = _build_shipping_item(order, method, option)
                if item is not None:
                    shipping_items.append(item)
        else:
            item = _build_shipping_item(order, method, None)
            if item is not None:
                shipping_items.append(item)

    # حفظ انتخاب قبلی اگر هنوز معتبر است؛ وگرنه اولین گزینه معتبر
    selected_item = None

    if order.shipping_method_id is not None:
        for item in shipping_items:
            if item["method"].pk != order.shipping_method_id:
                continue

            if item["option"] is None and order.shipping_option_id is None:
                selected_item = item
                break

            if (
                item["option"] is not None
                and item["option"].pk == order.shipping_option_id
            ):
                selected_item = item
                break

    if selected_item is None and shipping_items:
        selected_item = shipping_items[0]

    if selected_item is not None:
        selected_item["selected"] = True

    context = {
        "order": order,
        "shipping_items": shipping_items,
    }

    html = render_to_string(
        "order_module/includes/shipping_methods.html",
        context,
        request=request,
    )

    return JsonResponse({
        "success": True,
        "address_id": address_id,
        "html": html,
        "method_id": selected_item["method"].pk if selected_item else None,
        "option_id": (
            selected_item["option"].pk
            if selected_item and selected_item["option"] is not None
            else None
        ),
    })


@login_required
@require_POST
def select_shipping_method(request):
    """
    ثبت روش ارسال انتخابی روی سفارش جاری کاربر.

    - اعتبارسنجی آدرس، روش و گزینه
    - ذخیره روی سفارش
    - محاسبه هزینه ارسال و بازگرداندن خلاصه سبد
    """
    address_id = request.POST.get("address_id")
    method_id = request.POST.get("method_id")
    option_id = request.POST.get("option_id") or None

    address = None
    if address_id:
        address = Address.objects.filter(
            pk=address_id,
            user=request.user,
        ).first()

    if address is None:
        return JsonResponse({
            "success": False,
            "message": "آدرس انتخاب‌شده معتبر نیست.",
        }, status=400)

    method = None
    if method_id:
        method = ShippingMethod.objects.filter(
            pk=method_id,
            active=True,
        ).first()

    if method is None:
        return JsonResponse({
            "success": False,
            "message": "روش ارسال انتخاب‌شده معتبر نیست.",
        }, status=400)

    option = None
    if option_id:
        option = ShippingOption.objects.filter(
            pk=option_id,
            active=True,
            method_id=method.pk,
        ).first()

        if option is None:
            return JsonResponse({
                "success": False,
                "message": "گزینه ارسال انتخاب‌شده معتبر نیست.",
            }, status=400)

    order = (
        Order.objects
        .filter(user=request.user, is_paid=False)
        .order_by("-id")
        .first()
    )

    if order is None:
        return JsonResponse({
            "success": False,
            "message": "سبد خرید یافت نشد.",
        }, status=404)

    # آدرس را روی سفارش می‌گذاریم تا محاسبات روی مقصد درست انجام شود
    order.address = address

    # اطمینان از اینکه روش واقعاً برای این مقصد در دسترس است
    is_valid_method = (
        order.get_suitable_shipping_methods()
        .filter(pk=method.pk)
        .exists()
    )

    if not is_valid_method:
        return JsonResponse({
            "success": False,
            "message": "این روش ارسال برای این مقصد در دسترس نیست.",
        }, status=400)

    order.shipping_method = method
    order.shipping_option = option
    order.save(update_fields=[
        "address",
        "shipping_method",
        "shipping_option",
    ])

    cost = order.update_shipping_cost(save=True)

    is_cod = order.is_shipping_cod(method, option)

    if is_cod:
        shipping_cost = 0
        shipping_text = "پس‌کرایه؛ هزینه در مقصد دریافت می‌شود"
    else:
        if cost is None:
            return JsonResponse({
                "success": False,
                "message": "امکان محاسبه هزینه ارسال برای این روش وجود ندارد.",
            }, status=400)

        shipping_cost = int(cost)
        shipping_text = f"{shipping_cost:,} تومان"

    order.refresh_from_db()

    tax = order.get_tax()
    products_price = order.get_products_price()
    coupon_discount = order.get_coupon_discount()
    final_price = order.get_final_price()


    return JsonResponse({
        "success": True,
        "message": "روش ارسال با موفقیت ثبت شد.",
        "address_id": address.pk,
        "method_id": method.pk,
        "option_id": option.pk if option else None,
        "shipping_cost": to_int(shipping_cost),
        "shipping_text": shipping_text,
        "products_price": to_int(products_price),
        "tax": to_int(tax),
        "coupon_discount": to_int(coupon_discount),
        "final_price": to_int(final_price),
    })


# =========================================================================
# پیاده‌سازی متد حذف کوپن تخفیف
# =========================================================================
@login_required
@require_POST
def remove_coupon(request):
    order = Order.objects.filter(user=request.user, is_paid=False).first()
    if not order:
        return JsonResponse({'status': 'error', 'message': 'سفارش فعالی یافت نشد.'}, status=404)
        
    order.coupon = None
    order.save()
    
    # به‌روزرسانی هزینه‌ها پس از حذف کوپن
    if order.shipping_method:
        try:
            order.update_shipping_cost()
        except Exception:
            pass

    return JsonResponse({
        'status': 'success',
        'message': 'کد تخفیف با موفقیت حذف شد.',
        'final_price': to_int(order.get_final_price())
    })
@login_required
@require_POST
def remove_from_checkout(request):
    """
    حذف یک محصول از صفحه تسویه حساب.
    """

    detail_id = (request.POST.get("detail_id") or "").strip()

    if not detail_id:
        return JsonResponse(
            {
                "status": "error",
                "message": "شناسه آیتم ارسال نشده است.",
            },
            status=400,
        )

    try:
        with transaction.atomic():
            detail = (
                OrderDetail.objects
                .select_related("order")
                .select_for_update()
                .filter(
                    pk=detail_id,
                    order__user=request.user,
                    order__is_paid=False,
                )
                .first()
            )

            if detail is None:
                return JsonResponse(
                    {
                        "status": "error",
                        "message": "آیتم یافت نشد.",
                    },
                    status=404,
                )

            order = detail.order
            detail.delete()

            products_price = order.get_products_price()
            tax = order.get_tax()
            shipping = order.get_shipping_cost()
            final_price = order.get_final_price()
            total_items = get_order_total_items(order)

        return JsonResponse({
            "status": "success",
            "message": "محصول از سفارش حذف شد.",
            "products_price": to_int(products_price),
            "tax": to_int(tax),
            "shipping": to_int(shipping),
            "final_price": to_int(final_price),
            "total_items": total_items,
            "is_empty": total_items == 0,
        })

    except Exception:
        logger.exception(
            "Remove from checkout failed: user_id=%s, detail_id=%s",
            request.user.id,
            detail_id,
        )

        return JsonResponse(
            {
                "status": "error",
                "message": "خطای داخلی در حذف محصول از سفارش.",
            },
            status=500,
        )

@login_required
@require_POST
def apply_coupon(request):
    code = request.POST.get('coupon_code')
    now_time = now()
    
    # پیدا کردن کوپن فعال و معتبر
    coupon = Coupon.objects.filter(
        code__iexact=code,
        valid_from__lte=now_time,
        valid_to__gte=now_time,
        active=True
    ).first()
    
    if not coupon:
        return JsonResponse({'status': 'error', 'message': 'کد تخفیف نامعتبر یا منقضی شده است.'})
        
    order = Order.objects.filter(user=request.user, is_paid=False).first()
    if not order:
        return JsonResponse({'status': 'error', 'message': 'سفارش فعالی یافت نشد.'})
        
    order.coupon = coupon
    order.save()
    
    # هزینه ارسال و نهایی را مجدد محاسبه کنید
    order.update_shipping_cost() 
    
    return JsonResponse({
        'status': 'success',
        'message': 'کد تخفیف با موفقیت اعمال شد.',
        'discount_amount': to_int(order.get_coupon_discount()), # متدی که باید در مدل Order بنویسید
        'final_price': to_int(order.get_final_price())
    })
