import json
from decimal import Decimal
from django.db.models import F
from django.http import JsonResponse
from django.shortcuts import render, get_object_or_404
from django.utils import timezone
from django.views import View
from sales.models import Sale, SaleItem
from orders.models import Order, OrderItem
from payments.models import Payment
from payments.paystack import PaystackClient
from businesses.models import DeliveryOption
class SaleDetailView(View):
    """Public sale page at /sale/<slug>/. Behavior gated by sale status."""
    def get(self, request, slug):
        Order.release_expired_pending_orders()
        sale = get_object_or_404(Sale.objects.select_related('business'), slug=slug)
        # Increment view count
        Sale.objects.filter(pk=sale.pk).update(view_count=F('view_count') + 1)
        # Check and transition status if needed
        sale.check_and_transition()
        sale.refresh_from_db()
        items = sale.items.select_related('product').prefetch_related('product__images')
        delivery_options = sale.business.delivery_options.filter(is_active=True)
        return render(request, 'sales/detail.html', {
            'sale': sale,
            'items': items,
            'delivery_options': delivery_options,
            'now': timezone.now(),
        })
class DiscoveryView(View):
    """Discovery page at /sales/. Grid of live + upcoming + ended sales."""
    def get(self, request):
        Order.release_expired_pending_orders()
        Sale.update_all_statuses()
        sales = Sale.objects.filter(
            status__in=['live', 'upcoming', 'ended']
        ).select_related('business').prefetch_related(
            'items__product__images'
        ).order_by(
            models_case_ordering(),
            '-start_at'
        )
        return render(request, 'sales/discovery.html', {
            'sales': sales,
        })
def models_case_ordering():
    from django.db.models import Case, When, Value, IntegerField
    return Case(
        When(status='live', then=Value(0)),
        When(status='upcoming', then=Value(1)),
        When(status='ended', then=Value(2)),
        default=Value(3),
        output_field=IntegerField(),
    )
class CheckoutView(View):
    """POST: Guest checkout -- create Order + Paystack session with Delivery Option."""
    def post(self, request, slug):
        sale = get_object_or_404(Sale.objects.select_related('business'), slug=slug)
        if sale.status != 'live':
            return JsonResponse(
                {'error': 'This sale is not currently active.'},
                status=400
            )
        try:
            data = json.loads(request.body)
        except json.JSONDecodeError:
            return JsonResponse({'error': 'Invalid JSON.'}, status=400)
        buyer_name = data.get('buyer_name', '').strip()
        buyer_phone = data.get('buyer_phone', '').strip()
        buyer_email = data.get('buyer_email', '').strip()
        cart_items = data.get('items', [])
        delivery_option_id = data.get('delivery_option_id')
        delivery_address = data.get('delivery_address', '').strip()
        if not all([buyer_name, buyer_phone, buyer_email]):
            return JsonResponse({'error': 'Name, phone, and email are required.'}, status=400)
        if not cart_items:
            return JsonResponse({'error': 'Cart is empty.'}, status=400)
        # Validate delivery option if seller has delivery options configured
        delivery_fee = Decimal('0.00')
        delivery_option_obj = None
        if delivery_option_id:
            try:
                delivery_option_obj = DeliveryOption.objects.get(
                    id=delivery_option_id,
                    business=sale.business,
                    is_active=True
                )
                delivery_fee = delivery_option_obj.fee
            except DeliveryOption.DoesNotExist:
                return JsonResponse({'error': 'Invalid delivery option selected.'}, status=400)
        # Build cart item ids list for locking
        sale_item_ids = []
        cart_qty_map = {}
        for cart_item in cart_items:
            try:
                sid = cart_item['sale_item_id']
                qty = int(cart_item.get('quantity', 1))
            except (KeyError, ValueError, TypeError):
                return JsonResponse({'error': 'Invalid item in cart.'}, status=400)
            if qty < 1:
                return JsonResponse({'error': 'Quantity must be at least 1.'}, status=400)
            sale_item_ids.append(sid)
            cart_qty_map[sid] = qty

        # -- Atomic stock check, reservation + order creation to prevent race conditions --
        # select_for_update() locks the SaleItem rows so concurrent requests
        # queue up and evaluate remaining stock sequentially.
        try:
            with transaction.atomic():
                Order.release_expired_pending_orders()

                locked_items = (
                    SaleItem.objects
                    .select_for_update()
                    .filter(id__in=sale_item_ids, sale=sale)
                    .select_related('product')
                )

                if locked_items.count() != len(sale_item_ids):
                    return JsonResponse({'error': 'One or more items not found in this sale.'}, status=400)

                items_total = Decimal('0.00')
                order_items_data = []

                for sale_item in locked_items:
                    sid = str(sale_item.id)
                    quantity = cart_qty_map[sid]
                    if sale_item.stock_remaining < quantity:
                        raise ValueError(
                            f'"{sale_item.product.name}" only has {sale_item.stock_remaining} unit(s) left in stock.'
                        )
                    line_total = sale_item.price * quantity
                    items_total += line_total
                    order_items_data.append({
                        'sale_item': sale_item,
                        'quantity': quantity,
                        'unit_price': sale_item.price,
                    })

                # Atomically reserve stock for this pending order
                for item_data in order_items_data:
                    SaleItem.objects.filter(id=item_data['sale_item'].id).update(
                        stock_remaining=F('stock_remaining') - item_data['quantity']
                    )

                grand_total = items_total + delivery_fee

                order = Order.objects.create(
                    sale=sale,
                    buyer_name=buyer_name,
                    buyer_phone=buyer_phone,
                    buyer_email=buyer_email,
                    delivery_option=delivery_option_obj,
                    delivery_fee=delivery_fee,
                    delivery_address=delivery_address,
                    total_amount=grand_total,
                    status='pending',
                )
                for item_data in order_items_data:
                    OrderItem.objects.create(
                        order=order,
                        sale_item=item_data['sale_item'],
                        quantity=item_data['quantity'],
                        unit_price=item_data['unit_price'],
                    )

        except ValueError as stock_err:
            return JsonResponse({'error': str(stock_err)}, status=400)

        # -- Paystack session (outside atomic block -- no DB writes here) --
        client = PaystackClient()
        callback_url = request.build_absolute_uri(f'/sale/{slug}/order/success/')
        subaccount_code = sale.business.paystack_subaccount_code or None
        is_commission = (sale.pricing_model == 'commission')
        result = client.initialize_transaction(
            email=buyer_email,
            amount_ghs=grand_total,
            callback_url=callback_url,
            subaccount_code=subaccount_code,
            is_commission=is_commission,
            metadata={
                'type': 'order_checkout',
                'order_id': str(order.id),
                'sale_slug': slug,
            },
        )
        if result['success']:
            Payment.objects.create(
                type='order_checkout',
                paystack_reference=result['reference'],
                amount=grand_total,
                status='initiated',
                metadata={
                    'order_id': str(order.id),
                    'sale_slug': slug,
                },
            )
            return JsonResponse({
                'success': True,
                'authorization_url': result['authorization_url'],
                'reference': result['reference'],
                'order_id': str(order.id),
            })

        # On Paystack initialization failure, cancel order & revert reserved stock
        with transaction.atomic():
            order.status = 'failed'
            order.save(update_fields=['status'])
            for item in order.items.all():
                SaleItem.objects.filter(id=item.sale_item_id).update(
                    stock_remaining=F('stock_remaining') + item.quantity
                )
        return JsonResponse({'error': 'Payment initiation failed.'}, status=400)
class OrderSuccessView(View):
    """GET: Post-checkout confirmation page."""
    def get(self, request, slug):
        reference = request.GET.get('reference', '')
        return render(request, 'checkout/success.html', {
            'reference': reference,
            'slug': slug,
        })
class OrderStatusView(View):
    """GET: Poll for order payment confirmation."""
    def get(self, request, order_id):
        try:
            order = Order.objects.get(id=order_id)
        except Order.DoesNotExist:
            return JsonResponse({'status': 'not_found'}, status=404)
        return JsonResponse({
            'status': order.status,
            'order_number': order.order_number,
        })
    def get(self, request, slug):
        reference = request.GET.get('reference', '')
        return render(request, 'checkout/success.html', {
            'reference': reference,
            'slug': slug,
        })
class OrderStatusView(View):
    """GET: Poll for order payment confirmation."""
    def get(self, request, order_id):
        try:
            order = Order.objects.get(id=order_id)
        except Order.DoesNotExist:
            return JsonResponse({'status': 'not_found'}, status=404)
        return JsonResponse({
            'status': order.status,
            'order_number': order.order_number,
        })
