# sales/views.py (COMPLETE - Single working version)
import logging
logger = logging.getLogger(__name__)

from django.shortcuts import render, redirect, get_object_or_404
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.http import JsonResponse, HttpResponse
from django.views.decorators.csrf import csrf_exempt
from django.utils import timezone
from django.db import transaction
from django.db.models import Sum, Count, Q
from datetime import datetime, timedelta
import json
from inventory.models import Product
from core.models import SystemSettings, CustomField, DynamicFormData
from .models import POSOrder, POSOrderItem, PaymentTransaction, DailySalesSummary, UnusualTransaction
from .forms import POSOrderForm, POSOrderItemForm
from django.core.exceptions import PermissionDenied
from core.utils import check_limit_or_block
from core.models import SystemSettings, CustomField, DynamicFormData, SubscriptionPlan, License, Branch
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.utils import timezone
from core.models import SystemSettings, License, Branch
from inventory.models import Product
from .models import POSOrder, POSOrderItem
from accounts.decorators import role_required


@login_required
def pos_sales(request):
    """Main POS sales screen with product selection dropdown"""
    # ------------------------------------------------------
    # 1. System settings + plan -> barcode_scanner_enabled
    # ------------------------------------------------------
    system_settings = SystemSettings.objects.first()
    barcode_scanner_enabled = True  # default

    if system_settings:
        try:
            license_obj = system_settings.license
            plan = license_obj.plan
            barcode_scanner_enabled = getattr(plan, "allow_barcode_scanner", True)
            logger.debug(f"DEBUG POS: Plan={plan.code}, allow_barcode_scanner={barcode_scanner_enabled}")
        except License.DoesNotExist:
            logger.debug("DEBUG POS: No license attached to SystemSettings; barcode_scanner_enabled default=True")
            barcode_scanner_enabled = True
    else:
        logger.debug("DEBUG POS: No SystemSettings found; barcode_scanner_enabled default=True")
        barcode_scanner_enabled = True

    # ------------------------------------------------------
    # 2. Get active products for dropdown
    # ------------------------------------------------------
    products = Product.objects.filter(status="active").order_by("name").values(
        "id", "name", "sku", "barcode", "price", "stock_quantity"
    )

    logger.debug(f"DEBUG POS: Found {len(products)} active products")

    # ------------------------------------------------------
    # 3. Ensure there is a current order in session
    # ------------------------------------------------------
    order = None
    order_id = request.session.get("current_order_id")

    if order_id:
        try:
            order = POSOrder.objects.get(id=order_id)
            logger.debug(f"DEBUG POS: Using existing order id={order.id}, number={order.order_number}")
        except POSOrder.DoesNotExist:
            logger.debug(f"DEBUG POS: Invalid order id in session ({order_id}); creating new order")
            order = None

    if order is None:
        # Determine branch for this order
        user_branch = getattr(request.user, "branch", None)
        if user_branch is None:
            # fallback: first active branch
            user_branch = Branch.objects.filter(is_active=True).first()

        order = POSOrder.objects.create(
            order_number=f"POS-{timezone.now().strftime('%m%d%H%M')}-{''.join(__import__('random').choices(__import__('string').ascii_uppercase+__import__('string').digits,k=5))}",
            total_amount=0,
            tax_amount=0,
            final_amount=0,
            cashier=request.user.username,
            payment_method="pos",
            branch=user_branch,
        )

        request.session["current_order_id"] = order.id
        logger.debug(f"DEBUG POS: Created new order id={order.id}, number={order.order_number}")

    # ------------------------------------------------------
    # 4. Handle POST actions
    # ------------------------------------------------------
    if request.method == "POST":
        action = (request.POST.get("action") or "").strip()
        logger.debug(f"DEBUG POS: POST action={action}")

        # ADD ITEM
        if action == "add_item":
            selected_sku = (request.POST.get("selected_product") or "").strip()
            try:
                quantity = int(request.POST.get("quantity") or "1")
            except ValueError:
                quantity = 1

            logger.debug(f"DEBUG POS: add_item selected_sku='{selected_sku}', quantity={quantity}")

            if not selected_sku:
                messages.error(request, "Please select a product.")
                return redirect("sales:pos")

            product = Product.objects.filter(sku=selected_sku, status="active").first()
            if not product:
                messages.error(request, f"Product not found with SKU: {selected_sku}")
                logger.debug(f"DEBUG POS: Product not found for SKU={selected_sku}")
                return redirect("sales:pos")

            if product.stock_quantity < quantity:
                messages.error(
                    request,
                    f"Insufficient stock for {product.name}. Available: {product.stock_quantity}",
                )
                print(
                    f"DEBUG POS: Insufficient stock for {product.name}. "
                    f"requested={quantity}, available={product.stock_quantity}"
                )
                return redirect("sales:pos")

            existing_item = POSOrderItem.objects.filter(order=order, product=product).first()

            if existing_item:
                new_qty = existing_item.quantity + quantity
                # check against total available (already reserved by this item + stock)
                max_allowed = product.stock_quantity + existing_item.quantity
                if new_qty > max_allowed:
                    messages.error(
                        request,
                        f"Insufficient stock for {product.name}. Available: {product.stock_quantity}",
                    )
                    print(
                        f"DEBUG POS: Insufficient after update for {product.name}; "
                        f"new_qty={new_qty}, max_allowed={max_allowed}"
                    )
                    return redirect("sales:pos")

                existing_item.quantity = new_qty
                existing_item.total_price = float(existing_item.unit_price) * new_qty
                existing_item.save()

                product.stock_quantity -= quantity
                product.save()

                messages.success(request, f"Added {quantity} more {product.name} to cart")
                print(
                    f"DEBUG POS: Updated existing item product={product.name}, "
                    f"new_qty={new_qty}, remaining_stock={product.stock_quantity}"
                )
            else:
                POSOrderItem.objects.create(
                    order=order,
                    product=product,
                    quantity=quantity,
                    unit_price=product.price,
                    total_price=float(product.price) * quantity,
                )

                product.stock_quantity -= quantity
                product.save()

                messages.success(request, f"Added {quantity} {product.name} to cart")
                print(
                    f"DEBUG POS: Created new order item for product={product.name}, "
                    f"qty={quantity}, remaining_stock={product.stock_quantity}"
                )

            return redirect("sales:pos")

        # REMOVE ITEM
        elif action == "remove_item":
            item_id = request.POST.get("item_id")
            logger.debug(f"DEBUG POS: remove_item id={item_id}")
            try:
                order_item = get_object_or_404(POSOrderItem, id=item_id, order=order)
                order_item.product.stock_quantity += order_item.quantity
                order_item.product.save()
                order_item.delete()
                messages.success(request, "Item removed from cart")
                logger.debug(f"DEBUG POS: Removed item id={item_id}")
            except Exception as exc:
                messages.error(request, "Error removing item")
                logger.debug(f"DEBUG POS: Error removing item id={item_id}: {exc}")
            return redirect("sales:pos")

        # CLEAR ORDER
        elif action == "clear_order":
            logger.debug(f"DEBUG POS: clear_order for order id={order.id}")
            for item in order.items.all():
                item.product.stock_quantity += item.quantity
                item.product.save()
                print(
                    f"DEBUG POS: Restored stock for {item.product.name}, "
                    f"+{item.quantity}, new_stock={item.product.stock_quantity}"
                )
            order.delete()
            if "current_order_id" in request.session:
                del request.session["current_order_id"]
            messages.info(request, "Current order cleared")
            logger.debug("DEBUG POS: Order cleared and session key removed")
            return redirect("sales:pos")

        # COMPLETE ORDER
        elif action == "complete_order":
            logger.debug(f"DEBUG POS: complete_order for order id={order.id}")

            # enforce plan daily order limit
            try:
                check_limit_or_block("orders_per_day")
            except PermissionDenied as e:
                messages.error(request, str(e))
                logger.debug(f"DEBUG POS: orders_per_day blocked: {e}")
                return redirect("sales:pos")

            payment_method = request.POST.get("payment_method", "pos")
            reference_number = request.POST.get("reference_number", "")
            customer_name = request.POST.get("customer_name", "")
            customer_phone = request.POST.get("customer_phone", "")

            print(
                f"DEBUG POS: Completing order={order.order_number}, "
                f"payment_method={payment_method}, customer={customer_name}, phone={customer_phone}"
            )

            # recalc totals
            order_items = POSOrderItem.objects.filter(order=order).select_related("product")
            subtotal = sum(float(item.total_price) for item in order_items)
            tax_amount = subtotal * 0.00
            final_total = subtotal + tax_amount

            order.total_amount = subtotal
            order.tax_amount = tax_amount
            order.final_amount = final_total
            order.payment_method = payment_method
            order.customer_name = customer_name
            order.customer_phone = customer_phone
            order.status = "completed"
            order.save()

            if "current_order_id" in request.session:
                del request.session["current_order_id"]

            messages.success(request, f"Order #{order.order_number} completed successfully!")
            logger.debug(f"DEBUG POS: Order {order.order_number} completed successfully")
            return redirect("sales:print_receipt", order_id=order.id)

        # UNKNOWN ACTION
        else:
            logger.debug(f"DEBUG POS: Unknown POST action='{action}', redirecting to POS")
            return redirect("sales:pos")

    # ------------------------------------------------------
    # 5. GET request: render POS with totals
    # ------------------------------------------------------
    order_items = POSOrderItem.objects.filter(order=order).select_related("product")

    subtotal = sum(float(item.total_price) for item in order_items)
    tax_amount = subtotal * 0.00
    final_total = subtotal + tax_amount

    order.total_amount = subtotal
    order.tax_amount = tax_amount
    order.final_amount = final_total
    order.save()

    print(
        f"DEBUG POS: GET render, order={order.order_number}, "
        f"items={order_items.count()}, subtotal={subtotal}, final_total={final_total}"
    )

    from .models import Customer
    context = {
        "order": order,
        "order_items": order_items,
        "subtotal": subtotal,
        "tax_amount": tax_amount,
        "final_total": final_total,
        "products": products,
        "system_settings": system_settings,
        "user_role": request.user.role,
        "user_name": request.user.get_full_name() or request.user.username,
        "barcode_scanner_enabled": barcode_scanner_enabled,
        "customers": Customer.objects.all().order_by('name'),
    }
    return render(request, "sales/pos_sales.html", context)


@login_required
def pos_grid(request):
    """Smart Grid POS V2 — gated by plan.allow_grid_pos"""
    system_settings = SystemSettings.objects.first()
    try:
        from core.utils import get_current_license
        lic = get_current_license()
        plan = getattr(lic, 'plan', None)
        if plan and not getattr(plan, 'allow_grid_pos', False):
            from django.urls import reverse
            return redirect(reverse('upgrade_required') + '?feature=Smart+Grid+POS+V2&required_plan=Standard')
    except Exception:
        pass

    from .models import Customer
    context = {
        'user_role': request.user.role,
        'user_name': request.user.get_full_name() or request.user.username,
        'system_settings': system_settings,
        'customers': Customer.objects.all().order_by('name'),
    }
    return render(request, 'sales/pos_grid.html', context)


from core.models import SystemSettings, CustomField, DynamicFormData
from .models import POSOrder, POSOrderItem, PaymentTransaction, DailySalesSummary, UnusualTransaction


import string

BASE36_ALPHABET = string.digits + string.ascii_lowercase


def _to_base36(num: int) -> str:
    """Convert positive int to base36 (same as JS toString(36))."""
    if num == 0:
        return "0"
    result = ""
    while num > 0:
        num, rem = divmod(num, 36)
        result = BASE36_ALPHABET[rem] + result
    return result


def generate_verification_code(order: POSOrder, system_settings: SystemSettings = None) -> str:
    """
    Generate the same verification code as the JS on the receipt template.
    Raw string: order_number | created_at(YmdHis) | business_name
    Hash: 32-bit int, multiplied by 31 each step, base36, padded to 8, SP-XXXX-XXXX.
    """
    if system_settings is None:
        system_settings = SystemSettings.objects.first()

    business_name = system_settings.business_name if system_settings else ""
    created_str = order.created_at.strftime("%Y%m%d%H%M%S")

    raw = f"{order.order_number}|{created_str}|{business_name}"

    # Same hashing approach as JS
    h = 0
    for ch in raw:
        h = (h * 31 + ord(ch)) & 0xFFFFFFFF  # 32-bit

    code = _to_base36(h).upper()
    code = code.rjust(8, "0")[-8:]  # pad to 8, keep last 8
    return f"SP-{code[:4]}-{code[4:]}"
    
    
    

from django.utils import timezone  # already imported earlier in your file

def verify_order(request):
    """
    Public verification page for QR scans.
    URL: /sales/verify-order/?order=<order_number>
    """
    order_number = request.GET.get("order", "").strip()
    system_settings = SystemSettings.objects.first()

    order = None
    verification_code = None
    is_valid = False

    if order_number:
        order = POSOrder.objects.filter(order_number=order_number).first()
        if order:
            verification_code = generate_verification_code(order, system_settings)
            is_valid = True

    context = {
        "order_number": order_number,
        "order": order,
        "verification_code": verification_code,
        "is_valid": is_valid,
        "system_settings": system_settings,
    }
    return render(request, "sales/verify_order.html", context)




from rest_framework.authtoken.models import Token
from django.conf import settings

def print_receipt(request, order_id):
    """Print HTML receipt (with QR) for completed order"""
    if not request.user.is_authenticated:
        token_key = request.GET.get('token')
        if token_key:
            try:
                token = Token.objects.get(key=token_key)
                request.user = token.user
            except Token.DoesNotExist:
                return redirect(f"{settings.LOGIN_URL}?next={request.path}")
        else:
            return redirect(f"{settings.LOGIN_URL}?next={request.path}")

    order = get_object_or_404(POSOrder, id=order_id)
    order_items = POSOrderItem.objects.filter(order=order).select_related('product')

    # Get system settings for receipt
    system_settings = SystemSettings.objects.first()

    # --- Generate QR code for HTML receipt ---
    qr_code_b64 = None
    try:
        # If you have a real verify view, better: reverse('sales:verify_order') + querystring
        qr_data = request.build_absolute_uri(
            f"/sales/verify-order/?order={order.order_number}"
        )

        qr = qrcode.QRCode(
            version=1,
            box_size=4,   # smaller boxes so it fits nicely in the 90x90px area
            border=1,
        )
        qr.add_data(qr_data)
        qr.make(fit=True)

        img = qr.make_image(fill_color="black", back_color="white")
        buffer = BytesIO()
        img.save(buffer, format="PNG")
        qr_code_b64 = base64.b64encode(buffer.getvalue()).decode("ascii")
    except Exception as e:
        # Optional: log instead of print in production
        logger.warning("QR generation error:", e)

    context = {
        'order': order,
        'order_items': order_items,
        'system_settings': system_settings,
        'qr_code': qr_code_b64,
        'currency_symbol': system_settings.currency_symbol if system_settings else '₦',
        'user_role': request.user.role,
        'user_name': request.user.get_full_name() or request.user.username,
    }
    return render(request, 'sales/receipt.html', context)

@login_required
def repeat_sale(request, order_id):
    """Create a new order with same items as the original order"""
    original_order = get_object_or_404(POSOrder, id=order_id)
    
    # Create new order
    new_order = POSOrder.objects.create(
        order_number=f"GPOS-{timezone.now().strftime('%m%d%H%M')}-{''.join(__import__('random').choices(__import__('string').ascii_uppercase+__import__('string').digits,k=5))}",
        total_amount=0,
        final_amount=0,
        cashier=request.user.username,
        payment_method='cash'
    )
    
    # Copy items
    for item in original_order.items.all():
        # Check if product still exists and has sufficient stock
        if item.product.stock_quantity >= item.quantity:
            POSOrderItem.objects.create(
                order=new_order,
                product=item.product,
                quantity=item.quantity,
                unit_price=item.unit_price,
                total_price=item.total_price
            )
            # Update stock
            item.product.stock_quantity -= item.quantity
            item.product.save()
        else:
            messages.warning(request, f'Insufficient stock for {item.product.name}')
    
    # Recalculate totals
    order_items = POSOrderItem.objects.filter(order=new_order)
    subtotal = sum(item.total_price for item in order_items)
    tax_amount = subtotal * 0.075
    final_total = subtotal + tax_amount
    
    new_order.total_amount = subtotal
    new_order.tax_amount = tax_amount
    new_order.final_amount = final_total
    new_order.save()
    
    # Set as current order
    request.session['current_order_id'] = new_order.id
    
    messages.success(request, 'Sale repeated successfully!')
    return redirect('sales:pos')

from django.shortcuts import render
from django.contrib.auth.decorators import login_required
from django.utils import timezone
from datetime import timedelta
from django.db.models import Sum, Count
from .models import POSOrder, DailySalesSummary

@login_required
@role_required(['admin', 'manager'])
def payment_summary(request):
    """
    Payment summary view for displaying analytics and sales data.
    This function dynamically generates summaries for today and the last 7 days.
    """
    from django.utils import timezone
    from datetime import timedelta
    from django.db.models import Sum, Count

    # Get today's date
    today = timezone.now().date()

    # Generate today's summary
    today_orders = POSOrder.objects.filter(created_at__date=today, status='completed')
    today_summary_data = {
        'date': today,
        'total_sales': today_orders.aggregate(total=Sum('final_amount'))['total'] or 0,
        'total_transactions': today_orders.count(),
    }

    # Calculate method-specific totals for today
    for method in ['pos', 'transfer', 'cash', 'mobile_money']:
        method_orders = today_orders.filter(payment_method=method)
        today_summary_data[f'{method}_sales'] = method_orders.aggregate(
            total=Sum('final_amount')
        )['total'] or 0
        today_summary_data[f'{method}_transactions'] = method_orders.count()

    # Save or update today's summary in the database
    today_summary, created = DailySalesSummary.objects.update_or_create(
        date=today,
        defaults={
            'total_revenue': today_summary_data['total_sales'],
            'total_transactions': today_summary_data['total_transactions'],
            'cash_revenue': today_summary_data.get('cash_sales', 0),
            'cash_transactions': today_summary_data.get('cash_transactions', 0),
            'transfer_revenue': today_summary_data.get('transfer_sales', 0),
            'transfer_transactions': today_summary_data.get('transfer_transactions', 0),
            'pos_revenue': today_summary_data.get('pos_sales', 0),
            'pos_transactions': today_summary_data.get('pos_transactions', 0),
            'mobile_money_revenue': today_summary_data.get('mobile_money_sales', 0),
            'mobile_money_transactions': today_summary_data.get('mobile_money_transactions', 0),
        }
    )

    # Generate summaries for the last 7 days
    recent_dates = [today - timedelta(days=i) for i in range(7)]
    recent_summaries = []
    for date in recent_dates:
        orders = POSOrder.objects.filter(created_at__date=date, status='completed')
        summary_data = {
            'date': date,
            'total_sales': orders.aggregate(total=Sum('final_amount'))['total'] or 0,
            'total_transactions': orders.count(),
        }

        # Calculate method-specific totals for each day
        for method in ['pos', 'transfer', 'cash', 'mobile_money']:
            method_orders = orders.filter(payment_method=method)
            summary_data[f'{method}_sales'] = method_orders.aggregate(
                total=Sum('final_amount')
            )['total'] or 0
            summary_data[f'{method}_transactions'] = method_orders.count()

        # Save or update the summary in the database
        summary, _ = DailySalesSummary.objects.update_or_create(
            date=date,
            defaults={
                'total_revenue': summary_data['total_sales'],
                'total_transactions': summary_data['total_transactions'],
                'cash_revenue': summary_data.get('cash_sales', 0),
                'cash_transactions': summary_data.get('cash_transactions', 0),
                'transfer_revenue': summary_data.get('transfer_sales', 0),
                'transfer_transactions': summary_data.get('transfer_transactions', 0),
                'pos_revenue': summary_data.get('pos_sales', 0),
                'pos_transactions': summary_data.get('pos_transactions', 0),
                'mobile_money_revenue': summary_data.get('mobile_money_sales', 0),
                'mobile_money_transactions': summary_data.get('mobile_money_transactions', 0),
            }
        )
        recent_summaries.append(summary)

    # Fetch recent summaries (last 7 days) for display
    recent_summaries = DailySalesSummary.objects.filter(
        date__gte=today - timedelta(days=7)
    ).order_by('-date')

    # Calculate payment method distribution for today
    payment_methods = POSOrder.objects.filter(
        created_at__date=today,
        status='completed'
    ).values('payment_method').annotate(
        total=Sum('final_amount'),
        count=Count('id')
    ).order_by('-total')

    # Calculate percentages for payment methods
    total_amount = sum(pm['total'] or 0 for pm in payment_methods)
    for pm in payment_methods:
        pm['percentage'] = round(((pm['total'] or 0) / total_amount) * 100, 2) if total_amount > 0 else 0

    # Map payment method codes to human-readable names
    PAYMENT_METHOD_CHOICES = dict(POSOrder.PAYMENT_METHODS)
    for pm in payment_methods:
        pm['display_name'] = PAYMENT_METHOD_CHOICES.get(pm['payment_method'], "Unknown")

    # Context dictionary with detailed explanation of each variable
    context = {
        'summary': today_summary,  # Today's summary
        'today_summary': today_summary,  # Duplicate for template consistency
        'recent_summaries': recent_summaries,  # Last 7 days' summaries
        'payment_methods': payment_methods,  # Payment method distribution
        'user_role': request.user.role,  # User role (e.g., admin, cashier)
        'user_name': request.user.get_full_name() or request.user.username,  # User name
    }

    return render(request, 'sales/payment_summary.html', context)
    
    
# sales/views.py (FINAL - Complete working version)
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.http import JsonResponse, HttpResponse
from django.views.decorators.csrf import csrf_exempt
from django.utils import timezone
from django.db import transaction
from django.db.models import Sum, Count, Q
from datetime import datetime, timedelta
import csv
from inventory.models import Product
from core.models import SystemSettings, CustomField, DynamicFormData
from .models import POSOrder, POSOrderItem, PaymentTransaction, DailySalesSummary, UnusualTransaction
from .forms import POSOrderForm, POSOrderItemForm

# sales/views.py
from core.utils import filter_orders_by_user_branch

# sales/views.py

from django.db.models import Sum, Count  # already imported, just make sure Sum is there
from core.models import Branch  # make sure this import exists at the top

@login_required
def daily_dashboard(request):
    """Today's sales dashboard – branch aware"""
    today = timezone.now().date()

    user = request.user
    selected_branch_id = request.GET.get("branch", "all")

    # Base queryset: today's completed orders
    orders_qs = POSOrder.objects.filter(
        created_at__date=today,
        status="completed",
    )

    # --- ROLE / BRANCH LOGIC (Option A strict) ---
    if user.role in ("manager", "cashier"):
        # Force their own branch
        if user.branch:
            orders_qs = orders_qs.filter(branch=user.branch)
            selected_branch_id = str(user.branch.id)
        else:
            # No branch assigned = no orders
            orders_qs = POSOrder.objects.none()
            selected_branch_id = "none"
    else:
        # superadmin / admin can choose branch from dropdown
        if selected_branch_id and selected_branch_id != "all":
            try:
                branch_id = int(selected_branch_id)
                orders_qs = orders_qs.filter(branch_id=branch_id)
            except ValueError:
                pass  # ignore bad input

    # --- Compute summary from filtered orders_qs ---
    total_revenue = orders_qs.aggregate(total=Sum("final_amount"))["total"] or 0
    total_transactions = orders_qs.count()

    # Payment method breakdown
    def pm_totals(method_code):
        m_qs = orders_qs.filter(payment_method=method_code)
        return (
            m_qs.aggregate(total=Sum("final_amount"))["total"] or 0,
            m_qs.count(),
        )

    cash_revenue, cash_transactions = pm_totals("cash")
    transfer_revenue, transfer_transactions = pm_totals("transfer")
    pos_revenue, pos_transactions = pm_totals("pos")
    mobile_revenue, mobile_transactions = pm_totals("mobile_money")

    # Profit + credit outstanding based on filtered orders
    from decimal import Decimal
    total_profit = Decimal("0")
    order_items = POSOrderItem.objects.filter(order__in=orders_qs).select_related("product")
    for item in order_items:
        selling = item.unit_price or Decimal("0")
        cost = (item.product.cost_price or Decimal("0")) if item.product else Decimal("0")
        qty = item.quantity or 0
        total_profit += (selling - cost) * qty

    credit_outstanding = (
        orders_qs.filter(balance_amount__gt=0)
        .aggregate(total=Sum("balance_amount"))["total"] or 0
    )

    # Build a simple "summary" object so template works unchanged
    class SummaryObj:
        pass

    summary = SummaryObj()
    summary.total_revenue = total_revenue
    summary.total_transactions = total_transactions
    summary.cash_revenue = cash_revenue
    summary.cash_transactions = cash_transactions
    summary.transfer_revenue = transfer_revenue
    summary.transfer_transactions = transfer_transactions
    summary.pos_revenue = pos_revenue
    summary.pos_transactions = pos_transactions
    summary.mobile_money_revenue = mobile_revenue
    summary.mobile_money_transactions = mobile_transactions
    summary.total_profit = total_profit
    summary.credit_outstanding = credit_outstanding

    # --- Low stock products (global for now) ---
    low_stock_products = Product.objects.filter(
        stock_status__in=["low_stock", "out_of_stock"]
    ).order_by("stock_quantity")[:10]

    # --- Top selling items (last 24h) from filtered orders ---
    last_24h_start = timezone.now() - timedelta(hours=24)
    top_items_qs = POSOrderItem.objects.filter(
        order__created_at__gte=last_24h_start,
        order__status="completed",
    )

    # Apply same branch visibility rules to items
    if user.role in ("manager", "cashier"):
        if user.branch:
            top_items_qs = top_items_qs.filter(order__branch=user.branch)
        else:
            top_items_qs = POSOrderItem.objects.none()
    else:
        if selected_branch_id and selected_branch_id not in ("all", "none"):
            try:
                branch_id = int(selected_branch_id)
                top_items_qs = top_items_qs.filter(order__branch_id=branch_id)
            except ValueError:
                pass

    top_selling_items = (
        top_items_qs.values("product__name", "product__sku")
        .annotate(
            total_quantity=Sum("quantity"),
            total_revenue=Sum("total_price"),
        )
        .order_by("-total_quantity")[:5]
    )

    # For branch dropdown on the page (if your template uses it)
    branches = Branch.objects.filter(is_active=True).order_by("name")

    context = {
        "summary": summary,
        "top_selling_items": top_selling_items,
        "low_stock_products": low_stock_products,
        "today_orders": orders_qs.order_by("-created_at"),
        "branches": branches,
        "selected_branch_id": selected_branch_id,
        "user_role": user.role,
        "user_name": user.get_full_name() or user.username,
    }
    return render(request, "sales/daily_dashboard.html", context)


# sales/views.py

@login_required
@role_required(['admin', 'manager'])
def yesterday_summary(request):
    """Yesterday's sales summary – branch aware"""
    yesterday = timezone.now().date() - timedelta(days=1)
    user = request.user
    selected_branch_id = request.GET.get("branch", "all")

    # Base queryset: yesterday's completed orders
    orders_qs = POSOrder.objects.filter(
        created_at__date=yesterday,
        status="completed",
    )

    # --- ROLE / BRANCH LOGIC ---
    if user.role in ("manager", "cashier"):
        if user.branch:
            orders_qs = orders_qs.filter(branch=user.branch)
            selected_branch_id = str(user.branch.id)
        else:
            orders_qs = POSOrder.objects.none()
            selected_branch_id = "none"
    else:
        if selected_branch_id and selected_branch_id != "all":
            try:
                branch_id = int(selected_branch_id)
                orders_qs = orders_qs.filter(branch_id=branch_id)
            except ValueError:
                pass

    # Summary (local, not saved to DB to avoid corrupting global summary)
    from decimal import Decimal

    total_revenue = orders_qs.aggregate(total=Sum("final_amount"))["total"] or Decimal("0")
    total_transactions = orders_qs.count()

    def pm_totals(method_code):
        m_qs = orders_qs.filter(payment_method=method_code)
        return (
            m_qs.aggregate(total=Sum("final_amount"))["total"] or Decimal("0"),
            m_qs.count(),
        )

    cash_revenue, cash_transactions = pm_totals("cash")
    transfer_revenue, transfer_transactions = pm_totals("transfer")
    pos_revenue, pos_transactions = pm_totals("pos")
    mobile_revenue, mobile_transactions = pm_totals("mobile_money")

    class SummaryObj:
        pass

    summary = SummaryObj()
    summary.total_revenue = total_revenue
    summary.total_transactions = total_transactions
    summary.cash_revenue = cash_revenue
    summary.cash_transactions = cash_transactions
    summary.transfer_revenue = transfer_revenue
    summary.transfer_transactions = transfer_transactions
    summary.pos_revenue = pos_revenue
    summary.pos_transactions = pos_transactions
    summary.mobile_money_revenue = mobile_revenue
    summary.mobile_money_transactions = mobile_transactions

    # Unusual transactions should also follow branch filter
    unusual_transactions = UnusualTransaction.objects.filter(
        order__in=orders_qs
    ).select_related("order")

    # Apply extra filters
    payment_method = request.GET.get("payment_method", "")
    min_amount = request.GET.get("min_amount", "")

    filtered_orders = orders_qs

    if payment_method:
        filtered_orders = filtered_orders.filter(payment_method=payment_method)

    if min_amount:
        try:
            min_amount_value = float(min_amount)
            filtered_orders = filtered_orders.filter(final_amount__gte=min_amount_value)
        except ValueError:
            pass

    # CSV export uses branch + filters
    if request.GET.get("export") == "csv":
        return export_yesterday_orders_csv(filtered_orders)

    branches = Branch.objects.filter(is_active=True).order_by("name")

    context = {
        "summary": summary,
        "yesterday_orders": filtered_orders.order_by("-created_at"),
        "unusual_transactions": unusual_transactions,
        "payment_method": payment_method,
        "min_amount": min_amount,
        "branches": branches,
        "selected_branch_id": selected_branch_id,
        "user_role": user.role,
        "user_name": user.get_full_name() or user.username,
    }
    return render(request, "sales/yesterday_summary.html", context)


def export_yesterday_orders_csv(orders):
    """Export yesterday's orders to CSV"""
    response = HttpResponse(content_type='text/csv')
    response['Content-Disposition'] = 'attachment; filename="yesterday_orders.csv"'
    
    writer = csv.writer(response)
    writer.writerow([
        'Order Number', 'Date', 'Cashier', 'Payment Method', 
        'Total Amount', 'Customer Name', 'Customer Phone'
    ])
    
    for order in orders:
        writer.writerow([
            order.order_number,
            order.created_at.strftime('%Y-%m-%d %H:%M'),
            order.cashier,
            order.get_payment_method_display(),
            order.final_amount,
            order.customer_name,
            order.customer_phone
        ])
    
    return response
    


@login_required
def generate_receipt_with_qr(request, order_id):
    """Generate receipt with QR code for verification"""
    import qrcode
    from reportlab.pdfgen import canvas
    from reportlab.lib.pagesizes import A4
    from reportlab.lib.colors import Color
    from io import BytesIO

    order = get_object_or_404(POSOrder, id=order_id)
    order_items = POSOrderItem.objects.filter(order=order).select_related('product')

    # Get system settings
    system_settings = SystemSettings.objects.first()

    # Generate QR code
    qr_data = f"{request.build_absolute_uri('/sales/verify-order/')}?order={order.order_number}"
    qr = qrcode.QRCode(version=1, box_size=10, border=5)
    qr.add_data(qr_data)
    qr.make(fit=True)

    img = qr.make_image(fill_color="black", back_color="white")
    buffer = BytesIO()
    img.save(buffer, format='PNG')
    qr_image = buffer.getvalue()

    # Create PDF
    buffer = BytesIO()
    p = canvas.Canvas(buffer, pagesize=A4)
    width, height = A4

    # -------------------------
    # BACKGROUND TEXTURE
    # -------------------------
    p.saveState()

    texture_color = Color(0.85, 0.85, 0.85, alpha=0.15)  # VERY light grey
    p.setFillColor(texture_color)
    p.setFont("Helvetica", 22)

    # Repeat the order number diagonally across the page
    for y in range(0, int(height), 120):
        for x in range(0, int(width), 250):
            p.drawString(x, y, str(order.order_number))

    p.restoreState()

    # -------------------------
    # HEADER (More Nigerian POS Style)
    # -------------------------
    p.setFont("Helvetica-Bold", 20)
    p.drawString(40, height - 60, system_settings.business_name or "MODERN POS")

    p.setFont("Helvetica", 11)
    p.drawString(40, height - 82, system_settings.business_address or "")

    p.setFont("Helvetica-Bold", 12)
    p.drawString(40, height - 110, f"ORDER RECEIPT - #{order.order_number}")

    p.setFont("Helvetica", 10)
    p.drawString(40, height - 130, f"Date: {order.created_at.strftime('%Y-%m-%d %H:%M')}")
    p.drawString(250, height - 130, f"Cashier: {order.cashier}")

    # -------------------------
    # ITEMS SECTION
    # -------------------------
    p.setFont("Helvetica-Bold", 12)
    y_position = height - 170
    p.drawString(40, y_position, "ITEMS PURCHASED")

    p.setFont("Helvetica", 10)
    y_position -= 20

    for item in order_items:
        p.drawString(40, y_position, f"{item.quantity} x {item.product.name}")
        p.drawRightString(400, y_position, f"₦{item.total_price}")
        y_position -= 16

    # -------------------------
    # TOTALS
    # -------------------------
    y_position -= 10
    p.line(40, y_position, 400, y_position)
    y_position -= 20

    p.setFont("Helvetica-Bold", 11)
    p.drawString(40, y_position, f"Subtotal:")
    p.drawRightString(400, y_position, f"₦{order.total_amount}")

    y_position -= 18
    p.drawString(40, y_position, "Tax:")
    p.drawRightString(400, y_position, f"₦{order.tax_amount}")

    y_position -= 18
    p.drawString(40, y_position, "Total Amount:")
    p.drawRightString(400, y_position, f"₦{order.final_amount}")

    y_position -= 18
    p.drawString(40, y_position, f"Payment Method:")
    p.drawRightString(400, y_position, order.get_payment_method_display())

    # -------------------------
    # QR CODE
    # -------------------------
    if qr_image:
        p.setFont("Helvetica", 10)
        p.drawString(40, y_position - 40, "Scan to verify order:")
        p.drawImage(BytesIO(qr_image), 40, y_position - 140, width=110, height=110)

    # -------------------------
    # FOOTER
    # -------------------------
    p.setFont("Helvetica-Oblique", 10)
    p.drawString(40, 40, system_settings.receipt_footer or "Thank you for your patronage!")

    p.showPage()
    p.save()

    buffer.seek(0)
    return HttpResponse(buffer.getvalue(), content_type='application/pdf')


from django.shortcuts import render, redirect, get_object_or_404
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.db.models import Q
from django.http import HttpResponse
from django.utils import timezone
from django.utils.dateparse import parse_date  # Import parse_date from Django utilities

import csv

from .models import POSOrder, POSOrderItem, PaymentTransaction


@login_required
@role_required(['admin'])
def transaction_history(request):
    """Show recent POS orders with search, day/range filter, and pagination."""
    from django.core.paginator import Paginator
    today = timezone.now().date()
    date_str    = request.GET.get("date") or ""
    date_from   = request.GET.get("date_from") or ""
    date_to     = request.GET.get("date_to") or ""
    search_query = request.GET.get("q") or ""

    orders = POSOrder.objects.filter(status="completed").order_by("-created_at")

    # Date range takes priority over single date
    if date_from or date_to:
        if date_from:
            from django.utils.dateparse import parse_date as _pd
            df = _pd(date_from)
            if df:
                orders = orders.filter(created_at__date__gte=df)
        if date_to:
            from django.utils.dateparse import parse_date as _pd2
            dt = _pd2(date_to)
            if dt:
                orders = orders.filter(created_at__date__lte=dt)
    elif date_str:
        selected_date = parse_date(date_str) or today
        orders = orders.filter(created_at__date=selected_date)
    else:
        selected_date = today
        orders = orders.filter(created_at__date=selected_date)

    if search_query:
        orders = orders.filter(
            Q(order_number__icontains=search_query)
            | Q(customer_name__icontains=search_query)
            | Q(customer_phone__icontains=search_query)
        )

    paginator  = Paginator(orders, 30)
    page_number = request.GET.get("page")
    page_obj   = paginator.get_page(page_number)

    context = {
        "orders": page_obj,
        "page_obj": page_obj,
        "selected_date": date_str or today,
        "date_from": date_from,
        "date_to": date_to,
        "search_query": search_query,
    }
    return render(request, "sales/transaction_history.html", context)


@login_required
@role_required(['admin', 'manager'])
def export_transactions(request):
    """Export filtered POS orders (same filters as transaction_history)."""
    today = timezone.now().date()
    date_str = request.GET.get("date") or ""
    search_query = request.GET.get("q") or ""

    if date_str:
        # Use parse_date to convert the date string into a date object
        selected_date = parse_date(date_str) or today
    else:
        selected_date = today

    orders = POSOrder.objects.filter(status="completed").order_by("-created_at")

    if selected_date:
        orders = orders.filter(created_at__date=selected_date)

    if search_query:
        orders = orders.filter(
            Q(order_number__icontains=search_query)
            | Q(customer_name__icontains=search_query)
            | Q(customer_phone__icontains=search_query)
        )

    response = HttpResponse(content_type="text/csv")
    filename = f"transactions_{selected_date.isoformat()}.csv"
    response["Content-Disposition"] = f'attachment; filename="{filename}"'

    writer = csv.writer(response)
    writer.writerow(
        [
            "order_number",
            "date",
            "time",
            "customer_name",
            "customer_phone",
            "payment_method",
            "total_amount",
            "discount_amount",
            "final_amount",
            "status",
        ]
    )

    for order in orders:
        dt = timezone.localtime(order.created_at)
        writer.writerow(
            [
                order.order_number,
                dt.date().isoformat(),
                dt.time().strftime("%H:%M:%S"),
                order.customer_name or "",
                order.customer_phone or "",
                order.payment_method,
                order.total_amount,
                order.discount_amount,
                order.final_amount,
                order.status,
            ]
        )

    return response




@login_required
@role_required(['admin', 'manager'])
def export_all_orders(request):
    """Export all POS orders as CSV (summary)."""
    response = HttpResponse(content_type="text/csv")
    filename = f"orders_all_{timezone.now().date().isoformat()}.csv"
    response["Content-Disposition"] = f'attachment; filename="{filename}"'

    writer = csv.writer(response)
    writer.writerow(
        [
            "order_number",
            "created_date",
            "created_time",
            "customer_name",
            "customer_phone",
            "payment_method",
            "total_amount",
            "discount_amount",
            "final_amount",
            "status",
        ]
    )

    orders = POSOrder.objects.all().order_by("-created_at")
    for order in orders:
        dt = timezone.localtime(order.created_at)
        writer.writerow(
            [
                order.order_number,
                dt.date().isoformat(),
                dt.time().strftime("%H:%M:%S"),
                order.customer_name or "",
                order.customer_phone or "",
                order.payment_method,
                order.total_amount,
                order.discount_amount,
                order.final_amount,
                order.status,
            ]
        )

    return response


@login_required
@role_required(['admin', 'manager'])
def export_order_items(request):
    """Export all order line items as CSV."""
    response = HttpResponse(content_type="text/csv")
    filename = f"order_items_{timezone.now().date().isoformat()}.csv"
    response["Content-Disposition"] = f'attachment; filename="{filename}"'

    writer = csv.writer(response)
    writer.writerow(
        [
            "order_number",
            "order_date",
            "product_name",
            "product_sku",
            "quantity",
            "unit_price",
            "line_total",
        ]
    )

    items = POSOrderItem.objects.select_related("order", "product").all().order_by(
        "-order__created_at"
    )
    for item in items:
        order = item.order
        product = item.product
        dt = timezone.localtime(order.created_at)
        writer.writerow(
            [
                order.order_number,
                dt.date().isoformat(),
                product.name if product else "",
                product.sku if product else "",
                item.quantity,
                item.unit_price,
                item.total_price,
            ]
        )

    return response


@login_required
@role_required(['admin', 'manager'])
def export_payments(request):
    """Export all payment transactions as CSV."""
    response = HttpResponse(content_type="text/csv")
    filename = f"payments_{timezone.now().date().isoformat()}.csv"
    response["Content-Disposition"] = f'attachment; filename="{filename}"'

    writer = csv.writer(response)
    writer.writerow(
        [
            "order_number",
            "payment_method",
            "amount",
            "reference_number",
            "transaction_id",
            "status",
            "created_at",
        ]
    )

    payments = PaymentTransaction.objects.select_related("order").all().order_by(
        "-created_at"
    )
    for p in payments:
        writer.writerow(
            [
                p.order.order_number if p.order else "",
                p.payment_method,
                p.amount,
                p.reference_number,
                p.transaction_id,
                p.status,
                p.created_at,
            ]
        )

    return response


from decimal import Decimal
from django.views.decorators.http import require_POST

@login_required
@require_POST
def api_create_order(request):
    """JSON endpoint for creating POS orders (used by offline sync)."""
    try:
        data = json.loads(request.body.decode("utf-8"))
    except json.JSONDecodeError:
        return JsonResponse({"success": False, "error": "Invalid JSON"}, status=400)

    items = data.get("items") or []
    if not items:
        return JsonResponse({"success": False, "error": "No items"}, status=400)

    totals = data.get("totals") or {}
    payment_method = data.get("payment_method") or "pos"
    reference = data.get("reference_number") or ""
    customer_name = data.get("customer_name") or ""
    customer_phone = data.get("customer_phone") or ""
    client_id = data.get("client_id")

    # ── Rate limiting (re-uses existing limit check) ──────────────
    try:
        check_limit_or_block("orders_per_day")
    except Exception as e:
        return JsonResponse({"success": False, "error": str(e)}, status=429)

    # ── Client-ID deduplication for offline retries ────────────────
    client_id = data.get("client_id") or ""
    if client_id:
        existing = POSOrder.objects.filter(client_id=client_id).first()
        if existing:
            return JsonResponse({
                "success": True,
                "order_id": existing.id,
                "order_number": existing.order_number,
                "redirect_url": f"/sales/receipt/{existing.id}/",
                "client_id": client_id,
                "duplicate": True,
            })

    try:
        with transaction.atomic():
            try:
                subtotal = Decimal(str(totals.get("subtotal", 0)))
                grand_total = Decimal(
                    str(totals.get("grand_total", totals.get("grandTotal", subtotal)))
                )
            except Exception:
                subtotal = Decimal("0")
                for item in items:
                    price = Decimal(str(item.get("price", 0)))
                    qty = int(item.get("quantity") or 1)
                    subtotal += price * qty
                grand_total = subtotal

            # NEW: handle payment status + amount paid
            payment_status = data.get("payment_status") or "full"
            raw_amount_paid = data.get("amount_paid")

            try:
                amount_paid = None
                if raw_amount_paid not in (None, "",):
                    amount_paid = Decimal(str(raw_amount_paid))
            except Exception:
                amount_paid = None

            # Logic:
            # - full: amount_paid = grand_total (if empty)
            # - partial/credit: if empty -> 0
            if payment_status == "full":
                if amount_paid is None:
                    amount_paid = grand_total
            else:
                if amount_paid is None:
                    amount_paid = Decimal("0")

            if amount_paid < 0:
                amount_paid = Decimal("0")
            if amount_paid > grand_total:
                amount_paid = grand_total

            balance_amount = grand_total - amount_paid

            user_branch = getattr(request.user, "branch", None)

            if user_branch is None:
    # fallback to first active branch so ALL orders have a branch
                user_branch = Branch.objects.filter(is_active=True).first()

            # ── Mixed alphanumeric order number (API terminal) ─────
            import string as _str, random as _rand
            _suffix = ''.join(_rand.choices(_str.ascii_uppercase + _str.digits, k=5))
            _order_number = f"ORD-{timezone.now().strftime('%m%d%H%M')}-{_suffix}"

            order = POSOrder.objects.create(
                order_number=_order_number,
                client_id=client_id,
                total_amount=subtotal,
                tax_amount=Decimal("0"),
                discount_amount=Decimal("0"),
                final_amount=grand_total,
                payment_method=payment_method,
                status="completed",
                payment_status=payment_status,
                amount_paid=amount_paid,
                balance_amount=balance_amount,
                customer_name=customer_name,
                customer_phone=customer_phone,
                cashier=request.user.username,
                branch=user_branch,
            )

            # (your existing loop creating POSOrderItem + reducing stock stays the same)
            # (your existing loop creating POSOrderItem + reducing stock stays the same)
            
                        # ---------- FIX: CREATE ORDER ITEMS ----------
            raw_items = (
                data.get("items")
                or data.get("cart")
                or data.get("cartItems")
                or data.get("products")
                or []
            )
            
            for item in raw_items:
                # Try different possible field names
                product_id = (
                    item.get("product_id")
                    or item.get("productId")
                    or item.get("id")
                    or item.get("product")
                )
            
                if not product_id:
                    continue  # skip invalid entry
            
                qty = int(item.get("quantity") or item.get("qty") or 1)
                price = Decimal(str(item.get("price") or item.get("unit_price") or "0"))
            
                # Create line item
                POSOrderItem.objects.create(
                    order=order,
                    product_id=product_id,
                    quantity=qty,
                    unit_price=price,
                    total_price=price * qty,
                )
                # Reduce stock safely
                product = Product.objects.select_for_update().get(id=product_id)
                if product.stock_quantity < qty:
                    raise ValueError(f"Insufficient stock for '{product.name}': only {product.stock_quantity} left.")
                product.stock_quantity -= qty
                product.save(update_fields=['stock_quantity'])
                # Snapshot product name on the order item
                POSOrderItem.objects.filter(order=order, product_id=product_id, quantity=qty).update(
                    product_name=product.name
                )
            # Record only what was actually paid now
            if amount_paid > 0:
                PaymentTransaction.objects.create(
                    order=order,
                    payment_method=payment_method,
                    amount=amount_paid,
                    reference_number=reference,
                    status="completed",
                )

    except ValueError as e:
        # Stock / validation conflicts ? tell client clearly
        return JsonResponse(
            {"success": False, "error": str(e), "code": "STOCK_CONFLICT"},
            status=400,
        )
    except Exception as e:
        # Unexpected error
        return JsonResponse(
            {"success": False, "error": "Server error"}, status=500
        )

    return JsonResponse(
        {
            "success": True,
            "order_id": order.id,
            "order_number": order.order_number,
            "redirect_url": f"/sales/receipt/{order.id}/",
            "client_id": client_id,
        }
    )
from django.db.models import Sum, Count, Max  # add Max here with Sum, Count, Q

@login_required
@role_required(['admin', 'manager'])
def credit_customers(request):
    q = request.GET.get("q", "").strip()

    credit_orders = POSOrder.objects.filter(
        Q(payment_status__iexact='credit') |
        Q(balance_amount__gt=0)
    ).order_by('-created_at')

    if q:
        credit_orders = credit_orders.filter(
            Q(customer_name__icontains=q) |
            Q(customer_phone__icontains=q)
        )

    return render(request, 'sales/credit_customers.html', {
        'credit_orders': credit_orders,
        'user_role': request.user.role,
        'user_name': request.user.get_full_name() or request.user.username,
    })



from accounts.models import CustomUser
from decimal import Decimal
from django.db.models import F


@login_required
@role_required(['admin'])
def staff_performance_dashboard(request):
    """
    Show performance metrics per cashier/staff:
    - total orders
    - total sales amount
    - estimated profit (using cost_price)
    - number of unusual transactions
    """
    today = timezone.now().date()

    # Get all cashiers & managers
    staff_qs = CustomUser.objects.filter(role__in=["cashier", "manager", "admin"])

    staff_stats = []

    for user in staff_qs:
        user_orders = POSOrder.objects.filter(
            cashier=user.username,
            status="completed",
        )

        total_orders = user_orders.count()
        total_sales = user_orders.aggregate(total=Sum("final_amount"))["total"] or Decimal("0")

        # Profit per user using POSOrderItem + cost_price
        items = POSOrderItem.objects.filter(order__in=user_orders).select_related("product")
        profit = Decimal("0")

        for item in items:
            selling = item.unit_price or Decimal("0")
            cost = item.product.cost_price or Decimal("0")
            qty = item.quantity or 0
            profit += (selling - cost) * qty

        unusual_count = UnusualTransaction.objects.filter(order__in=user_orders).count()

        staff_stats.append(
            {
                "user": user,
                "total_orders": total_orders,
                "total_sales": total_sales,
                "total_profit": profit,
                "unusual_transactions": unusual_count,
            }
        )

    # Sort by profit (highest first)
    staff_stats.sort(key=lambda x: x["total_profit"], reverse=True)

    return render(
        request,
        "sales/staff_performance.html",
        {
            "staff_stats": staff_stats,
            "today": today,
        },
    )
from decimal import Decimal

from decimal import Decimal

@login_required
@role_required(['admin', 'manager'])
def clear_credit(request, order_id):
    order = get_object_or_404(POSOrder, id=order_id)

    if request.method == "POST":
        amount = request.POST.get("amount")

        try:
            amount = Decimal(amount)
        except:
            messages.error(request, "Invalid amount")
            return redirect(request.META.get("HTTP_REFERER", "/"))

        order.clear_credit(amount)
        messages.success(request, "Customer credit cleared successfully.")
        return redirect("sales:credit_customers")

    return render(request, "sales/clear_credit.html", {"order": order})


# ═══════════════════════════════════════════════════════════
#  FEATURE 2: P&L REPORT
# ═══════════════════════════════════════════════════════════

@login_required
@role_required(['admin', 'manager'])
def pnl_report(request):
    """One-click P&L report with date range filter and export."""
    from accounts.audit import log_action
    log_action(request, 'view_pnl', target='P&L Report')

    today = timezone.now().date()
    date_from_str = request.GET.get('date_from', str(today))
    date_to_str = request.GET.get('date_to', str(today))

    try:
        date_from = datetime.strptime(date_from_str, '%Y-%m-%d').date()
    except ValueError:
        date_from = today

    try:
        date_to = datetime.strptime(date_to_str, '%Y-%m-%d').date()
    except ValueError:
        date_to = today

    from decimal import Decimal

    # All completed orders in range
    orders = POSOrder.objects.filter(
        created_at__date__gte=date_from,
        created_at__date__lte=date_to,
        status='completed',
    )

    # Apply branch filter
    user = request.user
    if user.role in ('manager', 'cashier') and getattr(user, 'branch', None):
        orders = orders.filter(branch=user.branch)

    total_revenue = orders.aggregate(t=Sum('final_amount'))['t'] or Decimal('0')
    total_orders_count = orders.count()
    credit_outstanding = orders.filter(balance_amount__gt=0).aggregate(t=Sum('balance_amount'))['t'] or Decimal('0')

    total_cost = Decimal('0')
    total_profit = Decimal('0')
    items = POSOrderItem.objects.filter(order__in=orders).select_related('product')
    for item in items:
        cost = (item.product.cost_price or Decimal('0'))
        total_cost += cost * item.quantity
        total_profit += (item.unit_price - cost) * item.quantity

    # Payment breakdown
    payment_breakdown = []
    for code, label in POSOrder.PAYMENT_METHODS:
        m_rev = orders.filter(payment_method=code).aggregate(t=Sum('final_amount'))['t'] or Decimal('0')
        m_count = orders.filter(payment_method=code).count()
        if m_count > 0:
            payment_breakdown.append({'code': code, 'label': label, 'revenue': m_rev, 'count': m_count})

    # Per-day breakdown
    from django.db.models.functions import TruncDate
    daily_data = (
        orders.annotate(day=TruncDate('created_at'))
        .values('day')
        .annotate(rev=Sum('final_amount'), cnt=Count('id'))
        .order_by('day')
    )

    context = {
        'date_from': date_from,
        'date_to': date_to,
        'total_revenue': total_revenue,
        'total_cost': total_cost,
        'total_profit': total_profit,
        'credit_outstanding': credit_outstanding,
        'total_orders_count': total_orders_count,
        'payment_breakdown': payment_breakdown,
        'daily_data': daily_data,
        'user_role': user.role,
        'user_name': user.get_full_name() or user.username,
    }
    return render(request, 'sales/pnl_report.html', context)


@login_required
@role_required(['admin', 'manager'])
def export_pnl(request):
    """Export P&L as Excel or CSV."""
    import openpyxl
    from openpyxl.styles import Font, PatternFill, Alignment
    from decimal import Decimal

    today = timezone.now().date()
    date_from_str = request.GET.get('date_from', str(today))
    date_to_str = request.GET.get('date_to', str(today))

    try:
        date_from = datetime.strptime(date_from_str, '%Y-%m-%d').date()
    except ValueError:
        date_from = today
    try:
        date_to = datetime.strptime(date_to_str, '%Y-%m-%d').date()
    except ValueError:
        date_to = today

    orders = POSOrder.objects.filter(
        created_at__date__gte=date_from,
        created_at__date__lte=date_to,
        status='completed',
    ).order_by('created_at')

    user = request.user
    if user.role in ('manager', 'cashier') and getattr(user, 'branch', None):
        orders = orders.filter(branch=user.branch)

    wb = openpyxl.Workbook()
    ws = wb.active
    ws.title = 'P&L Report'

    header_fill = PatternFill('solid', fgColor='667EEA')
    header_font = Font(bold=True, color='FFFFFF')
    bold = Font(bold=True)

    headers = ['Order #', 'Date', 'Cashier', 'Customer', 'Payment', 'Revenue', 'Cost', 'Profit', 'Credit Balance']
    for col, h in enumerate(headers, 1):
        cell = ws.cell(row=1, column=col, value=h)
        cell.font = header_font
        cell.fill = header_fill
        cell.alignment = Alignment(horizontal='center')

    row_idx = 2
    totals = {'revenue': Decimal('0'), 'cost': Decimal('0'), 'profit': Decimal('0')}

    for order in orders:
        items = POSOrderItem.objects.filter(order=order).select_related('product')
        cost = sum((item.product.cost_price or Decimal('0')) * item.quantity for item in items)
        profit = float(order.final_amount) - float(cost)
        totals['revenue'] += order.final_amount
        totals['cost'] += cost
        totals['profit'] += Decimal(str(profit))

        ws.append([
            order.order_number,
            order.created_at.strftime('%Y-%m-%d %H:%M'),
            order.cashier,
            order.customer_name or '—',
            order.get_payment_method_display(),
            float(order.final_amount),
            float(cost),
            profit,
            float(order.balance_amount),
        ])
        row_idx += 1

    # Totals row
    ws.append(['', '', '', '', 'TOTALS', float(totals['revenue']), float(totals['cost']), float(totals['profit']), ''])
    total_row = ws.max_row
    for col in range(1, 10):
        ws.cell(row=total_row, column=col).font = bold

    response = HttpResponse(
        content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
    )
    response['Content-Disposition'] = f'attachment; filename="PnL_{date_from}_to_{date_to}.xlsx"'
    wb.save(response)

    from accounts.audit import log_action
    log_action(request, 'export_data', target=f'P&L {date_from} to {date_to}')
    return response


# ═══════════════════════════════════════════════════════════
#  FEATURE 7: SUSPICIOUS ACTIVITY
# ═══════════════════════════════════════════════════════════

@login_required
@role_required(['admin'])
def suspicious_activity(request):
    """View flagged suspicious transactions."""
    from accounts.audit import log_action
    log_action(request, 'view_transactions', target='Suspicious Activity Page')

    qs = UnusualTransaction.objects.select_related('order').order_by('-flagged_at')

    # Filters
    resolved_filter = request.GET.get('resolved', '')
    cashier_filter = request.GET.get('cashier', '')
    date_from_str = request.GET.get('date_from', '')
    date_to_str = request.GET.get('date_to', '')

    if resolved_filter == 'yes':
        qs = qs.filter(resolved=True)
    elif resolved_filter == 'no':
        qs = qs.filter(resolved=False)

    if cashier_filter:
        qs = qs.filter(order__cashier__icontains=cashier_filter)

    if date_from_str:
        try:
            qs = qs.filter(flagged_at__date__gte=datetime.strptime(date_from_str, '%Y-%m-%d').date())
        except ValueError:
            pass

    if date_to_str:
        try:
            qs = qs.filter(flagged_at__date__lte=datetime.strptime(date_to_str, '%Y-%m-%d').date())
        except ValueError:
            pass

    # Resolve action
    if request.method == 'POST' and request.POST.get('action') == 'resolve':
        flag_id = request.POST.get('flag_id')
        try:
            flag = UnusualTransaction.objects.get(id=flag_id)
            flag.resolved = True
            flag.resolved_by = request.user.username
            flag.resolved_at = timezone.now()
            flag.save()
            messages.success(request, 'Transaction marked as resolved.')
        except UnusualTransaction.DoesNotExist:
            messages.error(request, 'Flag not found.')
        return redirect('sales:suspicious_activity')

    context = {
        'flags': qs,
        'resolved_filter': resolved_filter,
        'cashier_filter': cashier_filter,
        'date_from': date_from_str,
        'date_to': date_to_str,
        'total_unresolved': UnusualTransaction.objects.filter(resolved=False).count(),
        'user_role': request.user.role,
        'user_name': request.user.get_full_name() or request.user.username,
    }
    return render(request, 'sales/suspicious_activity.html', context)


# ═══════════════════════════════════════════════════════════
#  FEATURE 9: CUSTOMER CRUD
# ═══════════════════════════════════════════════════════════

@login_required
@role_required(['admin', 'manager'])
def customer_list(request):
    from .models import Customer
    from django.db.models import Sum, Count, Q

    q   = request.GET.get('q', '').strip()
    tab = request.GET.get('tab', 'all')  # all | credit | history

    # ── All registered customers ──────────────────────────────────────
    customers = Customer.objects.all()
    if q:
        customers = customers.filter(
            Q(name__icontains=q) | Q(phone__icontains=q) | Q(email__icontains=q)
        )

    # ── Customers with pending credit (from POSOrder) ─────────────────
    credit_orders = POSOrder.objects.filter(
        Q(payment_status='credit') | Q(payment_status='partial') | Q(balance_amount__gt=0)
    ).order_by('-created_at')
    if q:
        credit_orders = credit_orders.filter(
            Q(customer_name__icontains=q) | Q(customer_phone__icontains=q)
        )

    # Group credit by customer name for summary
    credit_summary = credit_orders.values('customer_name', 'customer_phone').annotate(
        total_owed=Sum('balance_amount'),
        orders_count=Count('id')
    ).order_by('-total_owed')

    # ── Payment history (fully paid credit orders) ─────────────────────
    payment_history = POSOrder.objects.filter(
        payment_status='full',
        amount_paid__gt=0,
    ).exclude(customer_name='').order_by('-updated_at')
    if q:
        payment_history = payment_history.filter(
            Q(customer_name__icontains=q) | Q(customer_phone__icontains=q)
        )

    # Summary stats for header
    total_outstanding = POSOrder.objects.filter(
        balance_amount__gt=0
    ).aggregate(total=Sum('balance_amount'))['total'] or 0
    total_credit_customers = credit_orders.values('customer_name').distinct().count()

    return render(request, 'sales/customer_list.html', {
        'customers':          customers,
        'credit_summary':     credit_summary,
        'credit_orders':      credit_orders[:100],
        'payment_history':    payment_history[:100],
        'tab':                tab,
        'q':                  q,
        'total_outstanding':  total_outstanding,
        'total_credit_customers': total_credit_customers,
        'user_role': request.user.role,
        'user_name': request.user.get_full_name() or request.user.username,
    })


@login_required
@role_required(['admin', 'manager'])
def customer_create(request):
    from .models import Customer
    from core.utils import check_limit_or_block
    from django.core.exceptions import PermissionDenied
    from accounts.audit import log_action
    from core.email_utils import send_credit_customer_alert

    if request.method == 'POST':
        try:
            check_limit_or_block('customers')
        except PermissionDenied as e:
            messages.error(request, str(e))
            return redirect('sales:customer_list')

        name = request.POST.get('name', '').strip()
        phone = request.POST.get('phone', '').strip()
        email = request.POST.get('email', '').strip()
        address = request.POST.get('address', '').strip()
        notes = request.POST.get('notes', '').strip()

        if not name:
            messages.error(request, 'Customer name is required.')
            return render(request, 'sales/customer_form.html', {
                'mode': 'create', 'user_role': request.user.role,
                'user_name': request.user.get_full_name() or request.user.username,
            })

        customer = Customer.objects.create(
            name=name, phone=phone, email=email,
            address=address, notes=notes,
            created_by=request.user.username
        )
        log_action(request, 'add_customer', target=f'Customer: {customer.name}')
        messages.success(request, f'Customer "{name}" added successfully.')
        return redirect('sales:customer_list')

    return render(request, 'sales/customer_form.html', {
        'mode': 'create',
        'user_role': request.user.role,
        'user_name': request.user.get_full_name() or request.user.username,
    })


@login_required
@role_required(['admin', 'manager'])
def customer_edit(request, pk):
    from .models import Customer
    from accounts.audit import log_action

    customer = get_object_or_404(Customer, pk=pk)

    if request.method == 'POST':
        customer.name = request.POST.get('name', customer.name).strip()
        customer.phone = request.POST.get('phone', customer.phone).strip()
        customer.email = request.POST.get('email', customer.email).strip()
        customer.address = request.POST.get('address', customer.address).strip()
        customer.notes = request.POST.get('notes', customer.notes).strip()
        customer.save()
        log_action(request, 'edit_customer', target=f'Customer: {customer.name}')
        messages.success(request, 'Customer updated.')
        return redirect('sales:customer_list')

    return render(request, 'sales/customer_form.html', {
        'mode': 'edit', 'customer': customer,
        'user_role': request.user.role,
        'user_name': request.user.get_full_name() or request.user.username,
    })


@login_required
@role_required(['admin'])
def customer_delete(request, pk):
    from .models import Customer
    from accounts.audit import log_action

    customer = get_object_or_404(Customer, pk=pk)
    if request.method == 'POST':
        name = customer.name
        customer.delete()
        log_action(request, 'delete_customer', target=f'Customer: {name}')
        messages.success(request, f'Customer "{name}" deleted.')
        return redirect('sales:customer_list')
    return redirect('sales:customer_list')


# ═══════════════════════════════════════════════════════════
#  FEATURE 10: BARCODE POS PAGE
# ═══════════════════════════════════════════════════════════

@login_required
def barcode_pos(request):
    """Dedicated barcode scanner POS page — subscription-gated."""
    from core.utils import is_feature_enabled
    from core.models import Branch
    from accounts.audit import log_action

    if not is_feature_enabled('barcode_scanner'):
        messages.error(request, 'Barcode Scanner POS is not included in your subscription plan. Please contact support to upgrade.')
        return redirect('sales:pos')

    system_settings = SystemSettings.objects.first()

    # Ensure active session order
    order = None
    order_id = request.session.get('barcode_order_id')
    if order_id:
        try:
            order = POSOrder.objects.get(id=order_id, status='pending')
        except POSOrder.DoesNotExist:
            order = None

    if order is None:
        user_branch = getattr(request.user, 'branch', None) or Branch.objects.filter(is_active=True).first()
        order = POSOrder.objects.create(
            order_number=f"BORD-{timezone.now().strftime('%m%d%H%M')}-{''.join(__import__('random').choices(__import__('string').ascii_uppercase+__import__('string').digits,k=5))}",
            total_amount=0, tax_amount=0, final_amount=0,
            cashier=request.user.username,
            payment_method='cash',
            branch=user_branch,
        )
        request.session['barcode_order_id'] = order.id

    if request.method == 'POST':
        action = request.POST.get('action', '').strip()

        if action == 'complete_order':
            from decimal import Decimal
            from core.utils import check_limit_or_block
            from django.core.exceptions import PermissionDenied
            from .suspicious import detect_suspicious
            from core.email_utils import send_credit_customer_alert

            try:
                check_limit_or_block('orders_per_day')
            except PermissionDenied as e:
                messages.error(request, str(e))
                return redirect('sales:barcode_pos')

            payment_method = request.POST.get('payment_method', 'cash')
            customer_name = request.POST.get('customer_name', '')
            customer_phone = request.POST.get('customer_phone', '')
            amount_paid_str = request.POST.get('amount_paid', '')

            order_items = POSOrderItem.objects.filter(order=order)
            subtotal = sum(float(item.total_price) for item in order_items)
            order.total_amount = subtotal
            order.final_amount = subtotal
            order.payment_method = payment_method
            order.customer_name = customer_name
            order.customer_phone = customer_phone

            try:
                amount_paid = Decimal(str(amount_paid_str)) if amount_paid_str else Decimal(str(subtotal))
            except Exception:
                amount_paid = Decimal(str(subtotal))

            order.amount_paid = amount_paid
            order.balance_amount = max(Decimal(str(subtotal)) - amount_paid, Decimal('0'))
            if order.balance_amount > 0:
                order.payment_status = 'partial' if amount_paid > 0 else 'credit'
            else:
                order.payment_status = 'full'
            order.status = 'completed'
            order.save()

            if 'barcode_order_id' in request.session:
                del request.session['barcode_order_id']

            log_action(request, 'complete_order', target=f'Barcode Order #{order.order_number}')
            detect_suspicious(order)

            if order.balance_amount > 0 and customer_name:
                send_credit_customer_alert(customer_name, customer_phone, order.order_number, order.balance_amount)

            messages.success(request, f'Order #{order.order_number} completed!')
            return redirect('sales:print_receipt', order_id=order.id)

        elif action == 'clear_order':
            for item in order.items.all():
                item.product.stock_quantity += item.quantity
                item.product.save()
            order.delete()
            if 'barcode_order_id' in request.session:
                del request.session['barcode_order_id']
            messages.info(request, 'Barcode order cleared.')
            return redirect('sales:barcode_pos')

    order_items = POSOrderItem.objects.filter(order=order).select_related('product')
    subtotal = sum(float(item.total_price) for item in order_items)
    order.total_amount = subtotal
    order.final_amount = subtotal
    order.save()

    from sales.models import Customer
    return render(request, 'sales/barcode_pos.html', {
        'order': order,
        'order_items': order_items,
        'subtotal': subtotal,
        'system_settings': system_settings,
        'user_role': request.user.role,
        'user_name': request.user.get_full_name() or request.user.username,
        'customers': Customer.objects.all().order_by('name'),
    })


@login_required
def api_scan_barcode(request):
    """AJAX: GET ?barcode=<code> → product JSON."""
    barcode = request.GET.get('barcode', '').strip()
    if not barcode:
        return JsonResponse({'ok': False, 'message': 'No barcode provided'}, status=400)

    product = Product.objects.filter(
        Q(barcode=barcode) | Q(sku=barcode), status='active'
    ).first()

    if not product:
        return JsonResponse({'ok': False, 'message': f'No product found for barcode: {barcode}'}, status=404)

    # Add to the current barcode order session
    order_id = request.session.get('barcode_order_id')
    if order_id:
        try:
            order = POSOrder.objects.get(id=order_id, status='pending')
            if product.stock_quantity < 1:
                return JsonResponse({'ok': False, 'message': f'"{product.name}" is out of stock'}, status=400)

            existing = POSOrderItem.objects.filter(order=order, product=product).first()
            if existing:
                if product.stock_quantity < 1:
                    return JsonResponse({'ok': False, 'message': 'Insufficient stock'}, status=400)
                existing.quantity += 1
                existing.total_price = float(existing.unit_price) * existing.quantity
                existing.save()
                product.stock_quantity -= 1
                product.save()
            else:
                POSOrderItem.objects.create(
                    order=order, product=product,
                    quantity=1, unit_price=product.price,
                    total_price=float(product.price),
                )
                product.stock_quantity -= 1
                product.save()

            # Recalc order total
            items = POSOrderItem.objects.filter(order=order)
            order.total_amount = sum(float(i.total_price) for i in items)
            order.final_amount = order.total_amount
            order.save()

        except POSOrder.DoesNotExist:
            pass

    return JsonResponse({
        'ok': True,
        'product': {
            'id': product.id,
            'name': product.name,
            'sku': product.sku,
            'barcode': product.barcode,
            'price': float(product.price),
            'stock_quantity': product.stock_quantity,
        }
    })


# ─────────────────────────────────────────────────────────────
# END-OF-DAY CASH RECONCILIATION  (#21)
# ─────────────────────────────────────────────────────────────
@login_required
@role_required(['admin', 'manager', 'cashier'])
def end_of_day(request):
    """End-of-day cash reconciliation view."""
    from .models import CashReconciliation
    from django.db.models import Sum

    today = timezone.now().date()
    settings = SystemSettings.objects.first()
    currency_symbol = settings.currency_symbol if settings else '₦'

    expected = (
        POSOrder.objects.filter(
            status='completed',
            payment_method='cash',
            created_at__date=today,
        ).aggregate(total=Sum('final_amount'))['total'] or 0
    )

    existing = CashReconciliation.objects.filter(date=today).first()

    if request.method == 'POST':
        try:
            actual = Decimal(str(request.POST.get('actual', '0')))
        except Exception:
            actual = Decimal('0')
        notes = request.POST.get('notes', '')
        rec, _ = CashReconciliation.objects.update_or_create(
            date=today,
            defaults={
                'expected': expected,
                'actual': actual,
                'cashier': request.user.username,
                'notes': notes,
            }
        )
        messages.success(request, f"Reconciliation saved. Variance: {currency_symbol}{rec.variance:+.2f}")
        return redirect('sales:end_of_day')

    history = CashReconciliation.objects.order_by('-date')[:10]

    context = {
        'today': today,
        'expected': expected,
        'existing': existing,
        'history': history,
        'currency_symbol': currency_symbol,
    }
    return render(request, 'sales/end_of_day.html', context)


# ─────────────────────────────────────────────────────────────
# CUSTOMER DETAIL  (#11)
# ─────────────────────────────────────────────────────────────
@login_required
def customer_detail(request, pk):
    """View full profile for a registered Customer."""
    from .models import Customer
    from django.db.models import Sum

    settings = SystemSettings.objects.first()
    currency_symbol = settings.currency_symbol if settings else '₦'

    customer = get_object_or_404(Customer, pk=pk)
    orders = customer.orders.order_by('-created_at')
    credit_orders = orders.filter(balance_amount__gt=0)

    context = {
        'customer': customer,
        'orders': orders,
        'credit_orders': credit_orders,
        'currency_symbol': currency_symbol,
    }
    return render(request, 'sales/customer_detail.html', context)
