# api/views.py  — SwiftPOS REST API  (Django REST Framework)
from decimal import Decimal

from django.contrib.auth import authenticate, get_user_model
from django.db.models import Sum, Count, Avg, Q, F
from django.db import transaction
from django.utils import timezone
from django.http import HttpResponse
from datetime import date, timedelta

from rest_framework import status
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated, AllowAny
from rest_framework.response import Response
from rest_framework.authtoken.models import Token

from inventory.models import (
    Product, ProductCategory, InventoryTransaction,
    Supplier, PurchaseOrder, StockAdjustment
)
from sales.models import POSOrder, POSOrderItem, Customer
from accounts.models import AuditLog
from accounts.audit import log_action
from core.models import Branch
from core.utils import check_limit_or_block, is_feature_enabled

from .serializers import (
    LoginSerializer, UserSerializer, UserCreateSerializer,
    BranchSerializer,
    CategorySerializer, ProductSerializer, StockAdjustSerializer,
    InventoryTransactionSerializer,
    SupplierSerializer, PurchaseOrderSerializer,
    OrderSerializer, OrderCreateSerializer,
    CustomerSerializer, AuditLogSerializer,
    DashboardSerializer, PnLSerializer,
)

User = get_user_model()


# ─────────────────────────────────────────────────────────────────
# AUTH
# ─────────────────────────────────────────────────────────────────

@api_view(['POST'])
@permission_classes([AllowAny])
def api_login(request):
    """
    POST /api/auth/login/
    Body: { "username": "…", "password": "…" }
    Returns: { "token": "…", "user": {…} }
    """
    ser = LoginSerializer(data=request.data)
    if not ser.is_valid():
        return Response(ser.errors, status=400)

    user = authenticate(
        request,
        username=ser.validated_data['username'],
        password=ser.validated_data['password']
    )
    if not user:
        return Response({'detail': 'Invalid credentials.'}, status=401)
    if not user.is_active:
        return Response({'detail': 'Account inactive.'}, status=403)

    token, _ = Token.objects.get_or_create(user=user)
    log_action(request, 'login', user.username)
    return Response({'token': token.key, 'user': UserSerializer(user).data})


@api_view(['POST'])
@permission_classes([IsAuthenticated])
def api_logout(request):
    """POST /api/auth/logout/  — deletes the token."""
    request.auth.delete()
    log_action(request, 'logout', request.user.username)
    return Response({'detail': 'Logged out.'})


@api_view(['GET'])
@permission_classes([IsAuthenticated])
def api_me(request):
    """GET /api/auth/me/  — current user profile."""
    return Response(UserSerializer(request.user).data)


# ─────────────────────────────────────────────────────────────────
# USERS
# ─────────────────────────────────────────────────────────────────

@api_view(['GET', 'POST'])
@permission_classes([IsAuthenticated])
def api_users(request):
    """
    GET  /api/users/  — list all users (admin only)
    POST /api/users/  — create a user (admin only)
    """
    if request.user.role not in ('admin', 'superadmin'):
        return Response({'detail': 'Forbidden.'}, status=403)

    if request.method == 'GET':
        role   = request.query_params.get('role')
        branch = request.query_params.get('branch')
        qs = User.objects.filter(company=request.company).order_by('username')
        if role:
            qs = qs.filter(role=role)
        if branch:
            qs = qs.filter(branch_id=branch)
        return Response(UserSerializer(qs, many=True).data)

    # POST
    ser = UserCreateSerializer(data=request.data)
    if not ser.is_valid():
        return Response(ser.errors, status=400)
    user = ser.save(company=request.company)
    log_action(request, 'user_create', user.username)
    return Response(UserSerializer(user).data, status=201)


@api_view(['GET', 'PUT', 'PATCH', 'DELETE'])
@permission_classes([IsAuthenticated])
def api_user_detail(request, pk):
    """GET/PUT/PATCH/DELETE /api/users/<pk>/"""
    if request.user.role not in ('admin', 'superadmin'):
        return Response({'detail': 'Forbidden.'}, status=403)
    try:
        user = User.objects.get(company=request.company, pk=pk)
    except User.DoesNotExist:
        return Response({'detail': 'Not found.'}, status=404)

    if request.method == 'GET':
        return Response(UserSerializer(user).data)

    if request.method in ('PUT', 'PATCH'):
        ser = UserSerializer(user, data=request.data, partial=(request.method == 'PATCH'))
        if not ser.is_valid():
            return Response(ser.errors, status=400)
        ser.save()
        log_action(request, 'user_edit', user.username)
        return Response(ser.data)

    # DELETE
    username = user.username
    user.delete()
    log_action(request, 'user_delete', username)
    return Response(status=204)


# ─────────────────────────────────────────────────────────────────
# BRANCHES
# ─────────────────────────────────────────────────────────────────

@api_view(['GET', 'POST'])
@permission_classes([IsAuthenticated])
def api_branches(request):
    """GET /api/branches/  POST /api/branches/"""
    if request.method == 'GET':
        branches = Branch.objects.filter(company=request.company).order_by('name')
        return Response(BranchSerializer(branches, many=True).data)

    if request.user.role not in ('admin', 'superadmin'):
        return Response({'detail': 'Forbidden.'}, status=403)
    ser = BranchSerializer(data=request.data)
    if not ser.is_valid():
        return Response(ser.errors, status=400)
    branch = ser.save(company=request.company)
    log_action(request, 'branch_create', branch.name)
    return Response(ser.data, status=201)


@api_view(['GET', 'PUT', 'PATCH', 'DELETE'])
@permission_classes([IsAuthenticated])
def api_branch_detail(request, pk):
    """GET/PUT/DELETE /api/branches/<pk>/"""
    try:
        branch = Branch.objects.get(company=request.company, pk=pk)
    except Branch.DoesNotExist:
        return Response({'detail': 'Not found.'}, status=404)

    if request.method == 'GET':
        return Response(BranchSerializer(branch).data)

    if request.user.role not in ('admin', 'superadmin'):
        return Response({'detail': 'Forbidden.'}, status=403)

    if request.method in ('PUT', 'PATCH'):
        ser = BranchSerializer(branch, data=request.data, partial=(request.method == 'PATCH'))
        if not ser.is_valid():
            return Response(ser.errors, status=400)
        ser.save()
        return Response(ser.data)

    branch.delete()
    return Response(status=204)


# ─────────────────────────────────────────────────────────────────
# CATEGORIES
# ─────────────────────────────────────────────────────────────────

@api_view(['GET', 'POST'])
@permission_classes([IsAuthenticated])
def api_categories(request):
    if request.method == 'GET':
        return Response(CategorySerializer(ProductCategory.objects.filter(company=request.company), many=True).data)
    if request.user.role not in ('admin', 'manager', 'superadmin'):
        return Response({'detail': 'Forbidden.'}, status=403)
    ser = CategorySerializer(data=request.data)
    if not ser.is_valid():
        return Response(ser.errors, status=400)
    cat = ser.save(company=request.company)
    log_action(request, 'category_create', cat.name)
    return Response(ser.data, status=201)


@api_view(['GET', 'PUT', 'PATCH', 'DELETE'])
@permission_classes([IsAuthenticated])
def api_category_detail(request, pk):
    try:
        cat = ProductCategory.objects.get(company=request.company, pk=pk)
    except ProductCategory.DoesNotExist:
        return Response({'detail': 'Not found.'}, status=404)
    if request.method == 'GET':
        return Response(CategorySerializer(cat).data)
    if request.user.role not in ('admin', 'manager', 'superadmin'):
        return Response({'detail': 'Forbidden.'}, status=403)
    if request.method in ('PUT', 'PATCH'):
        ser = CategorySerializer(cat, data=request.data, partial=(request.method == 'PATCH'))
        if not ser.is_valid():
            return Response(ser.errors, status=400)
        ser.save()
        return Response(ser.data)
    name = cat.name
    cat.delete()
    log_action(request, 'category_delete', name)
    return Response(status=204)


# ─────────────────────────────────────────────────────────────────
# PRODUCTS
# ─────────────────────────────────────────────────────────────────

@api_view(['GET', 'POST'])
@permission_classes([IsAuthenticated])
def api_products(request):
    """
    GET  /api/products/?search=&category=&branch=&status=low_stock
    POST /api/products/
    """
    if request.method == 'GET':
        qs = Product.objects.filter(company=request.company).select_related('category').order_by('name')
        q        = request.query_params.get('search', '')
        category = request.query_params.get('category')
        st       = request.query_params.get('status')
        if q:
            qs = qs.filter(Q(name__icontains=q) | Q(sku__icontains=q) | Q(barcode__icontains=q))
        if category:
            qs = qs.filter(category_id=category)
        if st:
            qs = qs.filter(stock_status=st)
        return Response(ProductSerializer(qs, many=True).data)

    if request.user.role not in ('admin', 'manager', 'superadmin'):
        return Response({'detail': 'Forbidden.'}, status=403)
    try:
        check_limit_or_block('products', request=request)
    except Exception as e:
        return Response({'detail': str(e)}, status=403)
    ser = ProductSerializer(data=request.data)
    if not ser.is_valid():
        return Response(ser.errors, status=400)
    product = ser.save(company=request.company)
    log_action(request, 'product_create', product.name)
    return Response(ser.data, status=201)


@api_view(['GET', 'PUT', 'PATCH', 'DELETE'])
@permission_classes([IsAuthenticated])
def api_product_detail(request, pk):
    try:
        product = Product.objects.select_related('category').get(company=request.company, pk=pk)
    except Product.DoesNotExist:
        return Response({'detail': 'Not found.'}, status=404)

    if request.method == 'GET':
        return Response(ProductSerializer(product).data)

    if request.user.role not in ('admin', 'manager', 'superadmin'):
        return Response({'detail': 'Forbidden.'}, status=403)

    if request.method in ('PUT', 'PATCH'):
        ser = ProductSerializer(product, data=request.data, partial=(request.method == 'PATCH'))
        if not ser.is_valid():
            return Response(ser.errors, status=400)
        ser.save()
        log_action(request, 'product_edit', product.name)
        return Response(ser.data)

    name = product.name
    product.delete()
    log_action(request, 'product_delete', name)
    return Response(status=204)


@api_view(['POST'])
@permission_classes([IsAuthenticated])
def api_product_stock(request, pk):
    """
    POST /api/products/<pk>/stock/
    Body: { "action": "restock"|"reduce"|"adjust", "quantity": N, "note": "…" }
    """
    try:
        product = Product.objects.get(company=request.company, pk=pk)
    except Product.DoesNotExist:
        return Response({'detail': 'Not found.'}, status=404)

    ser = StockAdjustSerializer(data=request.data)
    if not ser.is_valid():
        return Response(ser.errors, status=400)

    action   = ser.validated_data['action']
    quantity = ser.validated_data['quantity']
    note     = ser.validated_data.get('note', '')

    if action == 'restock':
        product.stock_quantity += quantity
    elif action == 'reduce':
        if quantity > product.stock_quantity:
            return Response({'detail': 'Cannot reduce below 0.'}, status=400)
        product.stock_quantity -= quantity
    elif action == 'adjust':
        product.stock_quantity = ser.validated_data.get('new_stock', quantity)

    product.save()
    InventoryTransaction.objects.create(
        company=request.company,
        product=product,
        transaction_type=action,
        quantity=quantity,
        notes=note or f'API {action}',
    )
    log_action(request, f'stock_{action}', product.name)
    return Response(ProductSerializer(product).data)


@api_view(['GET'])
@permission_classes([IsAuthenticated])
def api_stock_history(request):
    """GET /api/stock-history/?product=&type="""
    qs = InventoryTransaction.objects.filter(company=request.company).select_related('product').order_by('-created_at')
    product = request.query_params.get('product')
    ttype   = request.query_params.get('type')
    if product:
        qs = qs.filter(product_id=product)
    if ttype:
        qs = qs.filter(transaction_type=ttype)
    return Response(InventoryTransactionSerializer(qs[:200], many=True).data)


# ─────────────────────────────────────────────────────────────────
# SUPPLIERS
# ─────────────────────────────────────────────────────────────────

@api_view(['GET', 'POST'])
@permission_classes([IsAuthenticated])
def api_suppliers(request):
    if request.method == 'GET':
        return Response(SupplierSerializer(Supplier.objects.filter(company=request.company), many=True).data)
    if request.user.role not in ('admin', 'manager', 'superadmin'):
        return Response({'detail': 'Forbidden.'}, status=403)
    ser = SupplierSerializer(data=request.data)
    if not ser.is_valid():
        return Response(ser.errors, status=400)
    ser.save(company=request.company)
    return Response(ser.data, status=201)


@api_view(['GET', 'PUT', 'PATCH', 'DELETE'])
@permission_classes([IsAuthenticated])
def api_supplier_detail(request, pk):
    try:
        supplier = Supplier.objects.get(company=request.company, pk=pk)
    except Supplier.DoesNotExist:
        return Response({'detail': 'Not found.'}, status=404)
    if request.method == 'GET':
        return Response(SupplierSerializer(supplier).data)
    if request.user.role not in ('admin', 'manager', 'superadmin'):
        return Response({'detail': 'Forbidden.'}, status=403)
    if request.method in ('PUT', 'PATCH'):
        ser = SupplierSerializer(supplier, data=request.data, partial=(request.method == 'PATCH'))
        if not ser.is_valid():
            return Response(ser.errors, status=400)
        ser.save()
        return Response(ser.data)
    supplier.delete()
    return Response(status=204)


# ─────────────────────────────────────────────────────────────────
# PURCHASE ORDERS
# ─────────────────────────────────────────────────────────────────

@api_view(['GET', 'POST'])
@permission_classes([IsAuthenticated])
def api_purchase_orders(request):
    if request.method == 'GET':
        qs = PurchaseOrder.objects.filter(supplier__company=request.company).select_related('supplier').order_by('-created_at')
        return Response(PurchaseOrderSerializer(qs, many=True).data)
    if request.user.role not in ('admin', 'manager', 'superadmin'):
        return Response({'detail': 'Forbidden.'}, status=403)
    ser = PurchaseOrderSerializer(data=request.data)
    if not ser.is_valid():
        return Response(ser.errors, status=400)
    ser.save(company=request.company)
    return Response(ser.data, status=201)


@api_view(['GET', 'POST'])
@permission_classes([IsAuthenticated])
@transaction.atomic
def api_purchase_order_detail(request, pk):
    try:
        po = PurchaseOrder.objects.get(supplier__company=request.company, pk=pk)
    except PurchaseOrder.DoesNotExist:
        return Response({'detail': 'Not found.'}, status=404)
    if request.method == 'GET':
        return Response(PurchaseOrderSerializer(po).data)
    # POST = mark received
    if request.user.role not in ('admin', 'manager', 'superadmin'):
        return Response({'detail': 'Forbidden.'}, status=403)
    po.status      = 'received'
    po.received_at = timezone.now()
    po.save()
    return Response(PurchaseOrderSerializer(po).data)


# ─────────────────────────────────────────────────────────────────
# ORDERS (POS Sales)
# ─────────────────────────────────────────────────────────────────

@api_view(['GET'])
@permission_classes([IsAuthenticated])
def api_orders(request):
    """
    GET /api/orders/?date=YYYY-MM-DD&status=&payment_method=&branch=&page=1&page_size=100
    Returns: { count, next_offset, results }
    """
    qs = POSOrder.objects.filter(company=request.company).select_related('branch').order_by('-created_at')

    d      = request.query_params.get('date')
    st     = request.query_params.get('status')
    pm     = request.query_params.get('payment_method')
    branch = request.query_params.get('branch')
    q      = request.query_params.get('search')

    if d:
        qs = qs.filter(created_at__date=d)
    if st:
        qs = qs.filter(status=st)
    if pm:
        qs = qs.filter(payment_method=pm)
    if branch:
        qs = qs.filter(branch_id=branch)
    if q:
        qs = qs.filter(
            Q(order_number__icontains=q) |
            Q(customer_name__icontains=q) |
            Q(customer_phone__icontains=q)
        )

    # Role-based filtering
    user = request.user
    if user.role in ('manager', 'cashier') and user.branch_id:
        qs = qs.filter(branch_id=user.branch_id)

    # Pagination
    try:
        page_size = max(1, min(int(request.query_params.get('page_size', 100)), 500))
        offset    = max(0, int(request.query_params.get('offset', 0)))
    except (ValueError, TypeError):
        page_size, offset = 100, 0

    total = qs.count()
    page_qs = qs[offset: offset + page_size]
    next_offset = offset + page_size if (offset + page_size) < total else None

    return Response({
        'count':       total,
        'next_offset': next_offset,
        'results':     OrderSerializer(page_qs, many=True).data,
    })


@api_view(['POST'])
@permission_classes([IsAuthenticated])
@transaction.atomic
def api_order_create(request):
    """
    POST /api/orders/create/
    Creates a full POS order with items in one request.
    Used by the mobile app — sends proper snake_case field names.
    """
    ser = OrderCreateSerializer(data=request.data)
    if not ser.is_valid():
        return Response(ser.errors, status=400)

    data = ser.validated_data
    items_data = data.pop('items')
    client_id = data.get('client_id', '')

    if client_id:
        existing = POSOrder.objects.filter(client_id=client_id, company=request.company).first()
        if existing:
            return Response(OrderSerializer(existing).data, status=201)

    # Calculate total
    subtotal = Decimal('0')
    resolved_items = []

    settings = getattr(request.company, 'settings', None)
    allow_zero = settings.enable_unlimited_stock if settings else False

    for item in items_data:
        try:
            product = Product.objects.select_for_update().get(company=request.company, pk=item['product_id'], status='active')
        except Product.DoesNotExist:
            return Response({'detail': f"Product {item['product_id']} not found."}, status=400)

        is_unlimited = (allow_zero and product.stock_quantity == 0)
        if not is_unlimited and product.stock_quantity < item['quantity']:
            return Response({'detail': f"Insufficient stock for {product.name}."}, status=400)

        line   = product.price * item['quantity']
        disc   = (line * item.get('discount', Decimal('0'))) / 100
        resolved_items.append({'product': product, 'quantity': item['quantity'],
                                'discount': item.get('discount', 0), 'line': line - disc, 'is_unlimited': is_unlimited})
        subtotal += (line - disc)

    discount_amt = (subtotal * data.get('discount', Decimal('0'))) / 100
    
    vat_rate = settings.vat_rate if settings else Decimal('0')
    tax_amount = (subtotal * vat_rate) / 100
    
    final = subtotal + tax_amount - discount_amt

    import random, string
    order_number = 'ORD-' + ''.join(random.choices(string.digits, k=8))

    payment_method = data.get('payment_method', 'cash')
    amount_paid    = data.get('amount_paid', Decimal('0'))
    
    if payment_method in ['cash', 'pos', 'transfer', 'mobile_money'] and amount_paid <= Decimal('0'):
        amount_paid = final
        payment_status = 'full'
    elif payment_method == 'credit':
        payment_status = 'credit'
    elif amount_paid >= final:
        amount_paid = final
        payment_status = 'full'
    else:
        payment_status = 'partial'
        
    balance_amount = max(Decimal('0'), final - amount_paid)

    order = POSOrder.objects.create(
        company        = request.company,
        order_number   = order_number,
        client_id      = client_id,
        cashier        = request.user.username,
        customer_name  = data.get('customer_name', ''),
        customer_phone = data.get('customer_phone', ''),
        payment_method = payment_method,
        amount_paid    = amount_paid,
        discount_amount = discount_amt,
        tax_amount     = tax_amount,
        final_amount   = final,
        total_amount   = subtotal,
        balance_amount = balance_amount,
        payment_status = payment_status,
        status         = 'completed',
        branch_id      = data.get('branch') or getattr(request.user, 'branch_id', None),
    )

    for item in resolved_items:
        POSOrderItem.objects.create(
            order        = order,
            product      = item['product'],
            product_name = item['product'].name,
            quantity     = item['quantity'],
            unit_price   = item['product'].price,
            total_price  = item['line'],
        )
        if not item['is_unlimited']:
            item['product'].stock_quantity -= item['quantity']
            item['product'].save()
            
    if amount_paid > 0:
        from sales.models import PaymentTransaction
        PaymentTransaction.objects.create(
            order=order,
            payment_method=payment_method,
            amount=amount_paid,
            status='completed'
        )

    log_action(request, 'order_complete', order.order_number)
    
    from sales.suspicious import detect_suspicious
    detect_suspicious(order)
    
    from sales.models import DailySalesSummary
    DailySalesSummary.generate_summary_from_queryset(
        timezone.now().date(), 
        POSOrder.objects.filter(company=request.company, created_at__date=timezone.now().date(), status='completed')
    )

    return Response(OrderSerializer(order).data, status=201)


@api_view(['GET'])
@permission_classes([IsAuthenticated])
def api_order_detail(request, pk):
    """GET /api/orders/<pk>/"""
    try:
        order = POSOrder.objects.prefetch_related(
            'items__product'
        ).get(company=request.company, pk=pk)
    except POSOrder.DoesNotExist:
        return Response({'detail': 'Not found.'}, status=404)
    return Response(OrderSerializer(order).data)


# ─────────────────────────────────────────────────────────────────
# CREDIT ORDERS
# ─────────────────────────────────────────────────────────────────

@api_view(['GET'])
@permission_classes([IsAuthenticated])
def api_credit_orders(request):
    """GET /api/orders/credit/ — all unpaid credit orders"""
    qs = POSOrder.objects.filter(company=request.company,
        payment_method='credit', status='completed'
    ).exclude(amount_paid__gte=F('final_amount')).order_by('-created_at')
    return Response(OrderSerializer(qs, many=True).data)


@api_view(['POST'])
@permission_classes([IsAuthenticated])
def api_clear_credit(request, pk):
    """POST /api/orders/<pk>/clear-credit/"""
    try:
        order = POSOrder.objects.get(company=request.company, pk=pk, payment_method='credit')
    except POSOrder.DoesNotExist:
        return Response({'detail': 'Not found.'}, status=404)
    order.amount_paid = order.final_amount
    order.save()
    log_action(request, 'credit_cleared', order.order_number)
    return Response({'detail': 'Credit cleared.'})


# ─────────────────────────────────────────────────────────────────
# CUSTOMERS
# ─────────────────────────────────────────────────────────────────

@api_view(['GET', 'POST'])
@permission_classes([IsAuthenticated])
def api_customers(request):
    if request.method == 'GET':
        q = request.query_params.get('search', '')
        qs = Customer.objects.filter(company=request.company).order_by('name')
        if q:
            qs = qs.filter(Q(name__icontains=q) | Q(phone__icontains=q) | Q(email__icontains=q))
        return Response(CustomerSerializer(qs, many=True).data)

    try:
        check_limit_or_block('customers', request=request)
    except Exception as e:
        return Response({'detail': str(e)}, status=403)
    ser = CustomerSerializer(data=request.data)
    if not ser.is_valid():
        return Response(ser.errors, status=400)
    customer = ser.save(company=request.company)
    log_action(request, 'customer_create', customer.name)
    return Response(ser.data, status=201)


@api_view(['GET', 'PUT', 'PATCH', 'DELETE'])
@permission_classes([IsAuthenticated])
def api_customer_detail(request, pk):
    try:
        customer = Customer.objects.get(company=request.company, pk=pk)
    except Customer.DoesNotExist:
        return Response({'detail': 'Not found.'}, status=404)
    if request.method == 'GET':
        return Response(CustomerSerializer(customer).data)
    if request.method in ('PUT', 'PATCH'):
        ser = CustomerSerializer(customer, data=request.data, partial=(request.method == 'PATCH'))
        if not ser.is_valid():
            return Response(ser.errors, status=400)
        ser.save()
        return Response(ser.data)
    name = customer.name
    customer.delete()
    log_action(request, 'customer_delete', name)
    return Response(status=204)


# ─────────────────────────────────────────────────────────────────
# DASHBOARD
# ─────────────────────────────────────────────────────────────────

@api_view(['GET'])
@permission_classes([IsAuthenticated])
def api_dashboard(request):
    """GET /api/dashboard/?date=YYYY-MM-DD"""
    d = request.query_params.get('date')
    target = date.fromisoformat(d) if d else timezone.now().date()

    base_qs = POSOrder.objects.filter(company=request.company, status='completed')
    if request.user.role in ('manager', 'cashier') and getattr(request.user, 'branch_id', None):
        base_qs = base_qs.filter(branch_id=request.user.branch_id)

    today_qs = base_qs.filter(created_at__date=target)
    month_qs = base_qs.filter(created_at__year=target.year, created_at__month=target.month)

    today_agg = today_qs.aggregate(
        total_sales=Sum('final_amount'),
        cnt=Count('id'),
    )
    month_agg = month_qs.aggregate(total_sales=Sum('final_amount'))
    
    # Calculate today's items sold
    today_items_sold = POSOrderItem.objects.filter(order__in=today_qs).aggregate(total_qty=Sum('quantity'))['total_qty'] or 0

    # Credit balance
    credit_qs = POSOrder.objects.filter(company=request.company, payment_status__in=['credit', 'partial'])
    if request.user.role in ('manager', 'cashier') and getattr(request.user, 'branch_id', None):
        credit_qs = credit_qs.filter(branch_id=request.user.branch_id)
    credit_balance = credit_qs.aggregate(bal=Sum('balance_amount'))['bal'] or 0

    # Recent orders
    recent_orders = base_qs.order_by('-created_at')[:5].values(
        'id', 'order_number', 'final_amount', 'payment_status', 'created_at'
    )

    return Response({
        'date':               str(target),
        'today_sales':        today_agg['total_sales'] or 0,
        'today_transactions': today_agg['cnt'] or 0,
        'today_items':        today_items_sold,
        'month_revenue':      month_agg['total_sales'] or 0,
        'today_customers':    today_qs.values('customer_phone').distinct().count(),
        'avg_order_value':    float(today_agg['total_sales'] or 0) / max(today_agg['cnt'] or 1, 1),
        'total_products':     Product.objects.filter(company=request.company).count(),
        'low_stock_products': Product.objects.filter(company=request.company, stock_status='low_stock').count(),
        'out_of_stock':       Product.objects.filter(company=request.company, stock_status='out_of_stock').count(),
        'credit_balance':     credit_balance,
        'total_users':        User.objects.filter(company=request.company).count(),
        'active_users':       User.objects.filter(company=request.company, is_active=True).count(),
        'recent_orders':      list(recent_orders),
        'payment_breakdown':  list(
            today_qs.values('payment_method').annotate(
                count=Count('id'), total=Sum('final_amount')
            )
        ),
    })


# ─────────────────────────────────────────────────────────────────
# P&L REPORT
# ─────────────────────────────────────────────────────────────────

@api_view(['GET'])
@permission_classes([IsAuthenticated])
def api_pnl(request):
    """
    GET /api/pnl/?date_from=YYYY-MM-DD&date_to=YYYY-MM-DD
    """
    if request.user.role not in ('admin', 'manager', 'superadmin'):
        return Response({'detail': 'Forbidden.'}, status=403)

    today     = timezone.now().date()
    date_from = date.fromisoformat(request.query_params.get('date_from', str(today)))
    date_to   = date.fromisoformat(request.query_params.get('date_to',   str(today)))

    qs = POSOrder.objects.filter(company=request.company,
        created_at__date__gte=date_from,
        created_at__date__lte=date_to,
        status='completed'
    )

    total_revenue = qs.aggregate(total=Sum('final_amount'))['total'] or 0
    credit_outstanding = qs.filter(payment_method='credit').aggregate(
        owed=Sum('final_amount')
    )['owed'] or 0

    items = POSOrderItem.objects.filter(
        order__in=qs
    ).select_related('product')
    total_cost = sum(
        (i.product.cost_price or 0) * i.quantity for i in items
    )
    total_profit = float(total_revenue) - float(total_cost)

    daily = list(
        qs.extra(select={'day': 'DATE(created_at)'})
          .values('day')
          .annotate(cnt=Count('id'), rev=Sum('final_amount'))
          .order_by('day')
    )

    return Response({
        'date_from':          str(date_from),
        'date_to':            str(date_to),
        'total_revenue':      total_revenue,
        'total_cost':         total_cost,
        'total_profit':       total_profit,
        'credit_outstanding': credit_outstanding,
        'total_orders_count': qs.count(),
        'daily_data':         daily,
        'payment_breakdown':  list(
            qs.values('payment_method').annotate(
                count=Count('id'), revenue=Sum('final_amount')
            )
        ),
    })


# ─────────────────────────────────────────────────────────────────
# STAFF PERFORMANCE & SALES BY ITEM
# ─────────────────────────────────────────────────────────────────

@api_view(['GET'])
@permission_classes([IsAuthenticated])
def api_sales_by_item(request):
    """
    GET /api/sales-by-item/?start_date=YYYY-MM-DD&end_date=YYYY-MM-DD
    """
    if request.user.role not in ('admin', 'manager', 'superadmin'):
        return Response({'detail': 'Forbidden.'}, status=403)

    start_date = request.query_params.get('start_date')
    end_date = request.query_params.get('end_date')

    orders = POSOrder.objects.filter(company=request.company, status='completed')
    if start_date:
        orders = orders.filter(created_at__date__gte=start_date)
    if end_date:
        orders = orders.filter(created_at__date__lte=end_date)

    order_items = POSOrderItem.objects.filter(order__in=orders).select_related('product', 'product__category')

    item_summaries = {}
    for item in order_items:
        key = item.product.id if item.product else f"Unlinked-{item.product_name}"

        if key not in item_summaries:
            item_summaries[key] = {
                'item_name': item.product.name if item.product else item.product_name,
                'sku': item.product.sku if item.product else '',
                'category': item.product.category.name if item.product and item.product.category else 'No Category',
                'quantity': 0,
                'gross_sales': Decimal('0.00'),
                'refunds': Decimal('0.00'),
                'discounts': Decimal('0.00'),
                'net_sales': Decimal('0.00'),
                'cost_of_goods': Decimal('0.00'),
                'gross_profit': Decimal('0.00'),
                'margin': Decimal('0.00'),
                'taxes': Decimal('0.00'),
            }

        summary = item_summaries[key]
        summary['quantity'] += item.quantity
        gross = item.unit_price * item.quantity
        summary['gross_sales'] += gross

        order_subtotal = item.order.total_amount
        if order_subtotal > 0:
            ratio = gross / order_subtotal
            item_discount = item.order.discount_amount * ratio
            item_tax = item.order.tax_amount * ratio if hasattr(item.order, 'tax_amount') else Decimal('0.00')
        else:
            item_discount = Decimal('0.00')
            item_tax = Decimal('0.00')

        summary['discounts'] += item_discount
        summary['taxes'] += item_tax
        summary['refunds'] += Decimal('0.00')

        net = gross - item_discount
        summary['net_sales'] += net

        cost = (item.product.cost_price or Decimal('0.00')) if item.product else Decimal('0.00')
        cog = cost * item.quantity
        summary['cost_of_goods'] += cog

        profit = net - cog
        summary['gross_profit'] += profit

    for key, summary in item_summaries.items():
        if summary['net_sales'] > 0:
            summary['margin'] = (summary['gross_profit'] / summary['net_sales']) * 100

    report_data = sorted(item_summaries.values(), key=lambda x: x['quantity'], reverse=True)
    return Response(report_data)


@api_view(['GET'])
@permission_classes([IsAuthenticated])
def api_staff_performance(request):
    """GET /api/staff-performance/?date=YYYY-MM-DD"""
    if request.user.role not in ('admin', 'manager', 'superadmin'):
        return Response({'detail': 'Forbidden.'}, status=403)
    d      = request.query_params.get('date')
    target = date.fromisoformat(d) if d else timezone.now().date()
    data = (
        POSOrder.objects
        .filter(company=request.company, created_at__date=target, status='completed')
        .values('cashier')
        .annotate(
            orders=Count('id'),
            revenue=Sum('final_amount'),
            avg_order=Avg('final_amount'),
        )
        .order_by('-revenue')
    )
    return Response(list(data))


# ─────────────────────────────────────────────────────────────────
# AUDIT LOG
# ─────────────────────────────────────────────────────────────────

@api_view(['GET'])
@permission_classes([IsAuthenticated])
def api_audit_log(request):
    """GET /api/audit-log/"""
    if request.user.role not in ('admin', 'superadmin'):
        return Response({'detail': 'Forbidden.'}, status=403)
    qs = AuditLog.objects.filter(
        user__company=request.company
    ).select_related('user').order_by('-timestamp')
    
    # Hide superadmin actions from standard tenant admins
    if not request.user.is_superuser and request.user.role != 'superadmin':
        from django.db.models import Q
        qs = qs.exclude(Q(user__is_superuser=True) | Q(user__role='superadmin'))
        
    qs = qs[:200]
    return Response(AuditLogSerializer(qs, many=True).data)


# ─────────────────────────────────────────────────────────────────
# SUSPICIOUS ACTIVITY
# ─────────────────────────────────────────────────────────────────

@api_view(['GET'])
@permission_classes([IsAuthenticated])
def api_suspicious(request):
    """GET /api/suspicious/"""
    if request.user.role not in ('admin', 'manager', 'superadmin'):
        return Response({'detail': 'Forbidden.'}, status=403)
    from sales.models import UnusualTransaction
    qs = UnusualTransaction.objects.filter(
        order__company=request.company
    ).select_related('order').order_by('-flagged_at')[:100]
    data = [{
        'id': u.id,
        'order_number': u.order.order_number,
        'reason': u.reason,
        'flagged_at': u.flagged_at,
        'resolved': u.resolved,
    } for u in qs]
    return Response(data)


@api_view(['POST'])
@permission_classes([IsAuthenticated])
def api_suspicious_resolve(request, pk):
    """POST /api/suspicious/<pk>/resolve/"""
    if request.user.role not in ('admin', 'manager', 'superadmin'):
        return Response({'detail': 'Forbidden.'}, status=403)
    from sales.models import UnusualTransaction
    try:
        ut = UnusualTransaction.objects.get(pk=pk, order__company=request.company)
    except UnusualTransaction.DoesNotExist:
        return Response({'detail': 'Not found.'}, status=404)
    ut.resolved = True
    ut.save()
    log_action(request, 'suspicious_resolve', str(ut.id))
    return Response({'detail': 'Resolved.'})


# ─────────────────────────────────────────────────────────────────
# EXPORTS
# ─────────────────────────────────────────────────────────────────

@api_view(['GET'])
@permission_classes([IsAuthenticated])
def api_export_orders(request):
    """GET /api/exports/orders/?date_from=&date_to="""
    if request.user.role not in ('admin', 'manager', 'superadmin'):
        return Response({'detail': 'Forbidden.'}, status=403)

    import csv
    from django.http import StreamingHttpResponse

    today     = timezone.now().date()
    date_from = request.query_params.get('date_from', str(today))
    date_to   = request.query_params.get('date_to',   str(today))

    qs = POSOrder.objects.filter(
        company=request.company,
        created_at__date__gte=date_from,
        created_at__date__lte=date_to,
    ).order_by('-created_at')

    def rows():
        yield ['Order #', 'Date', 'Cashier', 'Customer', 'Payment', 'Total', 'Status']
        for o in qs:
            yield [
                o.order_number,
                o.created_at.strftime('%Y-%m-%d %H:%M'),
                o.cashier,
                o.customer_name or '',
                o.payment_method,
                str(o.final_amount),
                o.status,
            ]

    response = StreamingHttpResponse(
        ((','.join(str(c) for c in row) + '\n') for row in rows()),
        content_type='text/csv'
    )
    response['Content-Disposition'] = f'attachment; filename="orders_{date_from}_{date_to}.csv"'
    return response


@api_view(['GET'])
@permission_classes([IsAuthenticated])
def api_export_products(request):
    """GET /api/exports/products/"""
    if request.user.role not in ('admin', 'manager', 'superadmin'):
        return Response({'detail': 'Forbidden.'}, status=403)

    from django.http import StreamingHttpResponse

    qs = Product.objects.filter(company=request.company).select_related('category').order_by('name')

    def rows():
        yield ['Name', 'SKU', 'Category', 'Price', 'Cost', 'Stock', 'Status']
        for p in qs:
            yield [
                p.name, p.sku or '',
                p.category.name if p.category else '',
                str(p.price), str(p.cost_price or ''),
                str(p.stock_quantity), p.status,
            ]

    response = StreamingHttpResponse(
        ((','.join(str(c) for c in row) + '\n') for row in rows()),
        content_type='text/csv'
    )
    response['Content-Disposition'] = 'attachment; filename="products.csv"'
    return response


# ─────────────────────────────────────────────────────────────────
# BARCODE LOOKUP
# ─────────────────────────────────────────────────────────────────

@api_view(['GET'])
@permission_classes([IsAuthenticated])
def api_barcode_lookup(request):
    """GET /api/barcode/?q=<barcode>"""
    q = request.query_params.get('q', '').strip()
    if not q:
        return Response({'detail': 'No barcode provided.'}, status=400)
    try:
        product = Product.objects.get(
            Q(barcode=q) | Q(sku=q),
            company=request.company
        )
        return Response(ProductSerializer(product).data)
    except Product.DoesNotExist:
        return Response({'detail': 'Product not found.'}, status=404)
    except Product.MultipleObjectsReturned:
        product = Product.objects.filter(
            Q(barcode=q) | Q(sku=q),
            company=request.company
        ).first()
        return Response(ProductSerializer(product).data)


# ─────────────────────────────────────────────────────────────────
# SYSTEM INFO
# ─────────────────────────────────────────────────────────────────

@api_view(['GET'])
@permission_classes([IsAuthenticated])
def api_system_info(request):
    """GET /api/system/"""
    from django import __version__ as django_version
    company = request.company
    cs = getattr(company, 'settings', None)
    return Response({
        'django_version':    django_version,
        'company_name':      company.name if company else '',
        'company_slug':      company.slug if company else '',
        'currency_symbol':   cs.currency_symbol if cs else '₦',
        'business_name':     cs.business_name if cs else '',
        'pos_mode':          cs.pos_interface_mode if cs else 'classic',
        'unlimited_stock':   cs.enable_unlimited_stock if cs else False,
        'decimal_places':    cs.decimal_places if cs else 2,
    })


# ─────────────────────────────────────────────────────────────────
# PROMO CODE VALIDATION
# ─────────────────────────────────────────────────────────────────

@api_view(['POST'])
@permission_classes([IsAuthenticated])
def api_promo_validate(request):
    """POST /api/promo/validate/"""
    from django.utils import timezone as tz
    code        = (request.data.get('code') or '').strip().upper()
    order_total = Decimal(str(request.data.get('order_total', '0') or '0'))

    if not code:
        return Response({'valid': False, 'message': 'No code provided.'})

    try:
        from core.models import PromoCode
        promo = PromoCode.objects.get(
            code=code,
            company=request.company,
            is_active=True,
        )
    except Exception:
        return Response({'valid': False, 'message': 'Invalid or expired code.'})

    # Check expiry
    if promo.expires_at and promo.expires_at < tz.now():
        return Response({'valid': False, 'message': 'This promo code has expired.'})

    # Check usage limit
    if promo.usage_limit and promo.times_used >= promo.usage_limit:
        return Response({'valid': False, 'message': 'Promo code usage limit reached.'})

    # Check minimum order
    if promo.minimum_order_amount and order_total < promo.minimum_order_amount:
        return Response({
            'valid': False,
            'message': f'Minimum order of {promo.minimum_order_amount} required.'
        })

    # Calculate discount
    if promo.discount_type == 'percentage':
        discount = (order_total * promo.discount_value) / 100
        if promo.max_discount_amount:
            discount = min(discount, promo.max_discount_amount)
    else:
        discount = min(promo.discount_value, order_total)

    return Response({
        'valid':            True,
        'message':          f'{promo.discount_value}{"%" if promo.discount_type == "percentage" else ""} discount applied!',
        'discount_amount':  str(discount),
        'discount_type':    promo.discount_type,
        'discount_value':   str(promo.discount_value),
        'promo_id':         promo.id,
    })


# ─────────────────────────────────────────────────────────────────
# MONNIFY TRANSACTIONS
# ─────────────────────────────────────────────────────────────────

@api_view(['GET'])
@permission_classes([IsAuthenticated])
def api_monnify_transactions(request):
    """GET /api/monnify/transactions/ — superadmin only"""
    if not getattr(request.user, 'is_superuser', False):
        return Response({'detail': 'Forbidden.'}, status=403)
    from subscriptions.models import PaymentTransaction as SubTx
    from .serializers import SubscriptionTransactionSerializer
    qs = SubTx.objects.select_related('company', 'plan').order_by('-created_at')[:200]
    return Response(SubscriptionTransactionSerializer(qs, many=True).data)


# ─────────────────────────────────────────────────────────────────
# SUBSCRIPTION STATUS  (Mobile)
# ─────────────────────────────────────────────────────────────────

@api_view(['GET'])
@permission_classes([IsAuthenticated])
def api_subscription_status(request):
    """
    GET /api/subscription/status/
    Returns the current subscription, plan details and feature flags
    for the mobile paywall and home-screen expiry banner.
    """
    from subscriptions.models import Subscription, PaymentTransaction as SubTx
    company = request.company
    if not company:
        return Response({'detail': 'No company context.'}, status=400)

    sub = Subscription.objects.filter(company=company, is_active=True).first()
    if not sub:
        return Response({'detail': 'No active subscription.'}, status=404)

    recent_txns = SubTx.objects.filter(
        company=company
    ).order_by('-created_at')[:5]

    from .serializers import SubscriptionTransactionSerializer
    data = {
        'is_trial':       sub.is_trial,
        'is_active':      sub.is_active,
        'is_expired':     sub.is_expired,
        'payment_status': sub.payment_status,
        'billing_cycle':  sub.billing_cycle,
        'start_date':     str(sub.start_date),
        'expiry_date':    str(sub.expiry_date) if sub.expiry_date else None,
        'days_remaining': sub.days_remaining,
        'plan_id':        sub.plan.id,
        'plan_name':      sub.plan.name,
        'plan_price_monthly': str(sub.plan.price_monthly),
        'pending_downgrade_plan': (
            sub.pending_downgrade_plan.name if sub.pending_downgrade_plan else None
        ),
        'recent_transactions': SubscriptionTransactionSerializer(
            recent_txns, many=True
        ).data,
    }
    return Response(data)


@api_view(['GET'])
@permission_classes([IsAuthenticated])
def api_subscription_plans(request):
    """
    GET /api/subscription/plans/
    Returns all public active plans for the mobile pricing/upgrade screen.
    """
    from companies.models import Plan
    plans = Plan.objects.filter(is_active=True, is_public=True).order_by('display_order', 'price_monthly')
    data = [
        {
            'id':               p.id,
            'name':             p.name,
            'price_monthly':    str(p.price_monthly),
            'price_bimonthly':  str(p.price_bimonthly  or p.price_monthly * 2),
            'price_trimonthly': str(p.price_trimonthly or p.price_monthly * 3),
            'price_yearly':     str(p.price_yearly     or p.price_monthly * 12),
            'max_products':     p.max_products,
            'max_staff':        p.max_staff,
            'max_branches':     p.max_branches,
            'max_orders_per_day': p.max_orders_per_day,
            'feature_android_app':      p.feature_android_app,
            'feature_profit_loss':      p.feature_profit_loss,
            'feature_audit_logs':       p.feature_audit_logs,
            'feature_credit_sales':     p.feature_credit_sales,
            'feature_multi_branch':     p.feature_multi_branch,
            'feature_advanced_reports': p.feature_advanced_reports,
            'feature_barcode_pos':      p.feature_barcode_pos,
            'feature_promo':            p.feature_promo,
            'feature_discount':         p.feature_discount,
            'feature_export':           p.feature_export,
            'display_order':            p.display_order,
        }
        for p in plans
    ]
    return Response(data)


@api_view(['POST'])
@permission_classes([IsAuthenticated])
def api_subscription_checkout_initiate(request):
    """
    POST /api/subscription/checkout/initiate/
    Body: { "plan_id": N, "billing_cycle": "monthly", "promo_code": "" }
    Returns: { "gateway": "monnify"|"paystack", "checkout_url": "...",
               "reference": "...", "amount": 0.0, "free": false }
    The mobile app opens the checkout_url in a WebView (or uses
    the Paystack/Monnify in-app SDK with the returned reference).
    """
    from companies.models import Plan
    from subscriptions.services import create_payment_intent

    company = request.company
    if not company:
        return Response({'detail': 'No company context.'}, status=400)

    plan_id       = request.data.get('plan_id')
    billing_cycle = request.data.get('billing_cycle', 'monthly')
    promo_code    = (request.data.get('promo_code') or '').strip()

    if not plan_id:
        return Response({'detail': 'plan_id is required.'}, status=400)

    try:
        plan = Plan.objects.get(id=plan_id, is_active=True)
    except Plan.DoesNotExist:
        return Response({'detail': 'Plan not found.'}, status=404)

    # Build a deep-link redirect URL that the app handles after WebView closes
    redirect_url = request.build_absolute_uri('/api/subscription/status/')

    try:
        result = create_payment_intent(
            company=company,
            plan=plan,
            billing_cycle=billing_cycle,
            promo_code_str=promo_code,
            redirect_url=redirect_url,
            use_frontend_sdk=False,
        )
    except ValueError as exc:
        return Response({'detail': str(exc)}, status=400)
    except Exception as exc:
        return Response({'detail': 'Payment gateway error. Please try again.'}, status=502)

    txn     = result['transaction']
    gateway = result.get('gateway', 'monnify')
    is_free = (txn.status == 'paid')

    admin_user = company.users.filter(role='admin').first()
    customer_email = (
        (admin_user.email if admin_user else None)
        or company.email
        or ''
    )

    return Response({
        'free':           is_free,
        'gateway':        gateway,
        'checkout_url':   result.get('checkout_url', ''),
        'reference':      txn.payment_reference,
        'amount':         float(txn.final_amount),
        'customer_name':  company.name,
        'customer_email': customer_email,
    })


@api_view(['POST'])
@permission_classes([IsAuthenticated])
def api_subscription_promo_validate(request):
    """
    POST /api/subscription/promo/validate/
    Body: { "code": "PROMO10", "plan_id": N, "billing_cycle": "monthly" }
    Validates a SaaS subscription promo code (not a POS promo).
    """
    from companies.models import Plan
    from subscriptions.services import validate_promo_code, calculate_subscription_amount

    code         = (request.data.get('code') or '').strip().upper()
    plan_id      = request.data.get('plan_id')
    billing_cycle = request.data.get('billing_cycle', 'monthly')

    if not code:
        return Response({'ok': False, 'message': 'Code is required.'}, status=400)
    if not plan_id:
        return Response({'ok': False, 'message': 'plan_id is required.'}, status=400)

    try:
        plan = Plan.objects.get(id=plan_id, is_active=True)
    except Plan.DoesNotExist:
        return Response({'ok': False, 'message': 'Invalid plan.'}, status=404)

    base_amount = calculate_subscription_amount(plan, billing_cycle)

    try:
        promo_obj, discount = validate_promo_code(code, plan, base_amount)
    except ValueError as exc:
        return Response({'ok': False, 'message': str(exc)})

    final = base_amount - discount
    return Response({
        'ok':              True,
        'discount_amount': str(discount),
        'final_amount':    str(final),
        'base_amount':     str(base_amount),
        'message':         f'Promo applied! You save ₦{discount:,.2f}',
    })


# ─────────────────────────────────────────────────────────────────
# PROFILE  (Mobile)
# ─────────────────────────────────────────────────────────────────

@api_view(['PATCH'])
@permission_classes([IsAuthenticated])
def api_profile_update(request):
    """
    PATCH /api/profile/update/
    Body: { first_name, last_name, email, phone }
    Updates the authenticated user's own profile fields.
    """
    from .serializers import ProfileUpdateSerializer
    ser = ProfileUpdateSerializer(request.user, data=request.data, partial=True)
    if not ser.is_valid():
        return Response(ser.errors, status=400)
    ser.save()
    log_action(request, 'edit_user', request.user.username)
    from .serializers import UserSerializer
    return Response(UserSerializer(request.user).data)


@api_view(['POST'])
@permission_classes([IsAuthenticated])
def api_profile_change_pin(request):
    """
    POST /api/profile/change-pin/
    Body: { "current_pin": "1234", "new_pin": "5678" }
    If user has no PIN set yet, current_pin may be omitted.
    """
    from .serializers import ChangePinSerializer
    ser = ChangePinSerializer(data=request.data)
    if not ser.is_valid():
        return Response(ser.errors, status=400)

    user        = request.user
    current_pin = ser.validated_data.get('current_pin', '')
    new_pin     = ser.validated_data['new_pin']

    # If PIN already exists, require current PIN for verification
    if user.pin_hash:
        if not current_pin:
            return Response({'detail': 'Current PIN is required.'}, status=400)
        if not user.check_pin(current_pin):
            return Response({'detail': 'Current PIN is incorrect.'}, status=400)

    user.set_pin(new_pin)
    user.save(update_fields=['pin_hash'])
    log_action(request, 'set_pin', user.username)
    return Response({'detail': 'PIN updated successfully.'})


# ─────────────────────────────────────────────────────────────────
# OFFLINE SYNC  (Mobile)
# ─────────────────────────────────────────────────────────────────

@api_view(['POST'])
@permission_classes([IsAuthenticated])
def api_sync_push(request):
    """
    POST /api/sync/push/
    Body: { "orders": [ <OrderCreateSerializer payload>, ... ] }
    Bulk offline order sync. Idempotent via client_id.
    Returns per-order: { client_id, status, order_id, order_number, detail }
    """
    raw_orders = request.data.get('orders', [])
    if not isinstance(raw_orders, list):
        return Response({'detail': 'orders must be a list.'}, status=400)

    results = []
    for raw in raw_orders:
        client_id = raw.get('client_id', '')

        # Dedup check
        if client_id:
            existing = POSOrder.objects.filter(
                client_id=client_id, company=request.company
            ).first()
            if existing:
                results.append({
                    'client_id': client_id, 'status': 'duplicate',
                    'order_id': existing.id, 'order_number': existing.order_number,
                    'detail': 'Already synced.',
                })
                continue

        from .serializers import OrderCreateSerializer as OCS
        ser = OCS(data=raw)
        if not ser.is_valid():
            results.append({
                'client_id': client_id, 'status': 'error',
                'order_id': None, 'order_number': None,
                'detail': str(ser.errors),
            })
            continue

        try:
            data       = ser.validated_data
            items_data = data.pop('items')
            settings_obj = getattr(request.company, 'settings', None)
            allow_zero   = settings_obj.enable_unlimited_stock if settings_obj else False
            vat_rate     = settings_obj.vat_rate if settings_obj else Decimal('0')

            subtotal = Decimal('0')
            resolved_items = []
            for item in items_data:
                product = Product.objects.select_for_update().get(
                    company=request.company, pk=item['product_id'], status='active'
                )
                is_unlimited = allow_zero and product.stock_quantity == 0
                if not is_unlimited and product.stock_quantity < item['quantity']:
                    raise ValueError(f"Insufficient stock for {product.name}.")
                line = product.price * item['quantity']
                disc = (line * item.get('discount', Decimal('0'))) / 100
                resolved_items.append({
                    'product': product, 'quantity': item['quantity'],
                    'line': line - disc, 'is_unlimited': is_unlimited,
                })
                subtotal += (line - disc)

            discount_amt = (subtotal * data.get('discount', Decimal('0'))) / 100
            tax_amount   = (subtotal * vat_rate) / 100
            final        = subtotal + tax_amount - discount_amt

            import random, string
            order_number   = 'ORD-' + ''.join(random.choices(string.digits, k=8))
            payment_method = data.get('payment_method', 'cash')
            amount_paid    = data.get('amount_paid', Decimal('0'))

            if payment_method in ['cash', 'pos', 'transfer', 'mobile_money'] and amount_paid <= Decimal('0'):
                amount_paid    = final
                payment_status = 'full'
            elif payment_method == 'credit':
                payment_status = 'credit'
            elif amount_paid >= final:
                amount_paid    = final
                payment_status = 'full'
            else:
                payment_status = 'partial'

            balance_amount = max(Decimal('0'), final - amount_paid)

            with transaction.atomic():
                order = POSOrder.objects.create(
                    company=request.company, order_number=order_number,
                    client_id=client_id, cashier=request.user.username,
                    customer_name=data.get('customer_name', ''),
                    customer_phone=data.get('customer_phone', ''),
                    payment_method=payment_method, amount_paid=amount_paid,
                    discount_amount=discount_amt, tax_amount=tax_amount,
                    final_amount=final, total_amount=subtotal,
                    balance_amount=balance_amount, payment_status=payment_status,
                    status='completed',
                    branch_id=data.get('branch') or getattr(request.user, 'branch_id', None),
                )
                for item in resolved_items:
                    POSOrderItem.objects.create(
                        order=order, product=item['product'],
                        product_name=item['product'].name,
                        quantity=item['quantity'],
                        unit_price=item['product'].price,
                        total_price=item['line'],
                    )
                    if not item['is_unlimited']:
                        item['product'].stock_quantity -= item['quantity']
                        item['product'].save()
                if amount_paid > 0:
                    from sales.models import PaymentTransaction
                    PaymentTransaction.objects.create(
                        order=order, payment_method=payment_method,
                        amount=amount_paid, status='completed'
                    )

            results.append({
                'client_id': client_id, 'status': 'created',
                'order_id': order.id, 'order_number': order.order_number,
                'detail': '',
            })
        except Exception as exc:
            results.append({
                'client_id': client_id, 'status': 'error',
                'order_id': None, 'order_number': None,
                'detail': str(exc),
            })

    return Response({'results': results})


@api_view(['GET'])
@permission_classes([IsAuthenticated])
def api_sync_status(request):
    """
    GET /api/sync/status/?since=<ISO-datetime>
    Returns lightweight changed-item counts since `since`.
    Mobile uses this to know if a catalog refresh is needed.
    """
    from django.utils.dateparse import parse_datetime
    since_str = request.query_params.get('since', '')
    since_dt  = parse_datetime(since_str) if since_str else None
    company   = request.company
    now       = timezone.now()

    data = {'server_time': now.isoformat(), 'since': since_str or None}

    if since_dt:
        # Only count updated products if the model has updated_at
        if hasattr(Product, 'updated_at'):
            data['products_changed'] = Product.objects.filter(
                company=company, updated_at__gte=since_dt
            ).count()
        else:
            data['products_changed'] = 0
        data['orders_since'] = POSOrder.objects.filter(
            company=company, created_at__gte=since_dt
        ).count()
    else:
        data['products_changed'] = Product.objects.filter(company=company).count()
        data['orders_since']     = 0

    try:
        from analytics.models import Notification
        data['pending_notifications'] = Notification.objects.filter(
            company=company, is_read=False
        ).filter(Q(user=request.user) | Q(user__isnull=True)).count()
    except Exception:
        data['pending_notifications'] = 0

    return Response(data)


# ─────────────────────────────────────────────────────────────────
# NOTIFICATIONS  (Mobile)
# ─────────────────────────────────────────────────────────────────

@api_view(['GET'])
@permission_classes([IsAuthenticated])
def api_notifications(request):
    """
    GET /api/notifications/?unread_only=true
    Returns company-wide + user-targeted notifications.
    """
    from analytics.models import Notification
    qs = Notification.objects.filter(
        company=request.company
    ).filter(
        Q(user=request.user) | Q(user__isnull=True)
    ).order_by('-created_at')

    if request.query_params.get('unread_only') == 'true':
        qs = qs.filter(is_read=False)

    data = [
        {
            'id':                n.id,
            'title':             n.title,
            'message':           n.message,
            'notification_type': n.notification_type,
            'icon':              n.icon,
            'is_read':           n.is_read,
            'url':               n.url,
            'created_at':        n.created_at.isoformat(),
        }
        for n in qs[:100]
    ]
    return Response(data)


@api_view(['POST'])
@permission_classes([IsAuthenticated])
def api_notification_read(request, pk):
    """POST /api/notifications/<pk>/read/ — mark a single notification read."""
    from analytics.models import Notification
    try:
        notif = Notification.objects.get(pk=pk, company=request.company)
    except Notification.DoesNotExist:
        return Response({'detail': 'Not found.'}, status=404)
    notif.is_read = True
    notif.save(update_fields=['is_read'])
    return Response({'detail': 'Marked as read.'})


# ─────────────────────────────────────────────────────────────────
# EXPENSES  (Mobile)
# ─────────────────────────────────────────────────────────────────

@api_view(['GET'])
@permission_classes([IsAuthenticated])
def api_expense_categories(request):
    """GET /api/expenses/categories/ — list expense categories."""
    from analytics.models import ExpenseCategory
    cats = ExpenseCategory.objects.filter(company=request.company).order_by('name')
    return Response([{'id': c.id, 'name': c.name} for c in cats])


@api_view(['GET', 'POST'])
@permission_classes([IsAuthenticated])
def api_expenses(request):
    """
    GET  /api/expenses/?date_from=YYYY-MM-DD&date_to=YYYY-MM-DD&category=<id>
    POST /api/expenses/
         Body: { amount, expense_date, category (id or null), reference, notes }
    """
    from analytics.models import OperatingExpense, ExpenseCategory

    if request.method == 'GET':
        if request.user.role not in ('admin', 'manager', 'superadmin'):
            return Response({'detail': 'Forbidden.'}, status=403)

        qs = OperatingExpense.objects.filter(
            company=request.company
        ).select_related('category', 'logged_by').order_by('-expense_date', '-created_at')

        date_from = request.query_params.get('date_from')
        date_to   = request.query_params.get('date_to')
        cat_id    = request.query_params.get('category')
        if date_from: qs = qs.filter(expense_date__gte=date_from)
        if date_to:   qs = qs.filter(expense_date__lte=date_to)
        if cat_id:    qs = qs.filter(category_id=cat_id)

        return Response([
            {
                'id':            e.id,
                'category':      e.category_id,
                'category_name': e.category.name if e.category else 'Uncategorized',
                'amount':        str(e.amount),
                'expense_date':  str(e.expense_date),
                'reference':     e.reference,
                'notes':         e.notes,
                'logged_by_name': (
                    e.logged_by.get_full_name() or e.logged_by.username
                ) if e.logged_by else '',
                'created_at':    e.created_at.isoformat(),
            }
            for e in qs[:500]
        ])

    # POST
    if request.user.role not in ('admin', 'manager', 'superadmin'):
        return Response({'detail': 'Forbidden.'}, status=403)

    amount       = request.data.get('amount')
    expense_date = request.data.get('expense_date')
    if not amount or not expense_date:
        return Response({'detail': 'amount and expense_date are required.'}, status=400)

    cat = None
    cat_id = request.data.get('category')
    if cat_id:
        try:
            cat = ExpenseCategory.objects.get(pk=cat_id, company=request.company)
        except ExpenseCategory.DoesNotExist:
            return Response({'detail': 'Category not found.'}, status=404)

    expense = OperatingExpense.objects.create(
        company=request.company, category=cat, amount=amount,
        expense_date=expense_date,
        reference=request.data.get('reference', ''),
        notes=request.data.get('notes', ''),
        logged_by=request.user,
    )
    return Response({
        'id':            expense.id,
        'category':      expense.category_id,
        'category_name': expense.category.name if expense.category else 'Uncategorized',
        'amount':        str(expense.amount),
        'expense_date':  str(expense.expense_date),
        'reference':     expense.reference,
        'notes':         expense.notes,
        'logged_by_name': request.user.get_full_name() or request.user.username,
        'created_at':    expense.created_at.isoformat(),
    }, status=201)


@api_view(['GET', 'PATCH', 'DELETE'])
@permission_classes([IsAuthenticated])
def api_expense_detail(request, pk):
    """GET/PATCH/DELETE /api/expenses/<pk>/"""
    from analytics.models import OperatingExpense, ExpenseCategory
    try:
        expense = OperatingExpense.objects.select_related('category', 'logged_by').get(
            pk=pk, company=request.company
        )
    except OperatingExpense.DoesNotExist:
        return Response({'detail': 'Not found.'}, status=404)

    if request.method == 'GET':
        return Response({
            'id':            expense.id,
            'category':      expense.category_id,
            'category_name': expense.category.name if expense.category else 'Uncategorized',
            'amount':        str(expense.amount),
            'expense_date':  str(expense.expense_date),
            'reference':     expense.reference,
            'notes':         expense.notes,
            'logged_by_name': (
                expense.logged_by.get_full_name() or expense.logged_by.username
            ) if expense.logged_by else '',
            'created_at':    expense.created_at.isoformat(),
        })

    if request.user.role not in ('admin', 'manager', 'superadmin'):
        return Response({'detail': 'Forbidden.'}, status=403)

    if request.method == 'PATCH':
        if 'amount'       in request.data: expense.amount       = request.data['amount']
        if 'expense_date' in request.data: expense.expense_date = request.data['expense_date']
        if 'reference'    in request.data: expense.reference    = request.data['reference']
        if 'notes'        in request.data: expense.notes        = request.data['notes']
        if 'category'     in request.data:
            cat_id = request.data['category']
            if cat_id:
                try:
                    expense.category = ExpenseCategory.objects.get(
                        pk=cat_id, company=request.company
                    )
                except ExpenseCategory.DoesNotExist:
                    return Response({'detail': 'Category not found.'}, status=404)
            else:
                expense.category = None
        expense.save()
        return Response({'detail': 'Updated.'})

    expense.delete()
    return Response(status=204)


# ─────────────────────────────────────────────────────────────────
# COMPANY REGISTRATION  (Mobile — creates new tenant)
# ─────────────────────────────────────────────────────────────────

@api_view(['POST'])
@permission_classes([AllowAny])
def api_register(request):
    """
    POST /api/auth/register/
    Body: { company_name, username, email, password, phone? }
    Creates a new Company + admin user + trial subscription.
    Returns: { token, user, company_slug }
    """
    company_name = (request.data.get('company_name') or '').strip()
    username     = (request.data.get('username')     or '').strip()
    email        = (request.data.get('email')        or '').strip()
    password     = (request.data.get('password')     or '').strip()
    phone        = (request.data.get('phone')        or '').strip()

    errors = {}
    if not company_name: errors['company_name'] = 'Company name is required.'
    if not username:     errors['username']     = 'Username is required.'
    if not email:        errors['email']        = 'Email is required.'
    if not password or len(password) < 6:
        errors['password'] = 'Password must be at least 6 characters.'
    if errors:
        return Response(errors, status=400)

    User = get_user_model()
    from django.contrib.auth.password_validation import validate_password
    from django.core.exceptions import ValidationError as DjValidationError
    from companies.models import Company
    from subscriptions.services import create_trial_subscription
    from django.utils.text import slugify
    import uuid

    if User.objects.filter(username=username).exists():
        return Response({'username': 'Username already taken.'}, status=400)
    if User.objects.filter(email=email).exists():
        return Response({'email': 'Email already registered.'}, status=400)

    try:
        validate_password(password)
    except DjValidationError as e:
        return Response({'password': list(e.messages)}, status=400)

    # Generate unique slug
    base_slug = slugify(company_name)
    slug = base_slug
    counter = 1
    while Company.objects.filter(slug=slug).exists():
        slug = f'{base_slug}-{counter}'
        counter += 1

    with transaction.atomic():
        company = Company.objects.create(
            name=company_name, slug=slug, email=email,
            phone=phone, is_active=True,
        )
        user = User.objects.create_user(
            username=username, email=email, password=password,
            phone=phone, role='admin', company=company, is_active=True,
        )
        try:
            create_trial_subscription(company)
        except Exception:
            pass  # Non-fatal — company is created, trial can be fixed later

    token, _ = Token.objects.get_or_create(user=user)
    from .serializers import UserSerializer
    return Response({
        'token':        token.key,
        'company_slug': company.slug,
        'user':         UserSerializer(user).data,
    }, status=201)


# ─────────────────────────────────────────────────────────────────
# PASSWORD RESET  (Mobile)
# ─────────────────────────────────────────────────────────────────

@api_view(['POST'])
@permission_classes([AllowAny])
def api_password_reset_request(request):
    """
    POST /api/auth/password-reset/
    Body: { email }
    Sends a password-reset email with a one-time UID+token link.
    Always returns 200 (do not leak whether email exists).
    """
    email = (request.data.get('email') or '').strip().lower()
    if not email:
        return Response({'detail': 'Email is required.'}, status=400)

    User = get_user_model()
    from django.contrib.auth.tokens import default_token_generator
    from django.utils.encoding import force_bytes
    from django.utils.http import urlsafe_base64_encode
    from django.core.mail import send_mail
    from django.conf import settings as dj_settings

    try:
        user = User.objects.get(email__iexact=email, is_active=True)
        uid   = urlsafe_base64_encode(force_bytes(user.pk))
        token = default_token_generator.make_token(user)
        # Build a deep-link the mobile app can open: swiftpos://reset/<uid>/<token>
        reset_link = f"{getattr(dj_settings, 'MOBILE_RESET_SCHEME', 'swiftpos')}://reset/{uid}/{token}"
        send_mail(
            subject='Reset your SwiftPOS password',
            message=(
                f'Hello {user.get_full_name() or user.username},\n\n'
                f'Tap the link below to reset your password:\n\n{reset_link}\n\n'
                'This link expires in 1 hour. If you did not request this, ignore this email.'
            ),
            from_email=getattr(dj_settings, 'DEFAULT_FROM_EMAIL', 'noreply@swiftpos.ng'),
            recipient_list=[user.email],
            fail_silently=True,
        )
    except User.DoesNotExist:
        pass  # Silent — do not reveal whether email exists

    return Response({'detail': 'If that email is registered, a reset link has been sent.'})


@api_view(['POST'])
@permission_classes([AllowAny])
def api_password_reset_confirm(request):
    """
    POST /api/auth/password-reset/confirm/
    Body: { uid, token, new_password }
    Validates the token and updates the password.
    """
    uid          = (request.data.get('uid')          or '').strip()
    token        = (request.data.get('token')        or '').strip()
    new_password = (request.data.get('new_password') or '').strip()

    if not uid or not token or not new_password:
        return Response({'detail': 'uid, token and new_password are required.'}, status=400)
    if len(new_password) < 6:
        return Response({'detail': 'Password must be at least 6 characters.'}, status=400)

    User = get_user_model()
    from django.contrib.auth.tokens import default_token_generator
    from django.utils.encoding import force_str
    from django.utils.http import urlsafe_base64_decode

    try:
        pk   = force_str(urlsafe_base64_decode(uid))
        user = User.objects.get(pk=pk)
    except Exception:
        return Response({'detail': 'Invalid reset link.'}, status=400)

    if not default_token_generator.check_token(user, token):
        return Response({'detail': 'Reset link has expired or is invalid.'}, status=400)

    user.set_password(new_password)
    user.save(update_fields=['password'])
    # Invalidate all existing tokens
    Token.objects.filter(user=user).delete()
    return Response({'detail': 'Password updated successfully. Please log in.'})

