from django.forms import ValidationError
from rest_framework import viewsets
from rest_framework.filters import SearchFilter, OrderingFilter
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.permissions import IsAuthenticated
from django.db.models import Sum, Q
from django.utils.timezone import make_aware
from rest_framework.decorators import action
import datetime as datetime_timedelta
from django.utils import timezone

from .serializers import *
from .models import *
from organisations.models import Organisation, OrganisationBranch
from customers.models import Customer
from questbanker_api.utils import get_current_user
from django.http import Http404
from .helper import *

def is_qdf_staff(request):
    """Check if user belongs to QDF digital fund organization"""
    try:
        user_org_id = get_current_user(request, 'organisation_id', None)
        # Try multiple variations of QDF organization name
        qdf_organisation = None
        for name_pattern in ['QDF digital fund', 'QDF Digital Fund', 'qdf digital fund', 'QDF']:
            try:
                qdf_organisation = Organisation.objects.get(name__icontains=name_pattern)
                break
            except Organisation.DoesNotExist:
                continue
        
        if qdf_organisation:
            return user_org_id == qdf_organisation.id
        return False
    except Exception:
        return False

class ExternalLoanProductViewSet(viewsets.ModelViewSet):
    """External loan products management"""
    serializer_class = ExternalLoanProductSerializer
    permission_classes = [IsAuthenticated]
    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
    filterset_fields = ['is_active']
    search_fields = ['product_name']
    ordering_fields = ['date_added', 'product_name']
    ordering = ['-date_added']

    def get_queryset(self):
        # Only show loan products from QDF digital fund
        try:
            # Try multiple variations of QDF organization name
            qdf_organisation = None
            for name_pattern in ['QDF digital fund', 'QDF Digital Fund', 'qdf digital fund', 'QDF']:
                try:
                    qdf_organisation = Organisation.objects.get(name__icontains=name_pattern)
                    break
                except Organisation.DoesNotExist:
                    continue
            
            if qdf_organisation:
                return ExternalLoanProduct.objects.filter(
                    organisation=qdf_organisation,
                    is_deleted=False
                )
        except Exception:
            pass
        
        # Fallback to user's organization if QDF not found
        user_org_id = get_current_user(self.request, 'organisation_id', None)
        return ExternalLoanProduct.objects.filter(
            organisation_id=user_org_id,
            is_deleted=False
        )

    def perform_create(self, serializer):
        # Set QDF digital fund as the organization for loan products
        try:
            qdf_organisation = Organisation.objects.get(name__icontains='QDF digital fund')
            serializer.save(
                organisation=qdf_organisation,
                added_by=self.request.user
            )
        except Organisation.DoesNotExist:
            # Fallback to user's organization if QDF not found
            user_org_id = get_current_user(self.request, 'organisation_id', None)
            serializer.save(
                organisation_id=user_org_id,
                added_by=self.request.user
            )

    def perform_update(self, serializer):
        serializer.save(last_updated=timezone.now())

class ExternalLoanSectorViewSet(viewsets.ModelViewSet):
    """External loan sectors management"""
    serializer_class = ExternalLoanSectorSerializer
    permission_classes = [IsAuthenticated]
    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
    filterset_fields = ['is_active']
    search_fields = ['name']
    ordering_fields = ['date_added', 'name']
    ordering = ['-date_added']

    def get_queryset(self):
        # Show loan sectors from QDF digital fund to all organizations
        try:
            qdf_organisation = Organisation.objects.get(name__icontains='QDF digital fund')
            return ExternalLoanSector.objects.filter(
                organisation=qdf_organisation
            )
        except Organisation.DoesNotExist:
            # Fallback to user's organization if QDF not found
            user_org_id = get_current_user(self.request, 'organisation_id', None)
            return ExternalLoanSector.objects.filter(
                organisation_id=user_org_id
            )

    def perform_create(self, serializer):
        # Set QDF digital fund as the organization for loan sectors
        try:
            qdf_organisation = Organisation.objects.get(name__icontains='QDF digital fund')
            serializer.save(
                organisation=qdf_organisation,
                added_by=self.request.user
            )
        except Organisation.DoesNotExist:
            # Fallback to user's organization if QDF not found
            user_org_id = get_current_user(self.request, 'organisation_id', None)
            serializer.save(
                organisation_id=user_org_id,
                added_by=self.request.user
            )

    def perform_update(self, serializer):
        serializer.save(last_updated=timezone.now())

class ListExternalLoanApplicationsViewSet(viewsets.ModelViewSet):
    """List external loan applications - QDF staff see all, applicant orgs see their own"""
    serializer_class = ExternalLoanApplicationSerializer
    filter_backends = [SearchFilter, OrderingFilter, DjangoFilterBackend]
    filterset_fields = ['organisation_branch', 'status', 'external_loan_product', 'applicant_organisation']
    search_fields = ['status']
    ordering_fields = ['status']
    lookup_field = 'id'
    
    def get_queryset(self):
        user_org_id = get_current_user(self.request, 'organisation_id', None)
        status_filter = self.request.query_params.get("status_filter", None)
        loan_officer_id = self.request.query_params.get('loan_officer_id', None)
        
        try:
            qdf_organisation = Organisation.objects.get(name__icontains='QDF digital fund')
            
            if is_qdf_staff(self.request):
                # QDF staff can see all applications where QDF is the loan provider
                base_filter = {'is_deleted': False, 'deleted': False, 'loan_provider_organisation': qdf_organisation}
            else:
                # Applicant organizations can see their own loan applications
                base_filter = {
                    'is_deleted': False,
                    'deleted': False,
                    'applicant_organisation_id': user_org_id,
                    'loan_provider_organisation': qdf_organisation
                }
        except Organisation.DoesNotExist:
            return ExternalLoanApplication.objects.none()

        if status_filter:
            status_filter = status_filter.split(',')
            return ExternalLoanApplication.objects.filter(status__in=status_filter, **base_filter).order_by('-id')

        if loan_officer_id:
            self.pagination_class = None
            return ExternalLoanApplication.objects.filter(loan_officer__id=loan_officer_id, **base_filter).order_by('-id')

        return ExternalLoanApplication.objects.filter(**base_filter).order_by('-id')

class ExternalLoanApplicationViewSet(viewsets.ModelViewSet):
    """External loan applications management"""
    serializer_class = ExternalLoanApplicationSerializer
    permission_classes = [IsAuthenticated]
    filter_backends = [SearchFilter, OrderingFilter, DjangoFilterBackend]
    filterset_fields = ['organisation_branch', 'status', 'external_loan_product', 'applicant_organisation']
    search_fields = ['status', 'applicant_organisation__name', 'loan_number']
    ordering_fields = ['status', 'date_added', 'loan_amount']
    ordering = ['-date_added']
    lookup_field = 'id'

    def get_queryset(self):
        user_org_id = get_current_user(self.request, 'organisation_id', None)
        
        try:
            qdf_organisation = Organisation.objects.get(name__icontains='QDF digital fund')
            
            if is_qdf_staff(self.request):
                # QDF staff can see all applications where QDF is the loan provider
                return ExternalLoanApplication.objects.filter(
                    loan_provider_organisation=qdf_organisation,
                    is_deleted=False,
                    deleted=False
                ).order_by('-id')
            else:
                # Applicant organizations can see their own loan applications
                return ExternalLoanApplication.objects.filter(
                    loan_provider_organisation=qdf_organisation,
                    applicant_organisation_id=user_org_id,
                    is_deleted=False,
                    deleted=False
                ).order_by('-id')
        except Organisation.DoesNotExist:
            return ExternalLoanApplication.objects.none()

    def get_serializer_class(self):
        if self.action == 'create':
            return ExternalLoanApplicationCreateSerializer
        return ExternalLoanApplicationSerializer

    def create(self, request, *args, **kwargs):
        # Remove loan_provider_organisation from request data if present
        if 'loan_provider_organisation' in request.data:
            request.data.pop('loan_provider_organisation')
        return super().create(request, *args, **kwargs)

    def perform_create(self, serializer):
        organisation_branch_id = get_current_user(self.request, 'organisation_branch_id', None)
        organisation_branch = OrganisationBranch.objects.get(pk=organisation_branch_id)
        user_org_id = get_current_user(self.request, 'organisation_id', None)
        applicant_organisation = Organisation.objects.get(pk=user_org_id)
        loan_number = generate_external_loan_number(user_org_id)
        
        # Always set QDF Digital Fund as the loan provider organization
        try:
            qdf_organisation = Organisation.objects.get(name__icontains='QDF digital fund')
        except Organisation.DoesNotExist:
            qdf_organisation = Organisation.objects.first()
        
        new_application = serializer.save(
            added_by=self.request.user,
            organisation_branch=organisation_branch,
            applicant_organisation=applicant_organisation,
            loan_provider_organisation=qdf_organisation,
            loan_number=loan_number
        )
        
        application = ExternalLoanApplication.objects.get(pk=new_application.id)
        application.save()

    def perform_update(self, serializer):
        serializer.save()

class ExternalLoanApplicationIncomeSourceViewSet(viewsets.ModelViewSet):
    """External loan application income sources"""
    serializer_class = ExternalLoanIncomeSourceSerializer
    queryset = ExternalLoanIncomeSource.objects.all()
    filter_backends = [SearchFilter, OrderingFilter, DjangoFilterBackend]
    filterset_fields = ['external_loan_application', 'id', 'source_name']
    search_fields = ['external_loan_application', 'source_name']
    ordering_fields = ['source_name']

    def perform_create(self, serializer):
        # Get external_loan_application from request data
        external_loan_application_id = self.request.data.get('external_loan_application')
        if external_loan_application_id:
            try:
                external_loan_application = ExternalLoanApplication.objects.get(pk=external_loan_application_id)
                serializer.save(added_by=self.request.user, external_loan_application=external_loan_application)
            except ExternalLoanApplication.DoesNotExist:
                raise ValidationError("External loan application not found")
        else:
            raise ValidationError("external_loan_application field is required")
    
    def perform_update(self, serializer):
        serializer.save()
    
    def destroy(self, request, *args, **kwargs):
        try:
            instance = self.get_object()
            if instance.id:
                instance.delete()
        except Http404:
            pass
        return Response(status=status.HTTP_204_NO_CONTENT)

class ExternalLoanApplicationSecurityViewSet(viewsets.ModelViewSet):
    """External loan application security/collateral"""
    serializer_class = ExternalLoanApplicationSecuritySerializer
    queryset = ExternalLoanApplicationSecurity.objects.all()
    filter_backends = [SearchFilter, OrderingFilter, DjangoFilterBackend]
    filterset_fields = ['external_loan_application', 'id', 'description']
    search_fields = ['external_loan_application', 'description']
    ordering_fields = ['external_loan_application', 'description']

    def perform_create(self, serializer):
        serializer.save(added_by=self.request.user)

    def perform_update(self, serializer):
        serializer.save()

    def destroy(self, request, *args, **kwargs):
        try:
            instance = self.get_object()
            if instance.id:
                instance.delete()
        except Http404:
            pass
        return Response(status=status.HTTP_204_NO_CONTENT)

class ExternalLoanApprovalViewSet(viewsets.ModelViewSet):
    """External loan approvals management - only QDF staff"""
    serializer_class = ExternalLoanApprovalSerializer
    filter_backends = [SearchFilter, OrderingFilter, DjangoFilterBackend]
    filterset_fields = ['external_loan_application', 'id']
    search_fields = ['external_loan_application']
    ordering_fields = ['external_loan_application']

    def get_queryset(self):
        # Only QDF staff can approve external loans
        if not is_qdf_staff(self.request):
            return ExternalLoanApproval.objects.none()
        return ExternalLoanApproval.objects.all()

    def get_serializer_class(self):
        if self.action == 'create':
            return ExternalLoanApprovalCreateSerializer
        return ExternalLoanApprovalSerializer

    def perform_create(self, serializer):
        # Only QDF staff can create approvals
        if not is_qdf_staff(self.request):
            raise ValidationError("Only QDF digital fund staff can approve external loans")
            
        loan_approval = serializer.save(approved_by=self.request.user)

        if loan_approval:
            loan_details = ExternalLoanApplication.objects.get(id=loan_approval.external_loan_application.id)
            
            # Set the loan officer to the staff record of the user who approved the loan
            loan_details.loan_officer = self.request.user.user_staff
            loan_details.status = 'approved'
            loan_details.save()
            
            # Generate and save loan schedules upon approval
            schedules = generate_external_loan_schedules_detailed(self.request, loan_details.id)
            if schedules:
                save_external_loan_schedules(loan_details.id, schedules, self.request.user)
    
    def perform_update(self, serializer):
        if not is_qdf_staff(self.request):
            raise ValidationError("Only QDF digital fund staff can modify external loan approvals")
        serializer.save()

class ExternalLoanGuarantorsViewSet(viewsets.ModelViewSet):
    """External loan guarantors management"""
    serializer_class = ExternalLoanGuarantorsSerializer
    queryset = ExternalLoanGuarantors.objects.all()
    filter_backends = [SearchFilter, OrderingFilter, DjangoFilterBackend]
    filterset_fields = ['external_loan_application', 'id']
    search_fields = ['external_loan_application']
    ordering_fields = ['external_loan_application']

    def perform_create(self, serializer):
        serializer.save(added_by=self.request.user)

    def perform_update(self, serializer):
        serializer.save()
    
    def destroy(self, request, *args, **kwargs):
        try:
            instance = self.get_object()
            if instance.id:
                instance.delete()
        except Http404:
            pass
        return Response(status=status.HTTP_204_NO_CONTENT)

class ExternalLoanDisbursementViewSet(viewsets.ModelViewSet):
    """External loan disbursements management - only QDF staff"""
    permission_classes = [IsAuthenticated]
    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
    filterset_fields = ['disbursement_method', 'approval_status', 'disbursed_by']
    search_fields = ['external_loan_application__applicant_organisation__name', 'payment_reference']
    ordering_fields = ['disbursement_date', 'disbursed_amount']
    ordering = ['-disbursement_date']

    def get_serializer_class(self):
        if self.action == 'create':
            return ExternalLoanDisbursementCreateSerializer
        return ExternalLoanDisbursementSerializer

    def get_queryset(self):
        # Only QDF staff can view/manage disbursements
        if not is_qdf_staff(self.request):
            return ExternalLoanDisbursement.objects.none()
            
        try:
            qdf_organisation = Organisation.objects.get(name__icontains='QDF digital fund')
            return ExternalLoanDisbursement.objects.filter(
                external_loan_application__loan_provider_organisation=qdf_organisation,
                deleted=False
            )
        except Organisation.DoesNotExist:
            return ExternalLoanDisbursement.objects.none()

    @action(detail=True, methods=['post'])
    def approve(self, request, pk=None):
        """Approve disbursement - only QDF staff"""
        if not is_qdf_staff(request):
            return Response({'error': 'Only QDF digital fund staff can approve disbursements'}, status=status.HTTP_403_FORBIDDEN)
            
        disbursement = self.get_object()
        disbursement.approval_status = 'approved'
        disbursement.save()
        
        # Update application status
        application = disbursement.external_loan_application
        application.status = 'disbursed'
        application.save()
        
        return Response({'message': 'External loan disbursement approved successfully'})

    @action(detail=True, methods=['post'])
    def reject(self, request, pk=None):
        """Reject disbursement - only QDF staff"""
        if not is_qdf_staff(request):
            return Response({'error': 'Only QDF digital fund staff can reject disbursements'}, status=status.HTTP_403_FORBIDDEN)
            
        disbursement = self.get_object()
        disbursement.approval_status = 'rejected'
        disbursement.save()
        
        return Response({'message': 'External loan disbursement rejected successfully'})

class ExternalLoanWriteOffViewSet(viewsets.ModelViewSet):
    """External loan write-offs management"""
    serializer_class = ExternalLoanWriteOffSerializer

    def get_queryset(self):
        filter_query = {"external_loan_application__status": 'written_off'}
        loan_id = self.request.GET.get('loan_id', None)
        organisation_branch_id = get_current_user(self.request, 'organisation_branch_id', None)
        if loan_id:
            filter_query['external_loan_application__id'] = loan_id
        if organisation_branch_id:
            filter_query['external_loan_application__organisation_branch__id'] = organisation_branch_id
        return ExternalLoanWriteOff.objects.filter(**filter_query)
    
    def perform_create(self, serializer):
        request_data = self.request.data
        external_loan_application = ExternalLoanApplication.objects.get(pk=request_data.get('external_loan_application'))
        
        if external_loan_application:
            external_loan_application.status = 'written_off'
            external_loan_application.save()
            
            serializer.save(added_by=self.request.user)

class ExternalLoanApplicationsViewView(viewsets.ModelViewSet):
    """External loan applications search view - replicates LoanApplicationsViewView"""
    serializer_class = ExternalLoanApplicationSearchSerializer
    queryset = ExternalLoanApplication.objects.filter(is_deleted=False, deleted=False)
    filter_backends = [SearchFilter, OrderingFilter, DjangoFilterBackend]
    filterset_fields = ['id']
    search_fields = ['applicant_organisation__name', 'loan_number']
    ordering_fields = ['id', 'applicant_organisation__name', 'loan_number']

    def get_queryset(self):
        user_org_id = get_current_user(self.request, 'organisation_id', None)
        
        # Check if user is QDF staff - they should see all loans where QDF is the provider
        try:
            qdf_organisation = Organisation.objects.get(name__icontains='QDF digital fund')
            if user_org_id == qdf_organisation.id:
                # QDF staff can see all applications where QDF is the loan provider
                return ExternalLoanApplication.objects.filter(
                    is_deleted=False,
                    deleted=False,
                    loan_provider_organisation=qdf_organisation
                ).order_by('-id')
            else:
                # Non-QDF staff see loans where their org is the applicant
                return ExternalLoanApplication.objects.filter(
                    is_deleted=False,
                    deleted=False,
                    organisation_branch__branch_organisation_id=user_org_id
                ).order_by('-id')
        except Organisation.DoesNotExist:
            # Fallback to original behavior if QDF not found
            return ExternalLoanApplication.objects.filter(
                is_deleted=False,
                deleted=False,
                organisation_branch__branch_organisation_id=user_org_id
            ).order_by('-id')

class OrganisationExternalLoansViewSet(viewsets.ModelViewSet):
    """Organisation external loans view - replicates CustomerLoansView but for organizations"""
    serializer_class = ExternalLoanApplicationSearchSerializer
    
    def get_queryset(self):
        organisation_id = self.request.GET.get('id', None)
        status = self.request.GET.get('status', None)

        if status:
            status = status.split(',')

        if not status:
            status = ['disbursed']

        return ExternalLoanApplication.objects.filter(
            applicant_organisation_id=organisation_id,
            is_deleted=False,
            deleted=False,
            status__in=status
        ).order_by('-id').distinct('id')

# Function-based views for specific endpoints

class ExternalLoanDisbursementActiveAccountsView(APIView):
    """Get active accounts for external loan disbursement - both source and destination"""
    
    def get(self, request, format=None):
        from ledgers.models import CashAccounts, BankAccounts, SafeAccounts
        
        branch_id = get_current_user(self.request, 'organisation_branch_id', None)
        organisation_id = get_current_user(self.request, 'organisation_id', None)
        loan_id = request.GET.get('loan_id', None)
        
        response = {
            'source_accounts': {
                'bank_accounts': []
            },
            'destination_account_info': {}
        }
        
        # Get source bank accounts (lending organization's accounts)
        bank_accounts = BankAccounts.objects.filter(branch=branch_id, status='active', deleted=False)
        for bank_account in bank_accounts:
            response['source_accounts']['bank_accounts'].append({
                "id": bank_account.chart.id,
                "account_name": bank_account.chart.account_name,
                "account_code": bank_account.chart.account_code,
                "account_number": bank_account.account_number,
                "bank_name": bank_account.bank_name,
                "account_id": bank_account.id,
                "account_type": "bank"
            })
        
        # Get destination account info if loan_id is provided
        if loan_id:
            try:
                loan_details = ExternalLoanApplication.objects.get(pk=loan_id)
                if loan_details.bank_account_details:
                    response['destination_account_info'] = {
                        'bank_name': loan_details.bank_account_details.get('bank_name', ''),
                        'account_name': loan_details.bank_account_details.get('account_name', ''),
                        'account_number': loan_details.bank_account_details.get('account_number', ''),
                        'account_currency': loan_details.bank_account_details.get('account_currency', 'UGX'),
                        'applicant_organisation': loan_details.applicant_organisation.name if loan_details.applicant_organisation else ''
                    }
            except ExternalLoanApplication.DoesNotExist:
                pass
        
        return Response(response, status=status.HTTP_200_OK)

class ExternalLoanDisbursementView(APIView):
    """External loan disbursement processing - only QDF staff"""

    def post(self, request, format=None):
        # Only QDF staff can disburse external loans
        if not is_qdf_staff(request):
            return Response({"message": "Only QDF digital fund staff can disburse external loans"}, status=status.HTTP_403_FORBIDDEN)
            
        from .helper import process_external_loan_disbursement_with_ledger
        
        # Extract disbursement data
        disbursement_data = {
            'loan_application_id': request.data.get('loan_application_id'),
            'disbursed_amount': float(request.data.get('disbursed_amount', 0)),
            'disbursement_method': 'bank',  # Always bank for external loans
            'source_account_id': request.data.get('source_account_id'),
            'payment_reference': request.data.get('payment_reference', ''),
            'voucher_no': request.data.get('voucher_no', ''),
            'disbursement_notes': request.data.get('disbursement_notes', ''),
            'disbursement_date': request.data.get('disbursement_date')
        }
        
        # Get loan application to extract destination details
        try:
            loan_application = ExternalLoanApplication.objects.get(pk=disbursement_data['loan_application_id'])
            bank_details = loan_application.bank_account_details or {}
            
            # Auto-populate destination details from loan application
            disbursement_data.update({
                'destination_bank_name': bank_details.get('bank_name', ''),
                'destination_account_name': bank_details.get('account_name', ''),
                'destination_account_number': bank_details.get('account_number', ''),
                'destination_account_details': bank_details
            })
        except ExternalLoanApplication.DoesNotExist:
            return Response({"message": "Loan application not found"}, status=status.HTTP_404_NOT_FOUND)
        
        # Validate required fields
        if not disbursement_data['loan_application_id']:
            return Response({"message": "Loan application ID is required"}, status=status.HTTP_400_BAD_REQUEST)
        
        if not disbursement_data['source_account_id']:
            return Response({"message": "Source bank account is required"}, status=status.HTTP_400_BAD_REQUEST)
        
        if disbursement_data['disbursed_amount'] <= 0:
            return Response({"message": "Disbursed amount must be greater than zero"}, status=status.HTTP_400_BAD_REQUEST)
        
        # Validate destination details exist in loan application
        if not disbursement_data['destination_account_name'] or not disbursement_data['destination_account_number']:
            return Response({"message": "Loan application must have complete bank account details"}, status=status.HTTP_400_BAD_REQUEST)
        
        # Process disbursement
        result = process_external_loan_disbursement_with_ledger(request, disbursement_data)
        
        if result['success']:
            return Response({
                "message": result['message'],
                "disbursement_id": result['disbursement'].id,
                "transaction_id": result['system_transaction'].id,
                "loan_status": "disbursed"
            }, status=status.HTTP_200_OK)
        else:
            return Response({
                "message": result['message'],
                "error": result['error']
            }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

class GenerateExternalLoanScheduleView(APIView):
    """Generate external loan repayment schedule - accessible by QDF staff and applicant organizations"""
    
    def get(self, request, format=None):
        response = []
        loan_id = request.GET.get('loan_id', None)
        user_org_id = get_current_user(request, 'organisation_id', None)
        
        if loan_id:
            try:
                loan_details = ExternalLoanApplication.objects.get(pk=loan_id)
                
                # Check access permissions
                if not is_qdf_staff(request) and loan_details.applicant_organisation_id != user_org_id:
                    return Response({'error': 'Access denied'}, status=status.HTTP_403_FORBIDDEN)
                
                if loan_details.status == 'disbursed' or loan_details.status == 'cleared_off' or loan_details.status == 'written_off':
                    # Get loan schedule from db with simplified format
                    loan_repayment_schedules = ExternalLoanRepaymentSchedule.objects.filter(
                        external_loan_application=loan_details, 
                        status='active'
                    ).order_by('payment_number')
                    
                    response = []
                    for schedule in loan_repayment_schedules:
                        response.append({
                            'expected_date': schedule.expected_date,
                            'starting_balance': schedule.starting_balance,
                            'principal_expected': schedule.principal_expected,
                            'interest_expected': schedule.interest_expected,
                            'total_payment': schedule.total_payment,
                            'ending_balance': schedule.ending_balance
                        })
                else:
                    # Generate preview schedules with simplified format
                    detailed_schedules = generate_external_loan_schedules_detailed(request, loan_id)
                    response = []
                    for schedule in detailed_schedules:
                        response.append({
                            'expected_date': schedule['expected_date'],
                            'starting_balance': schedule['starting_balance'],
                            'principal_expected': schedule['principal_expected'],
                            'interest_expected': schedule['interest_expected'],
                            'total_payment': schedule['total_payment'],
                            'ending_balance': schedule['ending_balance']
                        })
            except ExternalLoanApplication.DoesNotExist:
                return Response({'error': 'Loan not found'}, status=status.HTTP_404_NOT_FOUND)
                
        return Response({"count": len(response), "results": response})

    def post(self, request, format=None):
        data = self.request.data if self.request.data else None
        detailed_schedules = generate_external_loan_schedules_detailed(request, None, 'normal', data)
        response = []
        for schedule in detailed_schedules:
            response.append({
                'expected_date': schedule['expected_date'],
                'starting_balance': schedule['starting_balance'],
                'principal_expected': schedule['principal_expected'],
                'interest_expected': schedule['interest_expected'],
                'total_payment': schedule['total_payment'],
                'ending_balance': schedule['ending_balance']
            })
        return Response({"count": len(response), "results": response})

class ExternalLoanScheduleWithPaymentsView(APIView):
    """External loan schedule with payments - accessible by QDF staff and applicant organizations"""
    
    def get(self, request, format=None):
        response = []
        loan_id = request.GET.get('loan_id', None)
        user_org_id = get_current_user(request, 'organisation_id', None)
        
        if loan_id:
            try:
                loan = ExternalLoanApplication.objects.get(pk=loan_id)
                
                # Check access permissions
                if not is_qdf_staff(request) and loan.applicant_organisation_id != user_org_id:
                    return Response({'error': 'Access denied'}, status=status.HTTP_403_FORBIDDEN)
                
                # Get schedules
                schedules = ExternalLoanRepaymentSchedule.objects.filter(
                    external_loan_application=loan,
                    status='active'
                ).order_by('payment_number')
                
                schedule_data = []
                for schedule in schedules:
                    # Get payments for this schedule
                    payments = ExternalLoanPayments.objects.filter(
                        external_loan_application=loan,
                        external_loan_repayment_schedule=schedule,
                        payment_status='normal'
                    ).aggregate(
                        total_principal_paid=Sum('princ_paid'),
                        total_interest_paid=Sum('int_paid'),
                        total_penalty_paid=Sum('penalty_paid')
                    )
                    
                    principal_paid = payments['total_principal_paid'] or 0
                    interest_paid = payments['total_interest_paid'] or 0
                    penalty_paid = payments['total_penalty_paid'] or 0
                    
                    # Get penalties for this schedule
                    penalties = ExternalLoanPenalty.objects.filter(
                        external_loan_application=loan,
                        external_loan_repayment_schedule=schedule
                    ).aggregate(total=Sum('amount'))['total'] or 0
                    
                    schedule_data.append({
                        'id': schedule.id,
                        'payment_number': schedule.payment_number,
                        'principal_expected': schedule.principal_expected,
                        'interest_expected': schedule.interest_expected,
                        'total_payment': schedule.total_payment,
                        'starting_balance': schedule.starting_balance,
                        'ending_balance': schedule.ending_balance,
                        'expected_date': schedule.expected_date,
                        'principal_paid': principal_paid,
                        'interest_paid': interest_paid,
                        'penalty_paid': penalty_paid,
                        'penalties': penalties,
                        'principal_balance': max(0, schedule.principal_expected - principal_paid),
                        'interest_balance': max(0, schedule.interest_expected - interest_paid),
                        'penalty_balance': max(0, penalties - penalty_paid)
                    })
                
                response = schedule_data
                
                # Get overall loan balance
                loan_balance = get_external_loan_balance_detailed(loan_id)
                
                return Response({
                    "count": len(response), 
                    "results": response, 
                    "schedule_due": loan_balance, 
                    "next_schedule": {}, 
                    "reschedule": {}
                })
                
            except ExternalLoanApplication.DoesNotExist:
                return Response({'error': 'Loan not found'}, status=status.HTTP_404_NOT_FOUND)
                
        return Response({
            "count": len(response), 
            "results": response, 
            "schedule_due": {}, 
            "next_schedule": {}, 
            "reschedule": {}
        })

class ExternalLoanSchedulePaymentView(APIView):
    """External loan schedule payment processing"""
    
    def post(self, request, format=None):
        loan_id = self.request.data.get('loan_application_id', None)
        
        if loan_id:
            external_loan_application = ExternalLoanApplication.objects.get(pk=loan_id)
            if not external_loan_application:
                return Response({"message": "No external loan application found"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
            
            amount_paid = float(self.request.data.get('amount_paid', 0))
            principal_paid = float(self.request.data.get('principal_paid', 0))
            int_paid = float(self.request.data.get('int_paid', 0))
            penalty_paid = float(self.request.data.get('penalty_paid', 0))
            payment_method = self.request.data.get('payment_method')
            account = self.request.data.get('account')
            account_id = self.request.data.get('account_id')
            date_added = self.request.data.get('date_added')
            voucher_no = self.request.data.get('voucher_no')
            cheque = self.request.data.get('cheque')
            send_sms = self.request.data.get('send_sms')

            # Validation
            if not payment_method:
                return Response({"message": "No loan payment method provided"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
            if not date_added:
                return Response({"message": "No date provided"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
            
            # For bank payments, get the receiving bank account from the loan provider organization
            if payment_method == 'bank':
                from ledgers.models import BankAccounts
                # Get the loan provider's bank account (where payment will be received)
                lender_bank = BankAccounts.objects.filter(
                    chart__account_organisation=external_loan_application.loan_provider_organisation,
                    status='active'
                ).first()
                
                if lender_bank:
                    account = lender_bank.chart.id
                    account_id = lender_bank.id
                else:
                    return Response({"message": "No active bank account found for loan provider"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
            elif not account:
                return Response({"message": "No account provided"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

            if amount_paid == 0 and principal_paid == 0 and int_paid == 0 and penalty_paid == 0:
                return Response({"message": "Nothing being paid"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
            
            # Verify payment amounts against balances
            loan_balance = get_external_loan_balance_detailed(loan_id)
            
            if round(principal_paid, 2) > round(loan_balance['principal_balance'], 2):
                message = 'Principal Amount: Field has an amount that exceeds the principal balance of the loan'
                return Response({"message": message}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

            if round(int_paid, 2) > round(loan_balance['interest_balance'], 2):
                message = 'Interest Amount: Field has an amount that exceeds the interest balance of the loan'
                return Response({"message": message}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

            if round(penalty_paid, 2) > round(loan_balance['penalty_balance'], 2):
                message = 'Accumulated Penalty: Field has an amount that exceeds the penalty balance of the loan'
                return Response({"message": message}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

            payment_details = {
                "amount_paid": amount_paid, 
                "principal_paid": principal_paid, 
                "int_paid": int_paid, 
                "penalty_paid": penalty_paid,
                "payment_method": payment_method, 
                "account": account, 
                "date_added": date_added, 
                "voucher_no": voucher_no, 
                "cheque": cheque,
                "loan_id": loan_id, 
                "account_id": account_id
            }
            
            payment = process_external_loan_payment_detailed(payment_details, request)

            if payment:
                # Auto clear off loans if balance is zero
                updated_balance = get_external_loan_balance_detailed(loan_id)
                if updated_balance['total_balance'] < 1:
                    external_loan_application.status = 'cleared_off'
                    external_loan_application.save()
                
                return Response({"message": "External loan payment made successfully", "results": self.request.data}, status=status.HTTP_200_OK)

            return Response({"message": "Error while making payment"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
        
        return Response({"message": "Failed: No loan application"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

class ExternalLoanRecoveryView(APIView):
    """External loan recovery processing"""
    
    def post(self, request, format=None):
        loan_id = self.request.data.get('loan_application_id', None)
        
        if loan_id:
            external_loan_application = ExternalLoanApplication.objects.get(pk=loan_id)
            if not external_loan_application:
                return Response({"message": "No external loan application found"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
            
            # Process recovery logic here
            return Response({"message": "External loan recovery payment made successfully"}, status=status.HTTP_200_OK)
        
        return Response({"message": "Failed: No external loan application"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

class ExternalLoanWriteOffListView(APIView):
    """List external loans eligible for write-off"""
    
    def get(self, request):
        organisation_id = get_current_user(self.request, 'organisation_id', None)
        branch_id = get_current_user(self.request, 'organisation_branch_id', None)
        search = self.request.GET.get("search", None)
        product_id = self.request.query_params.get("product_id", None)
        officer_id = self.request.query_params.get("officer_id", None)
        
        loans = []  # Logic to get loans eligible for write-off
        return Response({"count": len(loans), "results": loans})

class RescheduleExternalLoansView(APIView):
    """Reschedule external loans"""
    
    def post(self, request, format=None):
        loan_details = request.data
        if not loan_details:
            return Response({"message": "rescheduling data missing"})
        
        # Rescheduling logic here
        return Response({"message": "External loan rescheduled successfully"})

class ExternalLoansTopUpView(APIView):
    """External loans top-up"""
    
    def post(self, request, format=None):
        if not request.data:
            return Response({"message": "topup data missing"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
        
        # Top-up logic here
        return Response({"message": "External loan topup successfully"})

class ExternalLoansLedgerView(APIView):
    """External loans ledger view"""
    
    def get(self, request, format=None):
        loan_id = request.GET.get('id', None)
        response = {}
        
        if loan_id:
            # Ledger logic here
            pass
            
        return Response(response)

# Additional views to match loans module functionality
class ExternalLoanTransferViewSet(viewsets.ModelViewSet):
    """External loan transfers management"""
    serializer_class = ExternalLoanTransferSerializer
    queryset = ExternalLoanTransfer.objects.all()

class ExternalLoanSettingsViewSet(viewsets.ModelViewSet):
    """External loan settings management"""
    serializer_class = ExternalLoanSettingsSerializer
    permission_classes = [IsAuthenticated]
    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
    filterset_fields = ['organisation', 'is_active']
    search_fields = ['organisation__name']
    ordering = ['-created_at']

    def get_queryset(self):
        # Only QDF staff can manage settings, or show settings for user's org
        user_org_id = get_current_user(self.request, 'organisation_id', None)
        
        if is_qdf_staff(self.request):
            # QDF staff can see all settings
            return ExternalLoanSettings.objects.all()
        else:
            # Other orgs can only see their own settings
            return ExternalLoanSettings.objects.filter(organisation_id=user_org_id)

    def perform_create(self, serializer):
        # Only QDF staff can create settings
        if not is_qdf_staff(self.request):
            raise ValidationError("Only QDF digital fund staff can create external loan settings")
        
        serializer.save(created_by=self.request.user, updated_by=self.request.user)

    def perform_update(self, serializer):
        # Only QDF staff can update settings
        if not is_qdf_staff(self.request):
            raise ValidationError("Only QDF digital fund staff can update external loan settings")
        
        serializer.save(updated_by=self.request.user)

    @action(detail=False, methods=['post'])
    def create_for_qdf(self, request):
        """Create external loan settings for QDF Digital Fund organization"""
        if not is_qdf_staff(request):
            return Response({'error': 'Only QDF digital fund staff can create settings'}, status=status.HTTP_403_FORBIDDEN)
        
        try:
            # Find QDF organization
            qdf_org = None
            for name_pattern in ['QDF digital fund', 'QDF Digital Fund', 'qdf digital fund', 'QDF']:
                try:
                    qdf_org = Organisation.objects.get(name__icontains=name_pattern)
                    break
                except Organisation.DoesNotExist:
                    continue
            
            if not qdf_org:
                return Response({'error': 'QDF Digital Fund organization not found'}, status=status.HTTP_404_NOT_FOUND)
            
            # Check if settings already exist
            existing_settings = ExternalLoanSettings.objects.filter(organisation=qdf_org).first()
            if existing_settings:
                return Response({
                    'message': 'External loan settings already exist for QDF Digital Fund',
                    'settings': ExternalLoanSettingsSerializer(existing_settings).data
                }, status=status.HTTP_200_OK)
            
            # Find or create external loan chart account
            from ledgers.models import OrganisationSubAccount
            
            external_loan_chart = OrganisationSubAccount.objects.filter(
                account_organisation=qdf_org,
                account_line='assets',
                account_name__icontains='external'
            ).first()
            
            if not external_loan_chart:
                # Create external loan chart account
                external_loan_chart = OrganisationSubAccount.objects.create(
                    account_name="External Loans",
                    account_code="EXT_LOANS",
                    account_line='assets',
                    account_organisation=qdf_org,
                    description='External loans provided to other organizations',
                    account_type='system',
                    added_by=request.user.id
                )
            
            # Create external loan settings
            settings = ExternalLoanSettings.objects.create(
                organisation=qdf_org,
                external_loan_chart=external_loan_chart,
                default_payment_method='bank',
                is_active=True,
                created_by=request.user
            )
            
            return Response({
                'message': 'External loan settings created successfully for QDF Digital Fund',
                'settings': ExternalLoanSettingsSerializer(settings).data
            }, status=status.HTTP_201_CREATED)
            
        except Exception as e:
            return Response({
                'error': f'Failed to create external loan settings: {str(e)}'
            }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

    @action(detail=False, methods=['get'])
    def check_qdf_settings(self, request):
        """Check if QDF Digital Fund has external loan settings"""
        try:
            # Find QDF organization
            qdf_org = None
            for name_pattern in ['QDF digital fund', 'QDF Digital Fund', 'qdf digital fund', 'QDF']:
                try:
                    qdf_org = Organisation.objects.get(name__icontains=name_pattern)
                    break
                except Organisation.DoesNotExist:
                    continue
            
            if not qdf_org:
                return Response({
                    'exists': False,
                    'error': 'QDF Digital Fund organization not found'
                }, status=status.HTTP_404_NOT_FOUND)
            
            # Check if settings exist
            settings = ExternalLoanSettings.objects.filter(organisation=qdf_org).first()
            
            if settings:
                return Response({
                    'exists': True,
                    'organisation_name': qdf_org.name,
                    'organisation_id': qdf_org.id,
                    'settings': ExternalLoanSettingsSerializer(settings).data
                }, status=status.HTTP_200_OK)
            else:
                return Response({
                    'exists': False,
                    'organisation_name': qdf_org.name,
                    'organisation_id': qdf_org.id,
                    'message': 'External loan settings not found for QDF Digital Fund'
                }, status=status.HTTP_200_OK)
                
        except Exception as e:
            return Response({
                'exists': False,
                'error': str(e)
            }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

class ExternalLoanGuarantorsSecurityViewSet(viewsets.ModelViewSet):
    """External loan guarantors security management"""
    serializer_class = ExternalLoanGuarantorsSecuritySerializer
    queryset = ExternalLoanGuarantorsSecurity.objects.all()

class ExternalLoanMainTransactionsViewSet(viewsets.ModelViewSet):
    """External loan main transactions management"""
    serializer_class = ExternalLoanMainTransactionsSerializer
    queryset = ExternalLoanMainTransactions.objects.all()

class ExternalLoanRecoveryViewSet(viewsets.ModelViewSet):
    """External loan recovery management"""
    serializer_class = ExternalLoanRecoverySerializer
    queryset = ExternalLoanRecovery.objects.all()

# Additional views to match loans module functionality

class ExternalLoanProductChargesViewSet(viewsets.ModelViewSet):
    """External loan product charges management"""
    serializer_class = ExternalLoanProductChargesSerializer
    queryset = ExternalLoanProductCharges.objects.filter(is_deleted=False)

    def perform_create(self, serializer):
        serializer.save(added_by=self.request.user)
    
    def perform_update(self, serializer):
        serializer.save(last_updated=timezone.now())

class ExternalLoanRepaymentScheduleViewSet(viewsets.ModelViewSet):
    """External loan repayment schedules management"""
    serializer_class = ExternalLoanRepaymentScheduleSerializer
    queryset = ExternalLoanRepaymentSchedule.objects.all()
    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
    filterset_fields = ['external_loan_application', 'status']
    ordering = ['payment_number']

    def perform_create(self, serializer):
        serializer.save(added_by=self.request.user)

class ExternalLoanPaymentTransactionViewSet(viewsets.ModelViewSet):
    """External loan payment transactions management"""
    serializer_class = ExternalLoanPaymentTransactionSerializer
    queryset = ExternalLoanPaymentTransaction.objects.all()
    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
    filterset_fields = ['external_loan_application', 'transaction_status']
    ordering = ['-date_added']

    def perform_create(self, serializer):
        serializer.save(added_by=self.request.user)

class ExternalLoanPaymentsViewSet(viewsets.ModelViewSet):
    """External loan payments management"""
    serializer_class = ExternalLoanPaymentsSerializer
    queryset = ExternalLoanPayments.objects.all()
    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
    filterset_fields = ['external_loan_application', 'payment_status']
    ordering = ['-date_added']

    def perform_create(self, serializer):
        serializer.save(added_by=self.request.user)

class ExternalLoanPenaltyViewSet(viewsets.ModelViewSet):
    """External loan penalties management"""
    serializer_class = ExternalLoanPenaltySerializer
    queryset = ExternalLoanPenalty.objects.all()
    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
    filterset_fields = ['external_loan_application', 'status']
    ordering = ['-date_added']

    def perform_create(self, serializer):
        serializer.save(added_by=self.request.user)

class ExternalLoanPenaltyWaiveredViewSet(viewsets.ModelViewSet):
    """External loan penalty waivers management"""
    serializer_class = ExternalLoanPenaltyWaiveredSerializer
    queryset = ExternalLoanPenaltyWaivered.objects.all()
    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
    filterset_fields = ['external_loan_application']
    ordering = ['-date_added']

    def perform_create(self, serializer):
        serializer.save(added_by=self.request.user)

class ExternalLoanInterestWaiveredViewSet(viewsets.ModelViewSet):
    """External loan interest waivers management"""
    serializer_class = ExternalLoanInterestWaiveredSerializer
    queryset = ExternalLoanInterestWaivered.objects.all()
    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
    filterset_fields = ['external_loan_application']
    ordering = ['-date_added']

    def perform_create(self, serializer):
        serializer.save(added_by=self.request.user)

class ExternalRescheduledLoansViewSet(viewsets.ModelViewSet):
    """External rescheduled loans management"""
    serializer_class = ExternalRescheduledLoansSerializer
    queryset = ExternalRescheduledLoans.objects.all()
    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
    filterset_fields = ['external_loan_application', 'status']
    ordering = ['-date_added']

    def perform_create(self, serializer):
        organisation_branch_id = get_current_user(self.request, 'organisation_branch_id', None)
        serializer.save(added_by=self.request.user, organisation_branch_id=organisation_branch_id)

class ExternalLoanProviderViewSet(viewsets.ModelViewSet):
    """External loan provider organizations management"""
    serializer_class = ExternalLoanSettingsSerializer  # We'll need to create a proper serializer
    permission_classes = [IsAuthenticated]
    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
    search_fields = ['name']
    ordering_fields = ['name']
    ordering = ['name']

    def get_queryset(self):
        # Return organizations that can provide external loans
        return Organisation.objects.filter(is_active=True)

    @action(detail=True, methods=['post'])
    def set_as_loan_provider(self, request, pk=None):
        """Set an organization as a loan provider"""
        organisation = self.get_object()
        # Logic to set organization as loan provider
        return Response({'message': f'{organisation.name} set as loan provider successfully'})

class ExternalLoanTopUpViewSet(viewsets.ModelViewSet):
    """External loan top-ups management"""
    serializer_class = ExternalLoanTopUpSerializer
    queryset = ExternalLoanTopUp.objects.all()
    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
    filterset_fields = ['external_loan_application']
    ordering = ['-date_added']

    def perform_create(self, serializer):
        organisation_branch_id = get_current_user(self.request, 'organisation_branch_id', None)
        organisation_branch = OrganisationBranch.objects.get(pk=organisation_branch_id)
        serializer.save(added_by=self.request.user, organisation_branch=organisation_branch)

class ExternalLoanApplicationMemberViewSet(viewsets.ModelViewSet):
    """External loan application members management with CSV upload"""
    serializer_class = ExternalLoanApplicationMemberSerializer
    permission_classes = [IsAuthenticated]
    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
    filterset_fields = ['external_loan_application', 'member_number', 'is_active']
    search_fields = ['member_name', 'member_number']
    ordering = ['-date_added']

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

    def perform_create(self, serializer):
        # Try to match customer by member_number
        member_number = serializer.validated_data.get('member_number')
        customer = None
        if member_number:
            try:
                customer = Customer.objects.get(member_number=member_number)
            except Customer.DoesNotExist:
                pass
        serializer.save(added_by=self.request.user, customer=customer)

    @action(detail=False, methods=['post'])
    def upload_csv(self, request):
        """Upload members list via CSV file"""
        import csv
        import io
        
        loan_application_id = request.data.get('external_loan_application')
        csv_file = request.FILES.get('csv_file')
        
        if not loan_application_id or not csv_file:
            return Response({'message': 'external_loan_application and csv_file are required'}, status=status.HTTP_400_BAD_REQUEST)
        
        try:
            loan_application = ExternalLoanApplication.objects.get(pk=loan_application_id)
            
            # Read CSV file
            decoded_file = csv_file.read().decode('utf-8')
            csv_data = csv.DictReader(io.StringIO(decoded_file))
            
            created_members = []
            errors = []
            
            for row_num, row in enumerate(csv_data, start=2):
                try:
                    # Extract required fields
                    member_name = row.get('member_name', '').strip()
                    member_number = row.get('member_number', '').strip()
                    loan_amount = float(row.get('loan_amount', 0))
                    loan_period = int(row.get('loan_period', 0))
                    
                    if not member_name or not member_number:
                        errors.append(f"Row {row_num}: member_name and member_number are required")
                        continue
                    
                    # Try to match existing customer
                    customer = None
                    try:
                        customer = Customer.objects.get(member_number=member_number)
                    except Customer.DoesNotExist:
                        pass
                    
                    # Create member record
                    member = ExternalLoanApplicationMember.objects.create(
                        external_loan_application=loan_application,
                        member_name=member_name,
                        member_number=member_number,
                        loan_amount=loan_amount,
                        loan_period=loan_period,
                        customer=customer,
                        added_by=request.user
                    )
                    created_members.append(member)
                    
                except ValueError as e:
                    errors.append(f"Row {row_num}: Invalid number format - {str(e)}")
                except Exception as e:
                    errors.append(f"Row {row_num}: {str(e)}")
            
            if errors:
                return Response({
                    'message': f'CSV processed with errors. {len(created_members)} members created.',
                    'errors': errors,
                    'created_count': len(created_members)
                }, status=status.HTTP_207_MULTI_STATUS)
            
            response_data = ExternalLoanApplicationMemberSerializer(created_members, many=True).data
            return Response({
                'message': f'{len(created_members)} members uploaded successfully',
                'data': response_data
            }, status=status.HTTP_201_CREATED)
            
        except ExternalLoanApplication.DoesNotExist:
            return Response({'message': 'Loan application not found'}, status=status.HTTP_404_NOT_FOUND)
        except Exception as e:
            return Response({'message': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

class DeleteExternalLoanView(APIView):
    """Delete external loan application"""
    permission_classes = [IsAuthenticated]
    
    def post(self, request, format=None):
        loan_id = request.data.get('id')
        delete_type = request.data.get('delete_type', 'soft')
        reason = request.data.get('reason', '')
        
        if not loan_id:
            return Response({'error': 'Loan ID is required'}, status=status.HTTP_400_BAD_REQUEST)
        
        try:
            success = delete_external_loan_by_id(loan_id, delete_type, reason, user_id=request.user.id, request_user=request.user)
            
            if success:
                message = f"External loan {'permanently deleted' if delete_type == 'permanent' else 'marked as deleted'} successfully"
                return Response({'message': message}, status=status.HTTP_200_OK)
            else:
                return Response({'error': 'Failed to delete external loan'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
                
        except Exception as e:
            return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

class UserPermissionsDebugView(APIView):
    """Debug endpoint to check user permissions"""
    permission_classes = [IsAuthenticated]
    
    def get(self, request):
        from users.models import UserAssignedRole
        from organisations.models import RoleComponent, SystemComponent
        
        response = {
            'user': request.user.username,
            'user_org_id': get_current_user(request, 'organisation_id', None),
            'is_qdf_staff': is_qdf_staff(request),
            'can_approve_loans': is_qdf_staff(request),  # Simple check for frontend
            'can_disburse_loans': is_qdf_staff(request),  # Simple check for frontend
            'user_roles': [],
            'qdf_org_lookup': None
        }
        
        # Check QDF organization lookup
        try:
            for name_pattern in ['QDF digital fund', 'QDF Digital Fund', 'qdf digital fund', 'QDF']:
                try:
                    qdf_org = Organisation.objects.get(name__icontains=name_pattern)
                    response['qdf_org_lookup'] = {
                        'pattern': name_pattern,
                        'org_id': qdf_org.id,
                        'org_name': qdf_org.name
                    }
                    break
                except Organisation.DoesNotExist:
                    continue
        except Exception as e:
            response['qdf_org_error'] = str(e)
        
        try:
            # Get user roles
            user_roles = UserAssignedRole.objects.filter(
                user=request.user,
                is_active=True
            )
            
            for user_role in user_roles:
                role_info = {
                    'role_name': user_role.assigned_role.role_name,
                    'role_desc': user_role.assigned_role.role_desc,
                    'is_active': user_role.is_active,
                    'components': []
                }
                
                # Get role components
                role_components = RoleComponent.objects.filter(
                    user_role=user_role.assigned_role,
                    is_active=True
                )
                
                for role_component in role_components:
                    component = role_component.org_component.system_component
                    role_info['components'].append({
                        'component': component.component,
                        'key': component.key,
                        'type': component.type,
                        'is_active': role_component.is_active
                    })
                
                response['user_roles'].append(role_info)
                
        except Exception as e:
            response['error'] = str(e)
            
        return Response(response)


class EditExternalLoanDisbursementView(APIView):
    permission_classes = [IsAuthenticated]

    def post(self, request, format=None):
        loan_application_id = request.data.get('external_loan_application_id')
        loan_amount = request.data.get('loan_amount')
        loan_start_date = request.data.get('loan_start_date')
        loan_disbursement_date = request.data.get('loan_disbursement_date')
        external_loan_product = request.data.get('external_loan_product')
        external_loan_sector = request.data.get('external_loan_sector')
        loan_period = request.data.get('loan_period')
        period_type = request.data.get('period_type')
        frequency = request.data.get('frequency')
        grace_period_type = request.data.get('grace_period_type')
        loan_officer = request.data.get('loan_officer')
        int_rate = request.data.get('int_rate')
        app_grace_period = request.data.get('app_grace_period')
        int_method = request.data.get('int_method')
        action = request.data.get('action')

        try:
            loan_application = ExternalLoanApplication.objects.get(pk=loan_application_id)
        except ExternalLoanApplication.DoesNotExist:
            return Response({'message': 'No loan application found'}, status=status.HTTP_404_NOT_FOUND)

        disbursement = ExternalLoanDisbursement.objects.filter(
            external_loan_application=loan_application
        ).first()
        if not disbursement:
            return Response({'message': 'Loan not disbursed'}, status=status.HTTP_400_BAD_REQUEST)

        now = timezone.now()
        user_id = request.user.id
        df = dict(deleted=True, deleted_by_id=user_id, deleted_at=now)

        # Delete existing payment transactions and schedules
        ExternalLoanPaymentTransaction.objects.filter(
            external_loan_application=loan_application
        ).update(**df)

        ExternalLoanMainTransactions.objects.filter(
            external_loan_application=loan_application
        ).exclude(transaction_type='LoanDisbursement').update(**df)

        ExternalLoanRepaymentSchedule.objects.filter(
            external_loan_application=loan_application
        ).update(**{**df, 'status': 'deleted'})

        # Update loan application fields
        loan_application.loan_amount = float(loan_amount)
        loan_application.int_rate = float(int_rate)
        loan_application.int_method = int_method
        loan_application.loan_period = int(loan_period)
        loan_application.period_type = period_type
        loan_application.external_loan_product_id = int(external_loan_product)
        loan_application.external_loan_sector_id = int(external_loan_sector)
        if loan_officer:
            loan_application.loan_officer_id = int(loan_officer)
        loan_application.save()

        # Update disbursement dates
        disbursement.disbursed_amount = float(loan_amount)
        if loan_disbursement_date:
            disbursement.disbursement_date = loan_disbursement_date
        disbursement.save()

        # Regenerate loan schedule
        generate_external_loan_schedules(request, loan_application.id)

        return Response({'message': 'External loan disbursement updated successfully'}, status=status.HTTP_200_OK)
