from django.shortcuts import render
from django.db.models import Sum, Max
from django.db import transaction as db_transaction
# Create your views here.
from rest_framework import viewsets, generics, status
from rest_framework.decorators import action, api_view, permission_classes,authentication_classes
from rest_framework.response import Response
from rest_framework.permissions import AllowAny
from .models import Product, Location, StockLevel, Device, Transaction, TransactionItem, SalesTarget, BonusRecord, Expense, ExpenseCategory, CreditSale, StockLevelSize, LocationCashBalance,CashTransaction, ProfitRecord, StockLevelSize, LocationCashBalance,StockMovement
from .serializers import ProductSerializer, LocationSerializer, StockLevelSerializer, DeviceSerializer, TransactionSerializer, GlobalStockSerializer, ProductWithStockSerializer,SalesTargetSerializer, BonusRecordSerializer, ExpenseCategorySerializer, MobileExpenseSerializer, CreditSaleSerializer
from rest_framework.permissions import IsAuthenticated,IsAdminUser
from django.utils.timezone import now
from django.utils.timezone import now, timedelta
from django.utils import timezone
from django.shortcuts import get_object_or_404
from .utils.location_helpers import get_user_location_id
from django.contrib.admin.views.decorators import staff_member_required
from rest_framework.authentication import SessionAuthentication

class ProductViewSet(viewsets.ModelViewSet):
    queryset = Product.objects.all().order_by('name')
    serializer_class = ProductWithStockSerializer
   # permission_classes = [IsAuthenticated]

    def get_queryset(self):
        queryset = super().get_queryset()
        location_id = self.request.query_params.get('location')

        if location_id:
            queryset = queryset.filter(stock_levels__location_id=location_id).distinct()

        return queryset

class LocationViewSet(viewsets.ModelViewSet):
    queryset = Location.objects.all()
    serializer_class = LocationSerializer
    permission_classes = [IsAuthenticated]

class StockLevelViewSet(viewsets.ModelViewSet):
    queryset = StockLevel.objects.select_related('product', 'location').all()
    serializer_class = StockLevelSerializer

class DeviceRegisterView(generics.CreateAPIView):
    permission_classes = (AllowAny,)
    serializer_class = DeviceSerializer

class TransactionSyncView(generics.CreateAPIView):
    """
    Accepts a single transaction object or a list of transactions.
    Requires authentication (JWT). The serializer handles idempotency.
    """
    permission_classes = [IsAuthenticated]
    queryset = Transaction.objects.all()
    serializer_class = TransactionSerializer

    def post(self, request, *args, **kwargs):
        data = request.data
        is_list = isinstance(data, list)
        if is_list:
            serializer = self.get_serializer(data=data, many=True)
        else:
            serializer = self.get_serializer(data=data)

        serializer.is_valid(raise_exception=True)
        objs = serializer.save()

        if is_list:
            out = [TransactionSerializer(o).data for o in objs]
        else:
            out = TransactionSerializer(objs).data

        return Response(out, status=status.HTTP_201_CREATED)

class TransactionViewSet(viewsets.ModelViewSet):
    serializer_class = TransactionSerializer
    permission_classes = [IsAuthenticated]

    def get_queryset(self):
        """
        Return transactions filtered by the user's location (if available).
        """
        user = self.request.user
        queryset = Transaction.objects.all()

        # Get user's assigned location (if profile exists)
        profile = getattr(user, "userprofile", None)
        if profile and profile.location:
            queryset = queryset.filter(device__location=profile.location)

        return queryset

    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        # Automatically assign device/location if needed
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

@api_view(['GET'])
@permission_classes([IsAuthenticated])
def dashboard_summary(request):
    # Total sales
    total_sales = Transaction.objects.aggregate(total=Sum('total_amount'))['total'] or 0

    # Stock left
    stock_left = StockLevel.objects.aggregate(total=Sum('quantity'))['total'] or 0

    # Last sync (latest transaction date)
    last_sync = Transaction.objects.aggregate(last=Max('created_at'))['last']

    return Response({
        "sales": total_sales,
        "stockLeft": stock_left,
        "lastSync": last_sync
    })
    
@api_view(["GET"])
@permission_classes([IsAuthenticated])
def transactionitem_summary(request):
    # ✅ Get current user's location (auto-detect)
    profile = getattr(request.user, "userprofile", None)
    location_id = None
    if profile and profile.location:
        location_id = profile.location.id

    now = timezone.now()
    today_start = now.replace(hour=0, minute=0, second=0, microsecond=0)
    today_end = today_start + timedelta(days=1)

    # ✅ Base filter (today only)
    base_filter = {
        "transaction__created_at__gte": today_start,
        "transaction__created_at__lt": today_end,
    }

    # ✅ Add location filter if user has one
    if location_id:
        base_filter["transaction__device__location_id"] = location_id

    # 🔹 Total sales today
    sales_today = (
        TransactionItem.objects.filter(**base_filter)
        .aggregate(total=Sum("line_total"))["total"]
        or 0
    )

    # 🔹 Transactions today
    transactions_today = (
        TransactionItem.objects.filter(**base_filter)
        .values("transaction_id")
        .distinct()
        .count()
    )

    # 🔹 Stock left
    stock_filter = {}
    if location_id:
        stock_filter["location_id"] = location_id

    total_stock = (
        StockLevel.objects.filter(**stock_filter)
        .aggregate(total=Sum("quantity"))["total"]
        or 0
    )

    # 🔹 Top 5 products
    top_products = (
        TransactionItem.objects.filter(**base_filter)
        .values("product__id", "product__name")
        .annotate(total_qty=Sum("quantity"), total_sales=Sum("line_total"))
        .order_by("-total_qty")[:5]
    )

    # 🔹 Recent 5 sales
    recent_sales = (
        TransactionItem.objects.filter(**base_filter)
        .select_related("product")
        .order_by("-transaction__created_at")[:5]
        .values("id", "product__name", "quantity", "line_total")
    )

    return Response({
        "location_id": location_id,
        "sales_today": float(sales_today),
        "transactions_today": transactions_today,
        "stockLeft": total_stock,
        "lastSync": now,
        "topProducts": list(top_products),
        "recentSales": list(recent_sales),
    })

    
@api_view(["GET"])
@permission_classes([IsAuthenticated])
def admin_dashboard(request):
    today = now().date()

    # --- KPIs ---
    sales_today = TransactionItem.objects.filter(
        transaction__created_at__date=today
    ).aggregate(total=Sum("line_total"))["total"] or 0

    items_sold_today = TransactionItem.objects.filter(
        transaction__created_at__date=today
    ).aggregate(total=Sum("quantity"))["total"] or 0

    # Lorry stock level (only trucks)
    lorry_stock = StockLevel.objects.filter(
        location__is_truck=True
    ).aggregate(total=Sum("quantity"))["total"] or 0

    # Sales this week & month
    start_of_week = today - timedelta(days=today.weekday())  # Monday
    start_of_month = today.replace(day=1)

    sales_week = TransactionItem.objects.filter(
        transaction__created_at__date__gte=start_of_week
    ).aggregate(total=Sum("line_total"))["total"] or 0

    sales_month = TransactionItem.objects.filter(
        transaction__created_at__date__gte=start_of_month
    ).aggregate(total=Sum("line_total"))["total"] or 0

    # Sales chart (last 7 days)
    last7 = [today - timedelta(days=i) for i in range(6, -1, -1)]
    sales_chart = []
    for d in last7:
        day_total = TransactionItem.objects.filter(
            transaction__created_at__date=d
        ).aggregate(total=Sum("line_total"))["total"] or 0
        sales_chart.append({"date": d.strftime("%Y-%m-%d"), "total": day_total})

    # Top 5 products this week
    top_products = (
        TransactionItem.objects.filter(transaction__created_at__date__gte=start_of_week)
        .values("product__id", "product__name")
        .annotate(total_qty=Sum("quantity"), total_sales=Sum("line_total"))
        .order_by("-total_qty")[:5]
    )

    # Low stock alerts (threshold e.g. <= 5)
    low_stock = (
        StockLevel.objects.filter(quantity__lte=5, location__is_truck=True)
        .select_related("product", "location")
        .values("product__name", "quantity", "location__name")
    )

    # Last sync time from devices
    last_sync = Device.objects.aggregate(last=Max("last_seen"))["last"]

    return Response({
        "sales_today": sales_today,
        "items_sold_today": items_sold_today,
        "lorry_stock": lorry_stock,
        "sales_week": sales_week,
        "sales_month": sales_month,
        "sales_chart": sales_chart,
        "top_products": list(top_products),
        "low_stock_alerts": list(low_stock),
        "last_sync": last_sync,
    })
# for driver 
@api_view(['GET'])
@permission_classes([IsAuthenticated])
def driver_dashboard(request, device_id):
    today = now().date()

    device = Device.objects.filter(id=device_id).first()
    if not device:
        return Response({"error": "Device not found"}, status=404)

    sales_today = TransactionItem.objects.filter(
        transaction__device=device,
        transaction__created_at__date=today
    ).aggregate(total=Sum("line_total"))["total"] or 0

    items_sold_today = TransactionItem.objects.filter(
        transaction__device=device,
        transaction__created_at__date=today
    ).aggregate(total=Sum("quantity"))["total"] or 0

    inventory = StockLevel.objects.filter(location=device.location).values(
        "product__name", "quantity", "product__unit_price"
    )

    return Response({
        "driver": device.assigned_to,
        "lorry": device.location.name if device.location else None,
        "sales_today": sales_today,
        "items_sold_today": items_sold_today,
        "inventory": list(inventory),
        "last_sync": device.last_seen,
    })
    
#to see summary of admin kpis and so 
def admin_dashboard_view(request):
    # Call the DRF summary function directly
    api_response = admin_dashboard(request)  # This already returns a DRF Response
    data = api_response.data  # Extract the JSON data

    return render(request, "admin_dashboard.html", {"data": data})
#drivers stock only 
@api_view(["GET"])
def my_stock(request, device_id):
    device = Device.objects.get(id=device_id)
    stocks = StockLevel.objects.filter(location=device.location)
    serializer = StockLevelSerializer(stocks, many=True)
    return Response(serializer.data)

from django.db.models import Sum
from django.shortcuts import render
from django.utils.timezone import now
from .models import StockLevel, Transaction
#

def admin_stock_view(request):
    now = timezone.now()
    today_start = now.replace(hour=0, minute=0, second=0, microsecond=0)
    today_end = today_start + timedelta(days=1)

    # 🔹 Total sales today (timezone-safe)
    total_sales_today = (
        TransactionItem.objects.filter(
            transaction__created_at__gte=today_start,
            transaction__created_at__lt=today_end
        )
        .aggregate(total=Sum("line_total"))["total"] or 0
    )

    # 🔹 Global stock totals
    global_stock = (
        StockLevel.objects
        .values("product__id", "product__name")
        .annotate(total_qty=Sum("quantity"))
        .order_by("product__name")
    )

    # 🔹 Stock per location (mainstore + trucks)
    stock_levels = (
        StockLevel.objects
        .select_related("product", "location")
        .order_by("location__name", "product__name")
    )

    context = {
        "total_sales_today": float(total_sales_today),
        "global_stock": global_stock,
        "stock_levels": stock_levels,
        "last_sync": now,  # helpful to display dashboard freshness
    }

    return render(request, "admin_stock.html", context)



#for fetching uuid for the device 
class DeviceViewSet(viewsets.ModelViewSet):
    queryset = Device.objects.all()
    serializer_class = DeviceSerializer
    permission_classes = [IsAuthenticated]

    def get_queryset(self):
        # Optional: only return devices assigned to the logged-in user
        return Device.objects.filter(assigned_to=self.request.user.username)


#recipt printing apis 
@api_view(['GET'])
@permission_classes([IsAuthenticated])
def get_receipt_data(request, transaction_id):
    """
    Retrieve formatted receipt data for a specific transaction.
    Used for reprinting receipts.
    """
    try:
        transaction = get_object_or_404(Transaction, id=transaction_id)
        
        # Get all items for this transaction
        items = TransactionItem.objects.filter(transaction=transaction).select_related('product', 'size')
        
        receipt_data = {
            "receiptNumber": transaction.server_receipt_number or str(transaction.id)[:8],
            "date": transaction.created_at.isoformat(),
            "total": float(transaction.total_amount),
            "paymentMethod": transaction.payment_method,
            "items": [
                {
                    "name": item.product.name,
                    "size": item.size.name if item.size else "N/A",
                    "quantity": item.quantity,
                    "unit_price": float(item.unit_price),
                    "line_total": float(item.line_total),
                }
                for item in items
            ],
            "device": {
                "name": transaction.device.name if transaction.device else "Unknown",
                "location": transaction.device.location.name if transaction.device and transaction.device.location else "Unknown",
            } if transaction.device else None,
        }
        
        return Response(receipt_data, status=200)
    
    except Transaction.DoesNotExist:
        return Response({"error": "Transaction not found"}, status=404)
    except Exception as e:
        return Response({"error": str(e)}, status=500)


@api_view(['GET'])
@permission_classes([IsAuthenticated])
def recent_transactions(request):
    """
    Get recent transactions for reprinting receipts.
    Returns last 20 transactions.
    """
    try:
        limit = int(request.GET.get('limit', 20))
        transactions = Transaction.objects.all().order_by('-created_at')[:limit]
        
        data = []
        for tx in transactions:
            items_count = tx.items.count()
            data.append({
                "id": str(tx.id),
                "receiptNumber": tx.server_receipt_number or str(tx.id)[:8],
                "date": tx.created_at.isoformat(),
                "total": float(tx.total_amount),
                "itemsCount": items_count,
                "device": tx.device.name if tx.device else "Unknown",
            })
        
        return Response(data, status=200)
    
    except Exception as e:
        return Response({"error": str(e)}, status=500)


@api_view(['POST'])
@permission_classes([IsAuthenticated])
def mark_receipt_printed(request, transaction_id):
    """
    Mark a transaction's receipt as printed.
    Optional: Add a 'printed_at' field to Transaction model to track this.
    """
    try:
        transaction = get_object_or_404(Transaction, id=transaction_id)
        
        # If you add a 'receipt_printed' boolean field to Transaction model:
        # transaction.receipt_printed = True
        # transaction.receipt_printed_at = timezone.now()
        # transaction.save()
        
        return Response({
            "success": True,
            "message": f"Receipt #{transaction.server_receipt_number} marked as printed"
        }, status=200)
    
    except Exception as e:
        return Response({"error": str(e)}, status=500)

# ecommerce integration endpoints 
@api_view(['GET'])
@permission_classes([AllowAny])  # Public access
def ecommerce_products(request):
    """
    Public endpoint for e-commerce product catalog.
    No authentication required.
    """
    products = Product.objects.all().order_by('name')
    
    # Optional: Filter by category, search, etc.
    search = request.GET.get('search', '')
    if search:
        products = products.filter(name__icontains=search)
    
    serializer = ProductWithStockSerializer(products, many=True)
    return Response(serializer.data)


@api_view(['GET'])
@permission_classes([IsAuthenticated])
def get_sales_target(request):
    try:

        
        # FIXED: Handle missing profile gracefully (like transactionitem_summary does)
        profile = getattr(request.user, 'userprofile', None)
        location = None
        
        if profile and profile.location:
            location = profile.location
        else:
            # Log the issue but don't fail
            if not profile:
                print(f" No UserProfile found for {request.user.username}")
            else:
                print(f" No location assigned to {request.user.username}")
            
            return Response({
                'has_target': False,
                'message': f'No location assigned to user {request.user.username}. Please contact admin.'
            }, status=200)

        #  DEBUG: Query targets
        target = (
            SalesTarget.objects
            .filter(location=location, is_active=True)
            .order_by('-created_at')
            .first()
        )

        if not target:
            print(f" No active target for location: {location.name}")
            return Response({
                'has_target': False,
                'message': f'No active sales target for {location.name}'
            }, status=200)

        # target found
        print(f"Target found for {location.name}: KES {target.target_amount}")
        print(f"Current sales: KES {target.get_current_sales()}")
        print(f"Progress: {target.get_progress_percentage():.1f}%")
        
        serializer = SalesTargetSerializer(target)

        return Response({
            'has_target': True,
            'target': serializer.data
        })

    except Exception as e:
        print(f" Exception in get_sales_target: {str(e)}")
        import traceback
        traceback.print_exc()
        
        return Response({
            'has_target': False,
            'error': str(e),
            'message': 'Server error. Please contact admin.'
        }, status=500)

@api_view(['GET'])
@permission_classes([IsAuthenticated])
def get_bonus_history(request):
    """
    Get bonus history for user's location
    """
    try:
        profile = getattr(request.user, 'userprofile', None)
        if not profile or not profile.location:
            return Response({'error': 'No location assigned'}, status=400)
        
        bonuses = BonusRecord.objects.filter(
            location=profile.location
        ).order_by('-period_start')[:10]
        
        serializer = BonusRecordSerializer(bonuses, many=True)
        return Response(serializer.data)
    
    except Exception as e:
        return Response({'error': str(e)}, status=500)


@api_view(['GET'])
@permission_classes([IsAuthenticated])
def get_expense_categories(request):
    """
    Get business expense categories for mobile app
    """
    categories = ExpenseCategory.objects.filter(
        category_type='business',
        is_active=True
    ).order_by('name')
    
    serializer = ExpenseCategorySerializer(categories, many=True)
    return Response(serializer.data)


@api_view(['POST'])
@permission_classes([IsAuthenticated])
def submit_mobile_expense(request):
    """
    Submit expense from mobile app (business expenses only)
    """
    serializer = MobileExpenseSerializer(
        data=request.data,
        context={'request': request}
    )
    
    if serializer.is_valid():
        serializer.save()
        return Response({
            'success': True,
            'message': 'Expense recorded successfully',
            'expense': serializer.data
        }, status=status.HTTP_201_CREATED)
    
    return Response({
        'success': False,
        'errors': serializer.errors
    }, status=status.HTTP_400_BAD_REQUEST)


@api_view(['GET'])
@permission_classes([IsAuthenticated])
def get_recent_expenses(request):
    """
    Get recent expenses submitted by user
    """
    try:
        expenses = Expense.objects.filter(
            created_by=request.user,
            expense_type='business'
        ).order_by('-date')[:20]
        
        serializer = MobileExpenseSerializer(expenses, many=True)
        return Response(serializer.data)
    
    except Exception as e:
        return Response({'error': str(e)}, status=500)
    

def _perform_reversal(tx, user, reason):
    """
    Atomic reversal of a transaction.
    Handles regular sales AND credit sales (paid or unpaid).
    Returns a dict with success/error info.
    """
    from .models import (
        CreditSale, StockLevelSize, LocationCashBalance,
        CashTransaction, ProfitRecord,
    )
 
    with db_transaction.atomic():
        items = TransactionItem.objects.filter(
            transaction=tx
        ).select_related('product', 'size')
 
        if not items.exists():
            return {'success': False, 'error': 'No items found for this transaction'}
 
        location = tx.get_location()
        if not location:
            return {'success': False, 'error': 'Cannot determine transaction location'}
 
        reversal_summary = []
 
        # ── 1. Restock all items ──────────────────────────────────────────
        for item in items:
            sls = StockLevelSize.objects.select_for_update().filter(
                stock_level__product=item.product,
                stock_level__location=location,
                size=item.size,
            ).first()
 
            if sls:
                old_qty = sls.quantity
                sls.quantity += item.quantity
                sls.save()
                reversal_summary.append({
                    'product':          item.product.name,
                    'size':             item.size.name if item.size else 'N/A',
                    'quantity_restored': item.quantity,
                    'old_stock':        old_qty,
                    'new_stock':        sls.quantity,
                })
            else:
                reversal_summary.append({
                    'product':          item.product.name,
                    'size':             item.size.name if item.size else 'N/A',
                    'quantity_restored': item.quantity,
                    'warning':          'Stock record not found – could not restock',
                })
 
        # ── 2. Determine whether/how to reverse cash balance ─────────────
        is_credit       = tx.payment_method == 'on_credit'
        credit_is_paid  = False
        credit_paid_method = None
        credit_obj      = None
 
        try:
            credit_obj = tx.credit_sale
            credit_is_paid     = credit_obj.is_paid
            credit_paid_method = credit_obj.paid_payment_method
        except Exception:
            pass
 
        cash_reversal = None
 
        if is_credit and not credit_is_paid:
            # No money was collected → nothing to reverse in cash balances
            cash_reversal = {'note': 'Unpaid credit — no cash balance to reverse'}
        else:
            # Money was collected: for paid credits use the method they actually paid with
            reverse_method = credit_paid_method if (is_credit and credit_is_paid) else tx.payment_method
 
            cash_balance = LocationCashBalance.objects.select_for_update().filter(
                location=location,
                payment_method=reverse_method,
            ).first()
 
            if cash_balance:
                old_balance = cash_balance.balance
                cash_balance.balance     -= tx.total_amount
                cash_balance.total_sales -= tx.total_amount
                cash_balance.save()
 
                CashTransaction.objects.create(
                    location=location,
                    payment_method=reverse_method,
                    transaction_type='adjustment',
                    amount=-tx.total_amount,
                    sale_transaction=None,
                    description=(
                        f"REVERSAL: {reason} "
                        f"(Receipt: {tx.server_receipt_number or str(tx.id)[:8]})"
                    ),
                    reference_number=f"REV-{tx.server_receipt_number or str(tx.id)[:8]}",
                    balance_after=cash_balance.balance,
                    created_by=user,
                )
 
                cash_reversal = {
                    'payment_method':  reverse_method,
                    'old_balance':     float(old_balance),
                    'amount_reversed': float(tx.total_amount),
                    'new_balance':     float(cash_balance.balance),
                }
 
        # ── 3. Delete associated records ──────────────────────────────────
        profit_count    = ProfitRecord.objects.filter(stock_movement__transaction=tx).count()
        movement_count  = StockMovement.objects.filter(transaction=tx).count()
        cash_log_count  = CashTransaction.objects.filter(sale_transaction=tx).count()
 
        ProfitRecord.objects.filter(stock_movement__transaction=tx).delete()
        StockMovement.objects.filter(transaction=tx).delete()
        CashTransaction.objects.filter(sale_transaction=tx).delete()
 
        # Delete CreditSale if present
        if credit_obj:
            credit_obj.delete()
 
        # ── 4. Store receipt number before deletion ───────────────────────
        receipt_number = tx.server_receipt_number or str(tx.id)[:8]
        item_count = items.count()
        items.delete()
        tx.delete()
 
        return {
            'success': True,
            'message': 'Transaction reversed successfully',
            'reversal_details': {
                'receipt_number':  receipt_number,
                'reason':          reason,
                'items_restocked': reversal_summary,
                'cash_reversal':   cash_reversal,
                'records_deleted': {
                    'transaction_items': item_count,
                    'stock_movements':   movement_count,
                    'profit_records':    profit_count,
                    'cash_logs':         cash_log_count,
                },
            },
            'reversed_at': timezone.now().isoformat(),
            'reversed_by': user.username if user else 'system',
        }
    
    # ── Credit Sales List ─────────────────────────────────────────────────────────
 
@api_view(['GET'])
@permission_classes([IsAuthenticated])
def credit_sales_list(request):
    """
    GET /api/credits/
    ?paid=true   → include paid credits (default: unpaid only)
    ?overdue=true → only overdue credits
    Admin sees all; cashier sees own location only.
    """
    try:
        from .models import CreditSale
 
        credits = CreditSale.objects.select_related(
            'transaction',
            'transaction__device',
            'transaction__device__location',
        ).order_by('is_paid', 'due_date')
 
        # Location filter for non-admin users
        if not request.user.is_staff:
            profile = getattr(request.user, 'userprofile', None)
            if profile and profile.location:
                credits = credits.filter(
                    transaction__device__location=profile.location
                )
 
        # Optional filters
        show_paid = request.GET.get('paid', 'false').lower() == 'true'
        if not show_paid:
            credits = credits.filter(is_paid=False)
 
        today = timezone.now().date()
 
        data = []
        for c in credits:
            data.append({
                'id':             str(c.id),
                'transaction_id': str(c.transaction.id),
                'receipt_number': c.transaction.server_receipt_number,
                'customer_name':  c.customer_name,
                'customer_phone': c.customer_phone,
                'amount':         float(c.transaction.total_amount),
                'due_date':       c.due_date.isoformat(),
                'is_paid':        c.is_paid,
                'paid_at':        c.paid_at.isoformat() if c.paid_at else None,
                'is_overdue':     c.is_overdue,
                'days_overdue':   c.days_overdue,
                'days_until_due': c.days_until_due,
                'notes':          c.notes,
                'created_at':     c.created_at.isoformat(),
                'location': (
                    c.transaction.device.location.name
                    if c.transaction.device and c.transaction.device.location
                    else 'Unknown'
                ),
            })
 
        return Response(data)
 
    except Exception as e:
        import traceback; traceback.print_exc()
        return Response({'error': str(e)}, status=500)
 # ── Mark Credit Paid ──────────────────────────────────────────────────────────
 
@api_view(['POST'])
@permission_classes([IsAuthenticated, IsAdminUser])
def mark_credit_paid(request, credit_id):
    """
    Admin confirms a credit payment was received.
    POST /api/credits/<id>/mark-paid/
    Body: { "payment_method": "cash" }
    This is the moment LocationCashBalance is updated for the sale.
    """
    try:
        from .models import CreditSale, LocationCashBalance, CashTransaction
 
        credit = CreditSale.objects.select_related('transaction').get(id=credit_id)
 
        if credit.is_paid:
            return Response(
                {'error': 'This credit has already been marked as paid'},
                status=status.HTTP_400_BAD_REQUEST,
            )
 
        payment_method = request.data.get('payment_method', 'cash')
        valid_methods  = [m[0] for m in Transaction.PAYMENT_METHODS if m[0] != 'on_credit']
        if payment_method not in valid_methods:
            return Response(
                {'error': f'Invalid payment method. Choose from: {", ".join(valid_methods)}'},
                status=status.HTTP_400_BAD_REQUEST,
            )
 
        with db_transaction.atomic():
            # Mark as paid
            credit.is_paid             = True
            credit.paid_at             = timezone.now()
            credit.paid_payment_method = payment_method
            credit.confirmed_by        = request.user
            credit.save()
 
            # Now update cash balance (money has arrived)
            location = credit.transaction.get_location()
            if location:
                amount = credit.transaction.total_amount
 
                cash_balance, _ = LocationCashBalance.objects.get_or_create(
                    location=location,
                    payment_method=payment_method,
                    defaults={
                        'balance':         Decimal('0.00'),
                        'total_sales':     Decimal('0.00'),
                        'total_purchases': Decimal('0.00'),
                    }
                )
                cash_balance.balance     += amount
                cash_balance.total_sales += amount
                cash_balance.save()
 
                CashTransaction.objects.create(
                    location=location,
                    payment_method=payment_method,
                    transaction_type='sale',
                    amount=amount,
                    sale_transaction=credit.transaction,
                    balance_after=cash_balance.balance,
                    created_by=request.user,
                    description=(
                        f"Credit payment received — "
                        f"{credit.customer_name} ({credit.customer_phone})"
                        f" | Receipt #{credit.transaction.server_receipt_number}"
                    ),
                )
 
        return Response({
            'success':        True,
            'message':        (
                f"Credit marked as paid. KES {credit.transaction.total_amount:,.2f} "
                f"added to {payment_method.replace('_', ' ').title()} balance."
            ),
            'credit_id':      str(credit.id),
            'paid_at':        credit.paid_at.isoformat(),
            'payment_method': payment_method,
            'confirmed_by':   request.user.username,
        })
 
    except CreditSale.DoesNotExist:
        return Response({'error': 'Credit sale not found'}, status=status.HTTP_404_NOT_FOUND)
    except Exception as e:
        import traceback; traceback.print_exc()
        return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
    
# ── Cashier void (30-minute window, own location only) ───────────────────────
 
@api_view(['POST'])
@permission_classes([IsAuthenticated])
def cashier_void_transaction(request, transaction_id):
    try:
        with db_transaction.atomic():                          # ← wrap everything
            tx = Transaction.objects.select_for_update().get(id=transaction_id)

            time_limit = timezone.now() - timedelta(minutes=30)
            if tx.created_at < time_limit:
                return Response(
                    {
                        'success': False,
                        'error': (
                            'Cannot void transactions older than 30 minutes. '
                            'Ask your admin to reverse it.'
                        ),
                    },
                    status=status.HTTP_400_BAD_REQUEST,
                )

            profile = getattr(request.user, 'userprofile', None)
            if profile and profile.location:
                if tx.device and tx.device.location != profile.location:
                    return Response(
                        {'success': False, 'error': "You can only void your own location's sales"},
                        status=status.HTTP_403_FORBIDDEN,
                    )

            try:
                if tx.credit_sale.is_paid:
                    return Response(
                        {
                            'success': False,
                            'error': (
                                'Cannot void a credit sale that has already been paid. '
                                'Contact admin.'
                            ),
                        },
                        status=status.HTTP_400_BAD_REQUEST,
                    )
            except Exception:
                pass

            result = _perform_reversal(tx, request.user, reason='Voided by cashier')

        http_status = status.HTTP_200_OK if result['success'] else status.HTTP_400_BAD_REQUEST
        return Response(result, status=http_status)

    except Transaction.DoesNotExist:
        return Response(
            {'success': False, 'error': 'Transaction not found'},
            status=status.HTTP_404_NOT_FOUND,
        )
    except Exception as e:
        import traceback; traceback.print_exc()
        return Response(
            {'success': False, 'error': str(e)},
            status=status.HTTP_500_INTERNAL_SERVER_ERROR,
        )


@api_view(['GET']) 
@authentication_classes([SessionAuthentication])
@permission_classes([IsAuthenticated, IsAdminUser])
def reversible_transactions(request):
    """
    🔴 ADMIN ONLY: Get list of transactions from last 24 hours
    Only accessible from admin panel session
    """
    # Last 24 hours
    time_limit = timezone.now() - timedelta(hours=24)
    limit = int(request.GET.get('limit', 50))
    
    transactions = Transaction.objects.filter(
        created_at__gte=time_limit
    ).order_by('-created_at')[:limit]
    
    data = [
        {
            'id': str(tx.id),
            'receipt_number': tx.server_receipt_number or str(tx.id)[:8],
            'total_amount': float(tx.total_amount),
            'payment_method': tx.get_payment_method_display(),
            'mpesa_reference': tx.mpesa_reference or 'N/A',
            'created_at': tx.created_at.isoformat(),
            'age_hours': int((timezone.now() - tx.created_at).total_seconds() / 3600)
        }
        for tx in transactions
    ]
    
    return Response({
        'transactions': data,
        'time_window_hours': 24,
        'count': len(data)
    })


@api_view(['GET'])
@permission_classes([IsAuthenticated, IsAdminUser]) # ✅ Admin only
def transaction_reversal_preview(request, transaction_id):
    """
    🔴 ADMIN ONLY: Preview what will be reversed without actually reversing
    
    GET /api/transactions/<transaction_id>/reverse/preview/
    """
    try:
        tx = Transaction.objects.get(id=transaction_id)
        
        # Check if reversible
        time_limit = timezone.now() - timedelta(hours=24)
        is_reversible = tx.created_at >= time_limit
        
        # Get items
        items = TransactionItem.objects.filter(transaction=tx).select_related('product', 'size')
        
        # Get location
        location = tx.get_location()
        
        items_preview = []
        for item in items:
            stock_level_size = None
            if location:
                stock_level_size = StockLevelSize.objects.filter(
                    stock_level__product=item.product,
                    stock_level__location=location,
                    size=item.size
                ).first()
            
            items_preview.append({
                'product': item.product.name,
                'size': item.size.name if item.size else 'N/A',
                'quantity_sold': item.quantity,
                'current_stock': stock_level_size.quantity if stock_level_size else 0,
                'stock_after_reversal': (stock_level_size.quantity + item.quantity) if stock_level_size else item.quantity,
                'unit_price': float(item.unit_price),
                'line_total': float(item.line_total)
            })
        
        # Get cash balance
        cash_balance = None
        if location:
            cb = LocationCashBalance.objects.filter(
                location=location,
                payment_method=tx.payment_method
            ).first()
            
            if cb:
                cash_balance = {
                    'current_balance': float(cb.balance),
                    'balance_after_reversal': float(cb.balance - tx.total_amount),
                    'payment_method': tx.get_payment_method_display()
                }
        
        return Response({
            'transaction': {
                'id': str(tx.id),
                'receipt_number': tx.server_receipt_number or str(tx.id)[:8],
                'total_amount': float(tx.total_amount),
                'payment_method': tx.get_payment_method_display(),
                'mpesa_reference': tx.mpesa_reference or 'N/A',
                'created_at': tx.created_at.isoformat(),
                'location': location.name if location else 'Unknown',
                'age_hours': int((timezone.now() - tx.created_at).total_seconds() / 3600)
            },
            'is_reversible': is_reversible,
            'items_to_restock': items_preview,
            'cash_reversal': cash_balance,
            'warnings': [] if is_reversible else ['Transaction is older than 24 hours']
        })
    
    except Transaction.DoesNotExist:
        return Response({
            'error': 'Transaction not found'
        }, status=status.HTTP_404_NOT_FOUND)
    except Exception as e:
        return Response({
            'error': str(e)
        }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)


def transaction_reversal_page(request):
    """
    Simple admin page for reversing transactions.
    Only accessible to staff/admin users.
    """
    return render(request, 'transaction_reversal.html', {
        'title': 'Transaction Reversal',
    })