from rest_framework import viewsets
from rest_framework.views import APIView
from rest_framework.filters import SearchFilter, OrderingFilter
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.response import Response
from rest_framework.decorators import action
from .models import *
from .serializers import *
from datetime import datetime, timedelta
from rest_framework import status
import json
from django.db.models import Q, Sum
from django.db import transaction
from django.utils import timezone
from rest_framework.pagination import PageNumberPagination
from questbanker_api.utils import get_current_user
from ledgers.models import OrganisationSubAccount,SystemTransactions,CreditorSupplies, DebtorAccounts, DebtorSupplies
from ledgers.ledgers_helper import generate_reference_no,get_chart_of_account_by_code,generate_chart_of_account_code
from savings.models import SavingAccount, SavingAccountTransactions
from organisations.models import Organisation, OrganisationBranch


class TrashInventoryView(APIView):
     def post(self, request):
        organisation  = self.request.data.get('organisation')
        if organisation:
            Order.objects.filter(order_branch__branch_organisation__id=organisation).delete()
            Stock.objects.filter(stock_branch__branch_organisation__id=organisation).delete()
            OrganisationSeason.objects.filter(organisation__id=organisation).delete()
        
        return Response({"message":"Success"})

class SearchNewCustomerSupplierAPIView(APIView):
    def get(self, request, format=None):
        serializer = []
        search     = request.GET.get('search', None)
        if search:
            customer = Customer.objects.filter((Q(old_member_number__icontains=str(search)) | Q(name__icontains=search)), supplier_customer__isnull=True).all()
            serializer = CustomerSerializer(customer, many=True).data
            
        return Response({"count": len(serializer), "results": serializer})


class ProductCategoriesViewSet(viewsets.ModelViewSet):
    serializer_class = ProductCategorySerializer
    filter_backends = (SearchFilter, OrderingFilter, DjangoFilterBackend, )
    search_fields = ('category_name', )
    def get_queryset(self):
        organisation_id = get_current_user(self.request, 'organisation_id', None)
        return ProductCategory.objects.filter(**{'organisation__id':organisation_id,"deleted":False}).order_by('-id')
    
    def perform_create(self, serializer):
        organisation_id = get_current_user(self.request, 'organisation_id', None)
        serializer.save(organisation_id = organisation_id,added_by=self.request.user)

''' class ProductsViewSet(viewsets.ModelViewSet):
    serializer_class = ProductSerializer
    def get_queryset(self):
        organisation_id = get_current_user(self.request, 'organisation_id', None)
        return Product.objects.filter(**{'organisation__id':organisation_id,"deleted":False}).order_by('-id')
    
    def perform_create(self, serializer):
        organisation_id = get_current_user(self.request, 'organisation_id', None)
        serializer.save(organisation_id = organisation_id,added_by=self.request.user) '''

class ProductsAPIView(APIView):
    serializer_class = ProductSerializer

    def get(self, request, format=None):
        organisation_id = get_current_user(self.request, 'organisation_id', None)
        branch_id = get_current_user(self.request, 'organisation_branch_id', None)
        filter_array = {'organisation__id': organisation_id, 'deleted': False}
        page_size = self.request.GET.get('page_size')
        service_type = self.request.GET.get('service_type')
        search = self.request.GET.get('search', None)

        if search:
            filter_array["product_name__icontains"] = search

        paginator = PageNumberPagination()
        paginator.page_size = page_size or 10

        # You can still filter by service_type if you plan to reuse it later
        products = Product.objects.filter(**filter_array).order_by('-id')

        response = paginator.paginate_queryset(products, request)
        response_data = ProductSerializer(
            response,
            many=True,
            context={'branch_id': branch_id}
        ).data

        return Response({"count": len(response_data), "results": response_data})

    def post(self, request, format=None):
        """
        Create a new Product with the new structure
        """
        organisation_id = get_current_user(self.request, 'organisation_id', None)
        product_name = request.data.get('product_name')
        description = request.data.get('description', '')
        category_id = request.data.get('category')
        purchase_unit_type = request.data.get('purchase_unit_type', '')
        purchase_price = request.data.get('purchase_price', 0)
        sale_unit_type = request.data.get('sale_unit_type', '')
        sale_price = request.data.get('sale_price', 0)

        # Create the product entry
        product = Product.objects.create(
            product_name=product_name,
            description=description,
            category_id=category_id,
            purchase_unit_type=purchase_unit_type,
            purchase_price=purchase_price,
            sale_unit_type=sale_unit_type,
            sale_price=sale_price,
            organisation_id=organisation_id,
            added_by=self.request.user
        )

        return Response(ProductSerializer(product).data, status=status.HTTP_201_CREATED)

    def put(self, request, format=None):
        """
        Update an existing Product
        """
        id = request.data.get('product_id')
        action = request.data.get('action', None)
        product = Product.objects.filter(pk=id).first()

        if not product:
            return Response({"error": "Product not found"}, status=status.HTTP_404_NOT_FOUND)

        if action == 'update':
            product.product_name = request.data.get('product_name', product.product_name)
            product.description = request.data.get('description', product.description)
            product.purchase_unit_type = request.data.get('purchase_unit_type', product.purchase_unit_type)
            product.purchase_price = request.data.get('purchase_price', product.purchase_price)
            product.sale_unit_type = request.data.get('sale_unit_type', product.sale_unit_type)
            product.sale_price = request.data.get('sale_price', product.sale_price)

            category_id = request.data.get('category', None)
            if category_id:
                product.category = ProductCategory.objects.get(pk=category_id)

            product.save()

        elif action == 'delete':
            product.delete()

        return Response(ProductSerializer(product).data, status=status.HTTP_200_OK)
        
class StocksViewSet(viewsets.ModelViewSet):
    serializer_class = StockSerializer
    
    def get_queryset(self):
        search = self.request.GET.get('search', None)
        branch_id = get_current_user(self.request, 'organisation_branch_id',None)
        organisation_id = get_current_user(self.request, 'organisation_id', None)
        filter_array = {'stock_branch__branch_organisation__id':organisation_id,'stock_branch__id':branch_id}
        if search:
            filter_array["product__product_name__icontains"] = search
        return Stock.objects.filter(**filter_array).order_by('-id')
    
    def perform_create(self, serializer):
        supplier_id = self.request.data.get('supplier_id')
        payment_method  = self.request.data.get('payment_method')
        quantity   = self.request.data.get('quantity')
        product_id      = self.request.data.get('product')
        purchase_price = self.request.data.get('purchase_price')
        income_chart_id = self.request.data.get('income_chart')
        stock_chart_id = self.request.data.get('stock_chart')
        organisation_id = get_current_user(self.request, 'organisation_id', None)
        branch_id = get_current_user(self.request, 'organisation_branch_id',None)
        record_date = self.request.data.get('record_date')

        organisation = Organisation.objects.get(pk=organisation_id)
        product = Product.objects.filter(id=product_id).first()

        stock_chart = OrganisationSubAccount.objects.filter(pk=stock_chart_id).first()
        if not stock_chart:
            return Response({"message": "stock_chart is required", "statusCode": "400"}, status=400)

        # Determine credit account based on payment method
        if payment_method == 'credit':
            payable_chart = get_chart_of_account_by_code('sys-2123', organisation)
            credit_chart_id = payable_chart.id
        else:
            credit_chart_id = self.request.data.get('credit_chart')

        reference_no = generate_reference_no(stock_chart.account_line, organisation_id, 'ast')
        heading = 'Stock purchase: ' + product.product_name + " of amount: " + str(float(purchase_price) * float(quantity))

        transaction = SystemTransactions(amount=float(purchase_price) * float(quantity), heading=heading, record_date=record_date, payment_method=payment_method, reference_no=reference_no, debit_chart=stock_chart, credit_chart_id=credit_chart_id, branch_id=branch_id, added_by=self.request.user)
        transaction.save()
        if transaction:
            serializer.save(stock_branch_id=branch_id, added_by=self.request.user, transaction=transaction)
            if payment_method == 'credit':
                creditorsupplies = CreditorSupplies(creditor_id=supplier_id, reference_transaction=transaction, added_by=self.request.user.id)
                creditorsupplies.save()

        if not income_chart_id:
            return Response({"message": "income_chart is required", "statusCode": "400"}, status=400)
        product.income_chart = OrganisationSubAccount.objects.get(pk=income_chart_id)
        product.stock_chart = stock_chart
        product.save(update_fields=['income_chart', 'stock_chart'])

    def perform_update(self,serializer):
        supplier_id = self.request.data.get('supplier_id')
        record_date = self.request.data.get('record_date')
        payment_method = self.request.data.get('payment_method')
        quantity = self.request.data.get('quantity')
        purchase_price = self.request.data.get('purchase_price')
        income_chart_id = self.request.data.get('income_chart')
        branch_id = get_current_user(self.request, 'organisation_branch_id',None)
        organisation_id = get_current_user(self.request, 'organisation_id', None)
        action = self.request.data.get('action')
        
        if action == 'update':
            updated_instance = serializer.save()
            if updated_instance:
                organisation = Organisation.objects.get(pk=organisation_id)
                product = Product.objects.filter(id=updated_instance.product_id).first()

                stock_chart_id = self.request.data.get('stock_chart')
                stock_chart = OrganisationSubAccount.objects.filter(pk=stock_chart_id).first() or updated_instance.transaction.debit_chart

                # Determine credit account based on payment method
                if payment_method == 'credit':
                    payable_chart = get_chart_of_account_by_code('sys-2123', organisation)
                    credit_chart_id = payable_chart.id
                else:
                    credit_chart_id = updated_instance.transaction.credit_chart.id
                
                heading = 'Stock purchase Update: '+ product.product_name +" of amount: "+str(float(updated_instance.purchase_price) * float(updated_instance.quantity))
                
                updated_instance.transaction.amount = float(purchase_price) * float(quantity)
                updated_instance.transaction.heading = heading
                updated_instance.transaction.record_date = record_date
                updated_instance.transaction.debit_chart = stock_chart
                updated_instance.transaction.credit_chart_id = credit_chart_id
                updated_instance.transaction.save()
                
                if payment_method == 'credit':
                    creditorsupplies = CreditorSupplies(creditor_id=supplier_id,reference_transaction=updated_instance.transaction, added_by=self.request.user.id)
                    creditorsupplies.save()
                
                if income_chart_id:
                    product.income_chart = OrganisationSubAccount.objects.get(pk=income_chart_id)
                    product.save()

        if action == 'delete':
            stock_instance = Stock.objects.get(pk=self.request.data.get('id'))
            system_transaction_instance = stock_instance.transaction
            stock_instance.delete()
            system_transaction_instance.delete()

class OrganisationSuppliersAPIView(APIView):

    def get(self, request, format=None):
        response = {}
        organisation_id = get_current_user(self.request, 'organisation_id', None)
        product_id = self.request.GET.get('product_id', None)
        customer_id = self.request.GET.get('customer_id', None)
        if product_id and customer_id:
            supplier = OrganisationProductSupplier.objects.filter(supplier__customer__id=customer_id, product__id=product_id, organisation__id=organisation_id).first()
            if supplier:
                response = {"unit_cost": supplier.unit_cost, "id": supplier.id}
            else:
                response = {"unit_cost": 0, "id": ''}
        else:
            suppliers = OrganisationSuppliers.objects.filter(**{'organisation__id':organisation_id,"deleted":False}).order_by('-id')
            serializer = OrganisationSuppliersSerializer(suppliers, many=True).data
            response = {"count": len(serializer), "results": serializer}
        
        return Response(response)

    def post(self, request, format=None):
        supplier = request.data.get('supplier', None)
        products = request.data.get('products', None)
        response = {"statusCode":"200", "message":"created"}

        if supplier:
            organisation_id = get_current_user(self.request, 'organisation_id', None)
            data = {"customer":Customer.objects.get(pk=supplier['customerId']), 
            "description":supplier['description'], "added_by":request.user,
            "organisation":Organisation.objects.get(pk=organisation_id),
            "bank_name":supplier['bankName'], "bank_acc_no":supplier['bankAccNo']}

            supplier_obj = OrganisationSuppliers.objects.create(**data)
            if supplier_obj:
                if products:
                    for product in products:
                        product_obj = {"supplier": supplier_obj, "organisation":Organisation.objects.get(pk=organisation_id), "unit_cost":product['unitCost'], "product":Product.objects.get(pk=product['productId']), "added_by":request.user}
                        OrganisationProductSupplier.objects.create(**product_obj)
                else:
                    response = {"statusCode":"500", "message":"error"}
            else:
                response = {"statusCode":"500", "message":"error"}
        return Response(response)


class OrganisationSuppliersViewSet(viewsets.ModelViewSet):
    serializer_class = OrganisationSuppliersSerializer
    filter_backends = (SearchFilter, OrderingFilter, DjangoFilterBackend, )
    search_fields = ('id', )

    def get_queryset(self):
        organisation_id = get_current_user(self.request, 'organisation_id', None)
        return OrganisationSuppliers.objects.filter(**{'organisation__id':organisation_id,"deleted":False}).order_by('-id')
    
    def perform_create(self, serializer):
        organisation_id = get_current_user(self.request, 'organisation_id', None)
        organisation = Organisation.objects.get(id=organisation_id)
        serializer.save(organisation=organisation, added_by=self.request.user)

class OrganisationSeasonViewSet(viewsets.ModelViewSet):
    serializer_class = OrganisationSeasonSerializer
    filter_backends = (SearchFilter, OrderingFilter, DjangoFilterBackend, )
    search_fields = ('id', )

    def get_queryset(self):
        organisation_id = get_current_user(self.request, 'organisation_id', None)
        return OrganisationSeason.objects.filter(**{'organisation__id':organisation_id,"deleted":False}).order_by('-id')
    
class InventoryPayablesViewSet(viewsets.ModelViewSet):
    serializer_class = SupplierPayableSerializer
    queryset = SupplierPayable.objects.all()
    filter_backends = (SearchFilter, DjangoFilterBackend)
    search_fields = ['supplier__customer__name', 'description']

    def get_queryset(self):
        organisation_id = get_current_user(self.request, 'organisation_id', None)
        return SupplierPayable.objects.filter(organisation_id=organisation_id)

    def perform_create(self, serializer):
        """Automatically attach the logged-in user and their organisation."""
        organisation_id = get_current_user(self.request, 'organisation_id', None)

        serializer.save(
            added_by=self.request.user,
            organisation_id=organisation_id
        )


class InventoryPaymentsViewSet(viewsets.ModelViewSet):
    serializer_class = SupplierPaymentSerializer
    queryset = SupplierPayment.objects.all()
    filter_backends = (SearchFilter, DjangoFilterBackend)
    search_fields = ['reference_no', 'payable__supplier__customer__name']

    def get_queryset(self):
        organisation_id = get_current_user(self.request, 'organisation_id', None)
        return SupplierPayment.objects.filter(organisation_id=organisation_id)

    def perform_create(self, serializer):
        """Automatically attach the logged-in user and their organisation."""
        organisation_id = get_current_user(self.request, 'organisation_id', None)

        serializer.save(
            added_by=self.request.user,
            organisation_id=organisation_id
        ) 

class ProductOrdersAPIView(APIView):
    serializer_class = OrderSerializer

    def get(self, request, format=None):
        response = []
        orders_query = []
        period_response = []
        total = 0
        balance = 0

        customer_id = request.GET.get('customer_id')
        season_id = request.GET.get('season_id')
        action = request.GET.get('action')

        organisation_id = get_current_user(request, 'organisation_id')
        branch_id = get_current_user(request, 'organisation_branch_id')

        if action in ['sale', 'lease']:
            transaction_type = 'Sale' if action == 'sale' else 'Lease'

            orders = Order.objects.filter(
                order_branch__branch_organisation_id=organisation_id,
                order_branch=branch_id,
                transaction_type=transaction_type,
                transaction_status="normal"
            ).order_by('-id')

            orders_query = OrderSerializer(orders, many=True).data

            for order in orders:
                order_items = OrderItems.objects.filter(order=order)
                for item in order_items:
                    response.append({
                        "id": order.transaction.id,
                        "amount": order.transaction.amount,
                        "qty": item.quantity,
                        "price": item.price,
                        "description": order.transaction.heading,
                        "payment_method": order.transaction.payment_method,
                        "transaction_type": order.transaction_type,
                        "record_date": order.transaction.record_date,
                        "maturity_date": order.maturity_date,
                        "order_id": order.id,
                        "product_name": item.product.product_name,
                        "transaction_status": order.transaction_status,
                        "reference_no": order.transaction.reference_no,
                    })

            return Response({
                "balance": balance,
                "transactions": response,
                "orders_query": orders_query,
                "period": period_response,
                "period_total": total
            })

        season = OrganisationSeason.objects.filter(id=season_id).first()
        if season:
            start_date = season.start_date.strftime('%Y-%m-%d')
            end_date = season.end_date.strftime('%Y-%m-%d')

            orders = Order.objects.filter(
                order_branch__branch_organisation_id=organisation_id,
                order_branch=branch_id,
                customer_id=customer_id,
                transaction_type__in=['Payable', 'Sale', 'Receivable'],
                record_date__date__gte=start_date,
                record_date__date__lte=end_date,
                transaction_status='normal'
            ).order_by('id')

            for order in orders:
                item = OrderItems.objects.filter(order=order).first()
                if order.transaction_type == 'Payable':
                    balance += order.transaction.amount

                response.append({
                    "id": order.transaction.id,
                    "amount": order.transaction.amount,
                    "qty": item.quantity if item else "",
                    "description": order.transaction.heading,
                    "payment_method": order.transaction.payment_method,
                    "transaction_type": order.transaction_type,
                    "record_date": order.transaction.record_date,
                    "maturity_date": order.maturity_date,
                    "order_id": order.id,
                    "price": item.price if item else "",
                    "transaction_status": order.transaction_status,
                    "reference_no": order.transaction.reference_no
                })

            # Period summary
            start_dt = datetime.strptime(start_date, "%Y-%m-%d")
            end_dt = datetime.strptime(end_date, "%Y-%m-%d")
            current = start_dt

            while current <= end_dt:
                d = current.strftime("%Y-%m-%d")
                qty_sum = OrderItems.objects.filter(
                    order__transaction_type="Payable",
                    order__transaction__record_date__date=d,
                    order__customer_id=customer_id,
                    order__transaction_status="normal"
                ).aggregate(total=Sum("quantity"))["total"]

                qty = qty_sum or 0
                total += qty

                period_response.append({
                    "date": current.strftime("%d/%m"),
                    "qty": qty
                })

                current += timedelta(days=1)

        return Response({
            "balance": balance,
            "transactions": response,
            "orders_query": orders_query,
            "period": period_response,
            "period_total": total
        })

    def put(self, request, pk, format=None):
        update_reason = request.data.get('update_reason', None)
        amount = request.data.get('amount')
        record_date = request.data.get('record_date')
        maturity_date = request.data.get('maturity_date')
        qty = request.data.get('qty')

        # Update transaction
        SystemTransactions.objects.filter(id=pk).update(
            coment=update_reason,
            amount=amount,
            record_date=record_date
        )

        order_details = Order.objects.filter(transaction__id=pk).first()
        if order_details:
            order_details.maturity_date = maturity_date
            order_details.record_date = record_date
            order_details.save()

            order_item = OrderItems.objects.filter(order=order_details).first()
            if order_item:
                order_item.quantity = qty
                order_item.total_cost = amount
                order_item.save()

        debtor_supply = DebtorSupplies.objects.filter(reference_transaction_id=pk).first()
        if debtor_supply:
            debtor_supply.maturity_date = maturity_date
            debtor_supply.save()

        return Response({"message": "Updated Successfully", "statusCode": "200"})

    def post(self, request, format=None):
        transact_type = request.data.get('transaction_type')
        organisation_id = get_current_user(request, 'organisation_id')
        branch_id = get_current_user(request, 'organisation_branch_id')

        customer_id = request.data.get('customer_id')
        name = request.data.get('name')
        customer_type = request.data.get('customer_type')

        customer = None
        if customer_id:
            customer = Customer.objects.filter(pk=customer_id).first()
            if not customer:
                return Response({"error": "Customer not found"}, status=404)

        organisation = Organisation.objects.get(pk=organisation_id)
        organisation_branch = OrganisationBranch.objects.get(pk=branch_id)

        cart_items = request.data.get('items', [])
        record_date = request.data.get('record_date')
        payment_method = request.data.get('payment_method')
        voucher_no = request.data.get('voucher_no')
        selected_account = request.data.get('selected_account')

        # Get or create dedicated COGS chart (only auto-created chart)
        cogs_chart, created = OrganisationSubAccount.objects.get_or_create(
            account_name="Cost of Goods Sold - Inventory",
            account_code="sys-538-inv",
            account_organisation=organisation,
            defaults={
                'account_line': 'expenses',
                'parent_id': get_chart_of_account_by_code('sys-538', organisation),
                'added_by': request.user.id,
                'description': 'Cost of goods sold for inventory items'
            }
        )

        debit_chart = None
        if payment_method == "offset":
            savings_account = SavingAccount.objects.filter(id=selected_account).first()
            if not savings_account:
                return Response({"error": "Saving account not found"}, status=404)
            debit_chart = savings_account.account_product.accounts_chart
        elif selected_account:
            try:
                debit_chart = OrganisationSubAccount.objects.get(pk=selected_account)
            except OrganisationSubAccount.DoesNotExist:
                return Response({"error": "Selected account not found"}, status=404)

        if not (debit_chart and cogs_chart):
            return Response({"message": "No_charts", "statusCode": "500"})
            
        # Calculate totals and create individual income transactions per product
        total_amount = 0
        total_cost = 0
        for item in cart_items:
            if item:
                if transact_type == "Lease":
                    total_amount += item["stock_balance"]["selling_price"]
                    total_cost += item["stock_balance"]["cost_price"]
                else:
                    total_amount += item["stock_balance"]["selling_price"] * item["quantity"]
                    total_cost += item["stock_balance"]["cost_price"] * item["quantity"]

        # Create individual income transactions for each product
        transaction = None
        for item in cart_items:
            if not item:
                continue

            product = Product.objects.get(pk=item["id"])
            price = item["stock_balance"]["selling_price"]
            item_amount = price * item["quantity"] if transact_type != "Lease" else price

            income_chart_id = item.get("income_chart")
            income_chart = OrganisationSubAccount.objects.filter(pk=income_chart_id).first()
            if not income_chart:
                return Response({"error": f"income_chart is required for product {product.product_name}"}, status=400)

            reference_no = generate_reference_no(income_chart.account_line, organisation_id, 'sbt')
            
            # Create transaction for this product's income
            customer_label = f"{customer.name} - ({customer.member_number})" if customer else "Walk-in"
            transaction = SystemTransactions.objects.create(
                amount=item_amount,
                heading=f"Order of {product.product_name} successfully placed for {customer_label}",
                record_date=record_date,
                payment_method=payment_method,
                voucher_no=voucher_no,
                reference_no=reference_no,
                debit_chart=debit_chart,
                credit_chart=income_chart,
                branch_id=branch_id,
                added_by=request.user,
                coment=request.data.get("note")
            )
        provided_maturity = request.data.get('maturity_date')
        maturity_value = provided_maturity if provided_maturity else timezone.now().date() + timedelta(days=30)
        
        # Use the last transaction created for the order (all transactions are linked via order items)
        if not transaction:
            return Response({"error": "No transaction created"}, status=500)
        
        product_names = ", ".join([Product.objects.get(pk=item["id"]).product_name for item in cart_items if item])
        customer_label = f"{customer.name} - ({customer.member_number})" if customer else "Walk-in"
        response = {"statusCode": "200", "message": f"Order of {product_names} successfully placed for {customer_label}"}
            
        if customer and payment_method == "credit":
            debtor = DebtorAccounts.objects.filter(
                customer=customer,
                organisation_id=organisation_id,
                deleted=False
            ).first()

            if not debtor:
                receivable_chart = get_chart_of_account_by_code("sys-1133", organisation)
                debtor = DebtorAccounts.objects.create(
                    account_name=customer.name,
                    telephone_number=getattr(customer, "phone_number", ""),
                    description="Auto-created on credit sale",
                    customer=customer,
                    organisation=organisation,
                    chart=receivable_chart,
                    added_by=request.user.id
                )

            DebtorSupplies.objects.create(
                debtor=debtor,
                reference_transaction=transaction,
                maturity_date=maturity_value,
                date_added=timezone.now(),
                added_by=request.user.id
            )
        if payment_method == "offset":
            SavingAccountTransactions.objects.create(
                transaction=transaction,
                customer_account=savings_account,
                transaction_type="withdrawal"
            )
        order = Order.objects.create(
            status="Processed",
            payment_method=payment_method,
            transaction_type=transact_type,
            transaction=transaction,
            order_branch=organisation_branch,
            added_by=request.user,
            maturity_date=maturity_value,
            customer_id=customer.id if customer else None 
        )

        for item in cart_items:
            if not item:
                continue

            product = Product.objects.get(pk=item["id"])
            price = item["stock_balance"]["selling_price"]
            amount = price * item["quantity"] if transact_type != "Lease" else price
            cost_price = item["stock_balance"]["cost_price"]
            item_cost = cost_price * item["quantity"] if transact_type != "Lease" else cost_price

            OrderItems.objects.create(
                quantity=item["quantity"],
                total_cost=item["stock_balance"]["cost_price"],
                price=amount,
                order=order,
                product=product,
                added_by=request.user
            )
            
            # Create COGS entry for each product sold
            if transact_type in ["Sale", "Lease"]:
                stock_chart_id = item.get("stock_chart")
                stock_chart = OrganisationSubAccount.objects.filter(pk=stock_chart_id).first()
                if not stock_chart:
                    return Response({"error": f"stock_chart is required for product {product.product_name}"}, status=400)

                # COGS Entry: Dr. COGS Chart (cost), Cr. Stock Chart (cost)
                cogs_reference_no = generate_reference_no(cogs_chart.account_line, organisation_id, 'cgs')
                SystemTransactions.objects.create(
                    amount=item_cost,
                    heading=f"Cost of goods sold - {product.product_name}",
                    record_date=record_date,
                    payment_method='settlement',
                    reference_no=cogs_reference_no,
                    debit_chart=cogs_chart,
                    credit_chart=stock_chart,
                    branch_id=branch_id,
                    added_by=request.user,
                    coment=f"COGS for {product.product_name}: {item_cost}"
                )
        
        # Reduce inventory quantities for sold items
        if transact_type in ["Sale", "Lease"]:
            from .helper import reduce_inventory_on_sale
            order_items = OrderItems.objects.filter(order=order)
            reduce_inventory_on_sale(order_items, branch_id)
            
        if transact_type == "Give_Product":
            response = stock_product_transactions(request, branch_id, organisation_id)

        return Response(response)

class OrdersAPIView(APIView):
    def get(self, request, format=None):
        pass
     
    def post(self, request, format=None):
        organisation_id = get_current_user(self.request, 'organisation_id', None)
        branch_id = get_current_user(self.request, 'organisation_branch_id', None)

        product_id  = self.request.data.get('product_id')
        qty         = self.request.data.get('quantity')
        price       = self.request.data.get('price')
        record_date = self.request.data.get('record_date')
        description = self.request.data.get('note')
        customer_id = self.request.data.get('customer_id')
        maturity_date = self.request.data.get('maturity_date')

        organisation = Organisation.objects.get(pk=organisation_id)
        organisation_branch = OrganisationBranch.objects.get(pk=branch_id)
        
        # For payable transactions (buying from customers), use correct accounts
        debit_chart = get_chart_of_account_by_code('sys-119', organisation)  # Stock from members
        credit_chart = get_chart_of_account_by_code('sys-2123', organisation)  # Inventory payable

        if credit_chart and debit_chart:
            payment_method = 'non_cash'
            voucher_no = self.request.data.get('voucher_no')

            # Generate reference number
            reference_no = generate_reference_no(credit_chart.account_line, organisation_id, 'lbt')

            # Save transaction
            transaction = SystemTransactions.objects.create(
                amount=price,
                heading=description,
                record_date=record_date,
                payment_method=payment_method,
                voucher_no=voucher_no,
                reference_no=reference_no,
                debit_chart=debit_chart,
                credit_chart=credit_chart,
                branch_id=branch_id,
                added_by=self.request.user,
                coment=description
            )

            # Create order
            order = Order.objects.create(
                status="Processed",
                customer=Customer.objects.get(pk=customer_id),
                payment_method=payment_method,
                transaction_type="Payable",
                transaction=transaction,
                order_branch=organisation_branch,
                added_by=request.user,
                maturity_date=maturity_date
            )

            # Create order items
            if product_id and qty:
                product = Product.objects.get(pk=product_id)
                OrderItems.objects.create(
                    quantity=qty,
                    total_cost=price,
                    price=price,
                    order=order,
                    product=product,
                    added_by=request.user
                )

            # Return full details of created order and transaction
            return Response({
                "statusCode": "200",
                "message": "Order created",
                "order": OrderSerializer(order).data,
                "transaction": SystemTransactionsSerializer(transaction).data
            })

        else:
            return Response({"statusCode": "500", "message": "No_charts"})

class DebtorsReceivablesAPIView(APIView):
    """
    Retrieve list of company debtors with receivables details.
    Allows viewing individual debtor details and managing payments/receivables.
    """
    
    def get(self, request, format=None):
        organisation_id = get_current_user(self.request, 'organisation_id', None)
        debtor_id = self.request.GET.get('debtor_id', None)
        
        if debtor_id:
            # Get specific debtor details
            from ledgers.models import DebtorAccounts, DebtorSupplies, DebtorPayments
            from ledgers.serializers import DebtorAccountsSerializer, DebtorSuppliesSerializer
            
            debtor = DebtorAccounts.objects.filter(
                id=debtor_id,
                organisation_id=organisation_id,
                deleted=False
            ).first()
            
            if debtor:
                # Get all receivables for this debtor
                supplies = DebtorSupplies.objects.filter(
                    debtor=debtor,
                    deleted=False
                ).order_by('-date_added')
                
                debtor_serializer = DebtorAccountsSerializer(debtor).data
                supplies_serializer = DebtorSuppliesSerializer(supplies, many=True).data
                
                response = {
                    'debtor': debtor_serializer,
                    'receivables': supplies_serializer,
                    'outstanding_balance': debtor_serializer.get('total_receivable', 0) - debtor_serializer.get('total_paid', 0)
                }
                return Response(response, status=status.HTTP_200_OK)
            else:
                return Response({"error": "Debtor not found"}, status=status.HTTP_404_NOT_FOUND)
        
        else:
            # List all debtors
            from ledgers.models import DebtorAccounts
            from ledgers.serializers import DebtorAccountsSerializer
            
            debtors = DebtorAccounts.objects.filter(
                organisation_id=organisation_id,
                deleted=False
            ).prefetch_related('supplies_debtor').order_by('-date_added')
            
            serializer = DebtorAccountsSerializer(debtors, many=True)
            
            return Response({
                'count': len(serializer.data),
                'results': serializer.data
            }, status=status.HTTP_200_OK)



class CustomerRequestViewSet(viewsets.ModelViewSet):
    serializer_class = CustomerRequestSerializer
    filter_backends = (SearchFilter, DjangoFilterBackend)
    search_fields = ('request_number', 'customer__name')
    
    def get_queryset(self):
        organisation_id = get_current_user(self.request, 'organisation_id', None)
        queryset = CustomerRequest.objects.filter(customer__customer_branch__branch_organisation_id=organisation_id)
        
        status_filter = self.request.query_params.get('status')
        product = self.request.query_params.get('product')
        customer = self.request.query_params.get('customer')
        
        if status_filter:
            queryset = queryset.filter(status=status_filter)
        if product:
            queryset = queryset.filter(product_id=product)
        if customer:
            queryset = queryset.filter(customer_id=customer)
            
        return queryset.order_by('-created_at')
    
    def perform_create(self, serializer):
        request_number = f"CR-{timezone.now().year}-{CustomerRequest.objects.count() + 1:05d}"
        serializer.save(request_number=request_number)


class AggregatedRequestViewSet(viewsets.ModelViewSet):
    serializer_class = AggregatedRequestSerializer
    filter_backends = (SearchFilter, DjangoFilterBackend)
    search_fields = ('request_number', 'title')
    
    def get_queryset(self):
        organisation_id = get_current_user(self.request, 'organisation_id', None)
        
        # Get requests where current org is either the requester OR the service provider
        queryset = AggregatedRequest.objects.filter(
            Q(organisation_id=organisation_id) | Q(service_provider_id=organisation_id)
        )
        
        status_filter = self.request.query_params.get('status')
        from_date = self.request.query_params.get('from_date')
        to_date = self.request.query_params.get('to_date')
        view_type = self.request.query_params.get('view_type')  # 'sent' or 'received'
        
        # Filter by view type
        if view_type == 'sent':
            queryset = queryset.filter(organisation_id=organisation_id)
        elif view_type == 'received':
            queryset = queryset.filter(
                service_provider_id=organisation_id,
                status__in=['sent', 'approved', 'delivered', 'cancelled']
            )
        
        if status_filter:
            queryset = queryset.filter(status=status_filter)
        if from_date:
            queryset = queryset.filter(request_date__gte=from_date)
        if to_date:
            queryset = queryset.filter(request_date__lte=to_date)
            
        return queryset.order_by('-created_at')
    
    @transaction.atomic
    def perform_create(self, serializer):
        organisation_id = get_current_user(self.request, 'organisation_id', None)
        request_number = f"AGR-{timezone.now().year}-{AggregatedRequest.objects.count() + 1:05d}"

        aggregated_request = serializer.save(
            request_number=request_number,
            created_by=self.request.user,
            organisation_id=organisation_id,
            status='pending',
        )

        total_cost = sum(item.total_cost for item in aggregated_request.items.all())
        aggregated_request.total_estimated_cost = total_cost
        aggregated_request.save()

    @transaction.atomic
    def partial_update(self, request, *args, **kwargs):
        instance = self.get_object()
        organisation_id = get_current_user(request, 'organisation_id', None)

        if instance.organisation_id != organisation_id:
            return Response({'error': 'Permission denied'}, status=status.HTTP_403_FORBIDDEN)
        if instance.status != 'pending':
            return Response({'error': 'Only pending requests can be edited'}, status=status.HTTP_400_BAD_REQUEST)

        items_data = request.data.pop('items', None)
        serializer = self.get_serializer(instance, data=request.data, partial=True)
        serializer.is_valid(raise_exception=True)
        aggregated_request = serializer.save()

        if items_data is not None:
            aggregated_request.items.all().delete()
            for item_data in items_data:
                product = Product.objects.filter(pk=item_data['product']).first()
                AggregatedRequestItem.objects.create(
                    aggregated_request=aggregated_request,
                    customer_id=item_data['customer'],
                    product=product,
                    quantity_requested=item_data['quantity_requested'],
                    unit_price=item_data.get('unit_price', product.sale_price if product else 0),
                    payment_method=item_data.get('payment_method', 'credit'),
                    cash_account_id=item_data.get('cash_account'),
                )

        total_cost = sum(item.total_cost for item in aggregated_request.items.all())
        aggregated_request.total_estimated_cost = total_cost
        aggregated_request.save(update_fields=['total_estimated_cost'])

        return Response(AggregatedRequestSerializer(aggregated_request).data)
    
    @action(detail=True, methods=['post'])
    def send(self, request, pk=None):
        aggregated_request = self.get_object()
        organisation_id = get_current_user(request, 'organisation_id', None)
        branch_id = get_current_user(request, 'organisation_branch_id', None)

        if aggregated_request.organisation_id != organisation_id:
            return Response({'error': 'Permission denied'}, status=status.HTTP_403_FORBIDDEN)
        if aggregated_request.status != 'pending':
            return Response({'error': 'Only pending requests can be sent'}, status=status.HTTP_400_BAD_REQUEST)

        aggregated_request.status = 'sent'
        aggregated_request.save()

        # Send SMS notification to service provider
        try:
            from exservices.exservices_helper import send_customer_sms
            provider = aggregated_request.service_provider
            if provider and provider.phone_number:
                item_lines = '\n'.join(
                    f"{item.product.product_name} x{item.quantity_requested}"
                    for item in aggregated_request.items.filter(deleted=False)[:10]
                )
                sms_msg = (
                    f"New Order Request: {aggregated_request.request_number} from "
                    f"{aggregated_request.organisation.name}.\n"
                    f"Items:\n{item_lines}\n"
                    f"Total: {aggregated_request.total_estimated_cost:,.0f}\n"
                    f"Delivery by: {aggregated_request.expected_delivery_date}"
                )
                send_customer_sms({
                    'sms_key': 'aggregated_request_sms',
                    'telephone': provider.phone_number,
                    'user': request.user,
                    'branch_id': branch_id,
                    'sms_msg': sms_msg,
                })
        except Exception:
            pass  # SMS failure must not block the send action

        return Response({'status': 'sent'})

    @action(detail=True, methods=['post'])
    def approve(self, request, pk=None):
        aggregated_request = self.get_object()
        aggregated_request.status = 'approved'
        aggregated_request.save()
        return Response({'status': 'approved'})
    
    @action(detail=True, methods=['post'])
    def cancel(self, request, pk=None):
        aggregated_request = self.get_object()
        aggregated_request.status = 'cancelled'
        aggregated_request.save()
        return Response({'status': 'cancelled'})
    
    @action(detail=True, methods=['post'])
    @transaction.atomic
    def register_stock(self, request, pk=None):
        aggregated_request = self.get_object()
        organisation_id = get_current_user(request, 'organisation_id', None)
        
        # Only service provider can register stock
        if aggregated_request.service_provider_id != organisation_id:
            return Response(
                {'error': 'Only the service provider can register stock for this request'},
                status=status.HTTP_403_FORBIDDEN
            )
        
        stocks_data = request.data.get('stocks', [])
        branch_id = get_current_user(request, 'organisation_branch_id', None)
        organisation = Organisation.objects.get(pk=organisation_id)
        
        created_stocks = []
        for stock_data in stocks_data:
            product = Product.objects.get(id=stock_data['product'])

            stock_chart_id = stock_data.get('stock_chart')
            stock_chart = OrganisationSubAccount.objects.filter(pk=stock_chart_id).first()
            if not stock_chart:
                return Response({"error": f"stock_chart is required for product {product.product_name}"}, status=400)

            payment_method = stock_data.get('payment_method', 'credit')
            if payment_method == 'credit':
                payable_chart = get_chart_of_account_by_code('sys-2123', organisation)
                credit_chart_id = payable_chart.id
            else:
                credit_chart_id = stock_data.get('credit_chart')
            
            reference_no = generate_reference_no(stock_chart.account_line, organisation_id, 'ast')
            heading = f"Stock purchase: {product.product_name} - {stock_data['quantity']} units"
            
            transaction = SystemTransactions.objects.create(
                amount=float(stock_data['purchase_price']) * float(stock_data['quantity']),
                heading=heading,
                record_date=stock_data.get('record_date', timezone.now().date()),
                payment_method=payment_method,
                reference_no=reference_no,
                debit_chart=stock_chart,
                credit_chart_id=credit_chart_id,
                branch_id=branch_id,
                added_by=request.user
            )
            
            stock = Stock.objects.create(
                batch_number=stock_data.get('batch_number', ''),
                quantity=stock_data['quantity'],
                purchase_price=stock_data['purchase_price'],
                sell_price=stock_data['sell_price'],
                payment_method=payment_method,
                transaction=transaction,
                stock_branch_id=branch_id,
                product=product,
                added_by=request.user
            )
            
            AggregatedStock.objects.create(
                aggregated_request=aggregated_request,
                stock=stock,
                quantity_allocated=stock_data['quantity'],
                allocation_date=timezone.now().date(),
                created_by=request.user
            )

            product.stock_chart = stock_chart
            product.save(update_fields=['stock_chart'])

            created_stocks.append(stock)
        
        return Response({
            'message': f'{len(created_stocks)} stock(s) registered successfully',
            'stocks': StockSerializer(created_stocks, many=True).data
        })
    
    @action(detail=True, methods=['post'])
    @transaction.atomic
    def allocate_stock(self, request, pk=None):
        aggregated_request = self.get_object()
        organisation_id = get_current_user(request, 'organisation_id', None)
        
        # Only service provider can allocate stock
        if aggregated_request.service_provider_id != organisation_id:
            return Response(
                {'error': 'Only the service provider can allocate stock for this request'},
                status=status.HTTP_403_FORBIDDEN
            )
        
        allocations = request.data.get('allocations', [])
        
        for allocation in allocations:
            item = AggregatedRequestItem.objects.get(id=allocation['item_id'])
            stock = Stock.objects.get(id=allocation['stock'])
            quantity = allocation['quantity']
            
            stock.quantity -= quantity
            stock.save()
            
            item.quantity_fulfilled += quantity
            item.save()
        
        return Response({'message': 'Stock allocated successfully'})
    
    @action(detail=True, methods=['post'])
    @transaction.atomic
    def process_payments(self, request, pk=None):
        """Process per-item payments: cash debit or credit (receivable on member's savings account)"""
        aggregated_request = self.get_object()
        organisation_id = get_current_user(request, 'organisation_id', None)
        branch_id = get_current_user(request, 'organisation_branch_id', None)
        organisation = Organisation.objects.get(pk=organisation_id)

        if aggregated_request.organisation_id != organisation_id:
            return Response(
                {'error': 'Only the requesting organisation can process payments'},
                status=status.HTTP_403_FORBIDDEN
            )

        payments = request.data.get('payments', [])  # [{item_id, payment_method, cash_account_id}]
        if not payments:
            return Response({'error': 'No payment data provided'}, status=status.HTTP_400_BAD_REQUEST)

        # Get or create receivables parent chart (sys-113 = Receivables / Debtors)
        receivable_parent = get_chart_of_account_by_code('sys-113', organisation)
        if not receivable_parent:
            return Response({'error': 'Receivables chart not configured'}, status=status.HTTP_400_BAD_REQUEST)

        processed = []
        for payment_data in payments:
            item = AggregatedRequestItem.objects.filter(
                id=payment_data['item_id'],
                aggregated_request=aggregated_request
            ).select_related('customer', 'product', 'aggregated_request').first()

            if not item or not item.unit_price:
                continue

            amount = float(item.total_cost)
            heading = f"Aggregated request payment: {item.product.product_name} for {item.customer.name} ({item.customer.member_number})"
            record_date = request.data.get('record_date', timezone.now().date())
            payment_method = payment_data.get('payment_method', 'credit')

            # Determine the income chart from the product
            income_chart = item.product.income_chart
            if not income_chart:
                return Response(
                    {'error': f'Income chart not set for product {item.product.product_name}. Please register stock first.'},
                    status=status.HTTP_400_BAD_REQUEST
                )

            if payment_method == 'cash':
                cash_account_id = payment_data.get('cash_account_id')
                if not cash_account_id:
                    return Response({'error': f'Cash account required for cash payment on item {item.id}'}, status=status.HTTP_400_BAD_REQUEST)

                from ledgers.models import CashAccounts
                cash_account = CashAccounts.objects.filter(pk=cash_account_id).first()
                if not cash_account:
                    return Response({'error': 'Cash account not found'}, status=status.HTTP_400_BAD_REQUEST)

                # Dr. Cash Account  Cr. Income
                reference_no = generate_reference_no(income_chart.account_line, organisation_id, 'agr')
                txn = SystemTransactions.objects.create(
                    amount=amount,
                    heading=heading,
                    record_date=record_date,
                    payment_method='cash',
                    reference_no=reference_no,
                    debit_chart=cash_account.chart,
                    credit_chart=income_chart,
                    branch_id=branch_id,
                    added_by=request.user,
                )
                item.cash_account = cash_account

            else:  # credit / bank / mobile_money / flexipay — becomes a receivable on this member
                # Get or create debtor account for this customer
                debtor_account = DebtorAccounts.objects.filter(
                    customer=item.customer,
                    organisation=organisation,
                    deleted=False
                ).first()

                if not debtor_account:
                    from ledgers.ledgers_helper import generate_chart_of_account_code
                    account_code = generate_chart_of_account_code(receivable_parent.id, 'assets', organisation_id)
                    debtor_chart = OrganisationSubAccount.objects.create(
                        account_name=f'Receivable: {item.customer.name}',
                        account_line='assets',
                        account_organisation=organisation,
                        account_code=account_code,
                        parent_id=receivable_parent,
                        added_by=request.user.id,
                        allow_sub_accounts=False,
                    )
                    debtor_account = DebtorAccounts.objects.create(
                        account_name=item.customer.name,
                        telephone_number=getattr(item.customer, 'telephone', '') or '',
                        customer=item.customer,
                        organisation=organisation,
                        chart=debtor_chart,
                        added_by=request.user.id,
                    )

                # Dr. Debtor/Receivable chart  Cr. Income
                reference_no = generate_reference_no(income_chart.account_line, organisation_id, 'agr')
                txn = SystemTransactions.objects.create(
                    amount=amount,
                    heading=heading,
                    record_date=record_date,
                    payment_method='credit',
                    reference_no=reference_no,
                    debit_chart=debtor_account.chart,
                    credit_chart=income_chart,
                    branch_id=branch_id,
                    added_by=request.user,
                )

                # Record as DebtorSupply so it shows on the receivables ledger
                DebtorSupplies.objects.create(
                    debtor=debtor_account,
                    reference_transaction=txn,
                    maturity_date=request.data.get('maturity_date', timezone.now().date()),
                    added_by=request.user.id,
                )

            item.payment_method = payment_method
            item.payment_transaction = txn
            item.save(update_fields=['payment_method', 'cash_account', 'payment_transaction'])
            processed.append(item.id)

        return Response({'message': f'{len(processed)} payment(s) processed', 'processed_items': processed})

    @action(detail=True, methods=['post'])
    def fulfill(self, request, pk=None):
        aggregated_request = self.get_object()
        organisation_id = get_current_user(request, 'organisation_id', None)

        if aggregated_request.service_provider_id != organisation_id:
            return Response(
                {'error': 'Only the service provider can mark this as delivered'},
                status=status.HTTP_403_FORBIDDEN
            )

        aggregated_request.status = 'delivered'
        aggregated_request.save()
        return Response({'status': 'delivered'})



class ServiceProvidersAPIView(APIView):
    """Get service provider organisations (org type id=6) under the allowed parent orgs"""

    ALLOWED_PARENT_IDS = [141, 484, 166]

    def get(self, request, format=None):
        organisation_id = get_current_user(request, 'organisation_id', None)

        service_providers = Organisation.objects.filter(
            Q(organisation_type_id=6, admin_organisation_id__in=self.ALLOWED_PARENT_IDS) |
            Q(id__in=self.ALLOWED_PARENT_IDS)
        ).filter(
            product_organisation__deleted=False
        ).exclude(id=organisation_id).distinct()

        results = []
        for provider in service_providers:
            products = Product.objects.filter(organisation=provider, deleted=False).values(
                'id', 'product_name', 'description', 'purchase_price', 'sale_price',
                'purchase_unit_type', 'sale_unit_type'
            )
            results.append({
                'id': provider.id,
                'organisation_name': provider.name,
                'short_name': provider.short_name,
                'email': provider.email,
                'phone_number': provider.phone_number,
                'district': provider.district,
                'products': list(products),
                'product_count': products.count()
            })

        return Response({'count': len(results), 'results': results})


class ServiceProviderProductsAPIView(APIView):
    """Get products for a specific service provider"""
    
    def get(self, request, provider_id, format=None):
        products = Product.objects.filter(
            organisation_id=provider_id,
            deleted=False
        )
        
        branch_id = get_current_user(request, 'organisation_branch_id', None)
        serializer = ProductSerializer(products, many=True, context={'branch_id': branch_id})
        
        return Response({
            'count': products.count(),
            'results': serializer.data
        })
