from decimal import Decimal

from django.contrib import messages
from django.shortcuts import get_object_or_404, redirect, render
from django.utils import timezone

from products.models import Product

from .models import Coupon


def get_cart(request):
    return request.session.get('cart', {})


def save_cart(request, cart):
    request.session['cart'] = cart
    request.session.modified = True


def _redirect_after_add(request):
    next_url = request.GET.get('next', '').strip()
    if next_url.startswith('/'):
        return redirect(next_url)
    return redirect('cart')


def add_to_cart(request, id):
    product = get_object_or_404(Product, id=id)
    cart = get_cart(request)
    current_qty = cart.get(str(id), 0)

    if current_qty + 1 > product.stock:
        messages.warning(request, f'Only {product.stock} units of {product.name} are available.')
    else:
        cart[str(id)] = current_qty + 1
        messages.success(request, f'Added {product.name} to your cart.')

    save_cart(request, cart)
    return _redirect_after_add(request)


def buy_now(request, id):
    product = get_object_or_404(Product, id=id)
    if product.stock <= 0:
        messages.error(request, f'{product.name} is out of stock.')
        return redirect('product_detail', product_id=product.id)

    request.session['buy_now'] = {str(product.id): 1}
    request.session.modified = True
    messages.info(request, f'Proceeding to checkout for {product.name}.')
    return redirect('checkout')


def update_cart(request, id):
    if request.method == 'POST':
        qty = int(request.POST.get('quantity', 1))
        product = get_object_or_404(Product, id=id)
        cart = get_cart(request)

        if qty <= 0:
            cart.pop(str(id), None)
            messages.info(request, f'{product.name} removed from cart.')
        elif qty > product.stock:
            cart[str(id)] = product.stock
            messages.warning(request, f'Quantity adjusted to {product.stock} for {product.name}.')
        else:
            cart[str(id)] = qty
            messages.success(request, f'Cart updated for {product.name}.')

        save_cart(request, cart)

    return redirect('cart')


def remove_from_cart(request, id):
    cart = get_cart(request)
    if str(id) in cart:
        del cart[str(id)]
        messages.info(request, 'Item removed from your cart.')
    save_cart(request, cart)
    return redirect('cart')


def _get_coupon_for_cart(code, subtotal):
    if not code:
        return None, Decimal('0')

    now = timezone.now()
    coupon = Coupon.objects.filter(code=code, active=True).first()
    if not coupon:
        return None, Decimal('0')

    if coupon.valid_from and now < coupon.valid_from:
        return None, Decimal('0')
    if coupon.valid_to and now > coupon.valid_to:
        return None, Decimal('0')
    if subtotal < coupon.min_purchase:
        return coupon, Decimal('0')

    discount = (subtotal * (coupon.discount_percent / Decimal('100'))).quantize(Decimal('0.01'))
    return coupon, discount


def apply_coupon(request):
    if request.method == 'POST':
        code = request.POST.get('coupon_code', '').strip().upper()
        request.session['coupon_code'] = code
        messages.success(request, f'Coupon {code} applied.')
    return redirect('cart')


def remove_coupon(request):
    request.session.pop('coupon_code', None)
    messages.info(request, 'Coupon removed.')
    return redirect('cart')


def cart_view(request):
    cart = get_cart(request)
    products = []
    subtotal = Decimal('0')

    for id, qty in cart.items():
        product = get_object_or_404(Product, id=int(id))
        product.qty = qty
        product.subtotal = product.display_price * qty
        subtotal += product.subtotal
        products.append(product)

    shipping = Decimal('60') if products else Decimal('0')

    coupon_code = request.session.get('coupon_code', '')
    coupon, discount = _get_coupon_for_cart(coupon_code, subtotal)
    if coupon_code and not coupon:
        request.session.pop('coupon_code', None)
        messages.warning(request, 'Coupon is invalid or expired.')
        coupon_code = ''

    if coupon and subtotal < coupon.min_purchase:
        messages.warning(request, f'Coupon requires minimum purchase of Rs. {coupon.min_purchase}.')
        discount = Decimal('0')

    grand_total = subtotal - discount + shipping

    return render(
        request,
        'cart/cart.html',
        {
            'products': products,
            'subtotal': subtotal,
            'total': subtotal,
            'shipping': shipping,
            'discount': discount,
            'grand_total': grand_total,
            'coupon_code': coupon_code,
            'coupon': coupon,
        },
    )
