from django.utils import timezone
from django.db import transaction
from xygaming_app.models import Coupons, CouponReedemBy, Product


def validate_and_get_coupon(code, product_id, user, cart_dict, is_distributor=False):
    """
    Validates whether a coupon code can be applied to a specific product (or cart item).
    Returns (is_valid: bool, message: str, coupon: Coupons|None, discount: float, target_product_id: int|None)
    """
    if not code:
        return False, "Please enter a coupon code.", None, 0.0, None

    code_clean = str(code).strip()
    coupon = Coupons.objects.filter(coupon_code__iexact=code_clean).first()
    if not coupon:
        return False, f"Coupon '{code_clean}' does not exist or is invalid.", None, 0.0, None

    # Check status, expiry, and total usage
    is_valid, msg = coupon.is_valid_now()
    if not is_valid:
        return False, msg, None, 0.0, None

    # Check user eligibility and per-user limits
    can_use, user_msg = coupon.can_user_use(user)
    if not can_use:
        return False, user_msg, None, 0.0, None

    # Check cart presence
    if not cart_dict:
        return False, "Your cart is empty.", None, 0.0, None

    # Determine target product
    target_pid = None
    if product_id:
        try:
            target_pid = int(product_id)
        except (TypeError, ValueError):
            target_pid = None

    if target_pid:
        if str(target_pid) not in cart_dict:
            return False, "The item specified for this coupon is not in your cart.", None, 0.0, None
        if not coupon.applies_to_product(target_pid):
            product_obj = Product.objects.filter(id=target_pid).first()
            prod_name = product_obj.name if product_obj else f"Item #{target_pid}"
            if coupon.product:
                return False, f"Coupon '{coupon.coupon_code}' is only valid for '{coupon.product.name}', not '{prod_name}'.", None, 0.0, None
            return False, f"Coupon '{coupon.coupon_code}' cannot be applied to '{prod_name}'.", None, 0.0, None
    else:
        # Auto-match with any qualifying item in cart
        for pid_str in cart_dict.keys():
            try:
                pid = int(pid_str)
                if coupon.applies_to_product(pid):
                    target_pid = pid
                    break
            except (TypeError, ValueError):
                continue

        if not target_pid:
            if coupon.product:
                return False, f"Coupon '{coupon.coupon_code}' is only valid for '{coupon.product.name}', which is not in your cart.", None, 0.0, None
            return False, f"None of the items in your cart qualify for coupon '{coupon.coupon_code}'.", None, 0.0, None

    # Compute discount on target item
    cart_item = cart_dict[str(target_pid)]
    qty = int(cart_item.get('quantity', 1))

    if is_distributor:
        prod_obj = Product.objects.filter(id=target_pid).first()
        unit_price = float(prod_obj.dprice) if (prod_obj and prod_obj.dprice) else float(cart_item.get('price', 0.0))
    else:
        unit_price = float(cart_item.get('price', 0.0))

    discount = coupon.calculate_discount_for_item(unit_price, qty)
    if discount <= 0:
        if coupon.min_purchase_amount > 0:
            return False, f"Minimum purchase amount of NPR {coupon.min_purchase_amount:.2f} required for this coupon.", None, 0.0, None
        return False, "This coupon does not provide a discount for this item.", None, 0.0, None

    target_obj = Product.objects.filter(id=target_pid).first()
    target_name = target_obj.name if target_obj else f"Item #{target_pid}"
    return True, f"Coupon '{coupon.coupon_code}' applied! Saved NPR {discount:.2f} on {target_name}.", coupon, discount, target_pid


def get_cart_coupon_summary(request, cart_dict, is_distributor=False):
    """
    Computes pristine cart subtotal, applies session coupon discount (if valid),
    and calculates 3% online wallet processing fee and final grand total.
    NEVER mutates session cart prices!
    """
    items_subtotal = 0.0
    if not cart_dict:
        return {
            'items_subtotal': 0.0,
            'coupon_discount': 0.0,
            'discounted_subtotal': 0.0,
            'processing_fee': 0.0,
            'grand_total': 0.0,
            'applied_coupon': None,
        }

    # Calculate actual items subtotal
    for key, item in cart_dict.items():
        qty = int(item.get('quantity', 1))
        if is_distributor:
            p_obj = Product.objects.filter(id=item.get('product_id')).first()
            price = float(p_obj.dprice) if (p_obj and p_obj.dprice) else float(item.get('price', 0.0))
        else:
            price = float(item.get('price', 0.0))
        items_subtotal += (price * qty)

    # Check for active applied coupon
    applied = request.session.get('coupon')
    coupon_discount = 0.0
    applied_coupon_data = None

    if applied and isinstance(applied, dict):
        target_pid = str(applied.get('product_id'))
        if target_pid in cart_dict:
            coupon = Coupons.objects.filter(id=applied.get('coupon_id')).first()
            if coupon:
                is_valid, _ = coupon.is_valid_now()
                can_use, _ = coupon.can_user_use(request.user)
                if is_valid and can_use and coupon.applies_to_product(target_pid):
                    target_item = cart_dict[target_pid]
                    qty = int(target_item.get('quantity', 1))
                    if is_distributor:
                        p_obj = Product.objects.filter(id=target_pid).first()
                        unit_price = float(p_obj.dprice) if (p_obj and p_obj.dprice) else float(target_item.get('price', 0.0))
                    else:
                        unit_price = float(target_item.get('price', 0.0))

                    discount = coupon.calculate_discount_for_item(unit_price, qty)
                    if discount > 0:
                        coupon_discount = discount
                        applied_coupon_data = dict(applied)
                        applied_coupon_data['discount_amount'] = discount
                        applied_coupon_data['code'] = coupon.coupon_code
                        request.session['coupon'] = applied_coupon_data
                    else:
                        clear_coupon_from_session(request)
                else:
                    clear_coupon_from_session(request)
            else:
                clear_coupon_from_session(request)
        else:
            # Target item removed from cart
            clear_coupon_from_session(request)

    items_subtotal = round(items_subtotal, 2)
    coupon_discount = round(coupon_discount, 2)
    discounted_subtotal = max(0.0, round(items_subtotal - coupon_discount, 2))
    processing_fee = round(0.03 * discounted_subtotal, 2)
    grand_total = round(discounted_subtotal + processing_fee, 2)

    return {
        'items_subtotal': items_subtotal,
        'coupon_discount': coupon_discount,
        'discounted_subtotal': discounted_subtotal,
        'processing_fee': processing_fee,
        'grand_total': grand_total,
        'applied_coupon': applied_coupon_data,
    }


def apply_coupon_to_session(request, coupon, target_product_id, discount_amount):
    """
    Stores coupon metadata in session without touching cart prices or database.
    """
    prod_obj = Product.objects.filter(id=target_product_id).first()
    prod_name = prod_obj.name if prod_obj else f"Item #{target_product_id}"

    request.session['coupon'] = {
        'coupon_id': coupon.id,
        'coupon_code': coupon.coupon_code,
        'code': coupon.coupon_code,
        'product_id': int(target_product_id),
        'product_name': prod_name,
        'discount_amount': float(discount_amount),
    }
    if hasattr(request.session, 'modified'):
        request.session.modified = True


def clear_coupon_from_session(request):
    """
    Safely purges any coupon session data.
    """
    if 'coupon' in request.session:
        del request.session['coupon']
    # Legacy session keys cleanup
    for legacy_key in ['discount', 'showdis', 'coupon_failed', 'product_id']:
        if legacy_key in request.session:
            try:
                del request.session[legacy_key]
            except KeyError:
                pass
    if hasattr(request.session, 'modified'):
        request.session.modified = True


def finalize_order_coupon(request, order, user):
    """
    Atomically records coupon redemption on the order and creates audit log
    when an order is placed.
    """
    applied = request.session.get('coupon')
    if not applied or not isinstance(applied, dict):
        return

    coupon_id = applied.get('coupon_id')
    if not coupon_id:
        return

    try:
        with transaction.atomic():
            coupon = Coupons.objects.select_for_update().filter(id=coupon_id).first()
            if not coupon:
                clear_coupon_from_session(request)
                return

            discount_amt = float(applied.get('discount_amount', 0.0))
            order.coupon = coupon
            order.coupon_code = coupon.coupon_code
            order.discount_amount = discount_amt
            order.save(update_fields=['coupon', 'coupon_code', 'discount_amount'])

            CouponReedemBy.objects.create(
                coupon=coupon,
                user=user if (user and user.is_authenticated) else None,
                order=order,
                discount_amount=discount_amt
            )

            coupon.used_count += 1
            coupon.save(update_fields=['used_count'])
    except Exception as e:
        print(f"Error finalizing order coupon: {e}")
    finally:
        clear_coupon_from_session(request)
