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 questbanker_api.utils import get_current_user
from django.db.models import Q
from exservices.exservices_helper import send_customer_sms
from rest_framework.parsers import MultiPartParser
from datetime import datetime
from django.utils.timezone import make_aware
from savings.savings_bal_helper import get_account_balance
from rest_framework.permissions import AllowAny
import threading
import os
import csv
import io
import threading
from .serializers import *
from .models import *
from ledgers.models import *
from ledgers.ledgers_helper import *
from .helper import *
from .shares_balance_helper import get_client_shares_balance
from reports.reports_helper import generate_sacco_shares_files
from savings.models import SavingAccountTransactions, SavingAccount
import pandas as pd
from django.conf import settings
import json
import ast
from rest_framework.pagination import PageNumberPagination

from .helper import get_client_shares_holders
from django.shortcuts import get_object_or_404


class SharesSettingsView(viewsets.ModelViewSet):
    serializer_class = SharesSettingsSerializer

    def get_queryset(self):
        organisation_id = get_current_user(self.request, 'organisation_id',None) 
        if organisation_id:
            return SharesSettings.objects.filter(organisation__id=organisation_id)
    
    def perform_create(self, serializer):
        organisation_id = get_current_user(self.request, 'organisation_id', None)
        organisation = Organisation.objects.get(pk=organisation_id)

        serializer.save(share_setting_added_by=self.request.user, organisation=organisation)

    def update(self, request, *args, **kwargs):
        organisation_id = get_current_user(request, 'organisation_id', None)
        instance = get_object_or_404(SharesSettings, pk=kwargs.get('pk'), organisation__id=organisation_id)

        serializer = self.get_serializer(instance, data=request.data, partial=True)
        serializer.is_valid(raise_exception=True)
        self.perform_update(serializer)

        return Response(serializer.data, status=status.HTTP_200_OK)

    def perform_update(self, serializer):
        serializer.save(share_setting_updated_by=self.request.user)


class ManageSharesView(APIView):
    
    def allowed_file(self, filename):
        return '.' in filename.name and \
            filename.name.split('.')[1].lower() in ["csv"]
    
    def post(self, request, format=None):
        process_type    = request.GET.get('process_type', None)
        organisation_id = get_current_user(self.request, 'organisation_id', None)
        branch_id = get_current_user(self.request, 'organisation_branch_id', None)
        organisation = Organisation.objects.get(pk=organisation_id)
        
        source_trans         = None
        inter_branch_trans   = None
        inter_branch_trans_2 = None
        
        if not process_type:
            return Response({"message": "missing process type"})

        if process_type == 'purchase':

            # process shares purchase
            customer_id = self.request.data.get('customer_id')
            amount = self.request.data.get('amount')
            payment_method =  self.request.data.get('payment_method')
            selected_account = self.request.data.get('account')
            send_sms = self.request.data.get('send_sms')
            transaction_date = self.request.data.get('transaction_date')
            number_of_shares = self.request.data.get('number_of_shares')
            current_share_value = self.request.data.get('current_share_value')
            account_id = self.request.data.get('account_id')
            save_trans = None
            
            # InterBranch chart
            customer = Customer.objects.get(pk=customer_id)
            interbranch_chart = get_inter_branch_chart(OrganisationBranch.objects.get(pk=branch_id), customer.customer_branch)
            
            # Begin validation
            # validate savings account
            if payment_method == 'offset':
                savings_account = SavingAccount.objects.filter(id=account_id).first()
                if not savings_account:
                    return Response({"message":"No saving account found"}, status=status.HTTP_200_OK)
            
                if savings_account:
                        account_balance   = get_account_balance(account_id)
                        if account_balance['balance_raw'] < float(amount):
                            return Response({"message":"Insufficient saving account balance"}, status=status.HTTP_200_OK)
                    
            # validate debiting chart of account
            debit_chart = OrganisationSubAccount.objects.get(pk=selected_account)
            if not debit_chart:
                return Response({"message":"No chart of account found"}, status=status.HTTP_200_OK)

            if not customer:
                return Response({"message":"No customer found"}, status=status.HTTP_200_OK)
            
            # end of validation get_client_shares_bal
            heading = 'Share purchase by '+ customer.name+'  ('+ customer.member_number +')'
            share_holder = ShareHolders.objects.filter(customer=customer).first()
            if not share_holder:
                share_holder = ShareHolders.objects.create(customer=customer, share_holder_added_by=request.user)
            
            branch_id = get_current_user(request, 'organisation_branch_id', None)
            organisation_branch = OrganisationBranch.objects.get(pk=branch_id)

            # credit manual member shares chart of account
            credit_chart_code = "sys-311"
            credit_chart = OrganisationSubAccount.objects.filter(account_code=credit_chart_code, account_organisation=organisation).first()

            # Generate reference number
            reference_no = generate_reference_no(debit_chart.account_line, organisation_branch.branch_organisation.id, 'p-sh')
            
            # make the transaction to update the ledger
            if str(branch_id) != str(customer.customer_branch.id) and payment_method != 'offset':

                # Handle inter-branch transactions  -> soure branch 
                heading = 'Inter-branch share purchase by '+ customer.name+' ('+ customer.member_number +')'
                source_trans = SystemTransactions.objects.create(amount=amount, heading=heading, reference_no=reference_no, payment_method=payment_method, voucher_no='', debit_chart=debit_chart, credit_chart=interbranch_chart, branch_id=branch_id, added_by=request.user, record_date=transaction_date)

                # Handle inter-branch transactions  -> destination branch 
                inter_branch_trans = SystemTransactions.objects.create(amount=amount, heading=heading, reference_no=reference_no, payment_method='settlement', voucher_no='', debit_chart=interbranch_chart, credit_chart=credit_chart, branch_id=customer.customer_branch.id, added_by=request.user, record_date=transaction_date)
                
                # Reconcile inter-branch transactions
                if inter_branch_trans:
                    inter_branch_trans_field = {
                        "source_transaction":source_trans,
                        "destination_transaction":inter_branch_trans,
                        "added_by":request.user,
                    }
                    InterBranchTransactions.objects.create(**inter_branch_trans_field)

                share_transaction = { "shareholder":share_holder, "share_holder_trans_added_by":request.user,
                        "no_of_shares":number_of_shares, "current_share_value":current_share_value,
                        "transaction_type":"purchase", "organisation_branch":customer.customer_branch, "system_transaction":inter_branch_trans}
                
                save_trans = SharesTransaction.objects.create(**share_transaction)
                
                # Create transaction receipt for shares purchase
                try:
                    from vas.utils import create_shares_receipt
                    create_shares_receipt(
                        transaction_type='share',
                        shareholder=share_holder,
                        reference_number=reference_no,
                        generated_by=request.user
                    )
                except Exception as e:
                    print(f"Error creating shares receipt: {e}")

            else:
                # Handle single branch transactions 
                source_trans = SystemTransactions.objects.create(amount=amount, heading=heading, reference_no=reference_no, payment_method=payment_method, voucher_no='', debit_chart=debit_chart, credit_chart=credit_chart, branch_id=branch_id, added_by=request.user, record_date=transaction_date)

                share_transaction = { "shareholder":share_holder, "share_holder_trans_added_by":request.user,
                        "no_of_shares":number_of_shares, "current_share_value":current_share_value,
                        "transaction_type":"purchase", "organisation_branch":organisation_branch, "system_transaction":source_trans}
                
                save_trans = SharesTransaction.objects.create(**share_transaction)
                
                # Create transaction receipt for shares purchase
                try:
                    from vas.utils import create_shares_receipt
                    create_shares_receipt(
                        transaction_type='share',
                        shareholder=share_holder,
                        reference_number=reference_no,
                        generated_by=request.user
                    )
                except Exception as e:
                    print(f"Error creating shares receipt: {e}")
            
            # save saving account if offset
            if payment_method == 'offset' and source_trans:
                saved_transaction_fields = {
                            "transaction_type":'withdrawal',
                            "customer_account_id":account_id,
                            "transaction_id":source_trans.id
                        }
                SavingAccountTransactions.objects.create(**saved_transaction_fields)

            if send_sms:
                data = {"sms_key":"purchase_share_sms","customer":customer,"user":self.request.user,"branch_id":branch_id,"save_trans":save_trans}
                send_customer_sms(data)

            return Response({"message": "Purchase successfully"})

        elif process_type == 'withdraw':
            # process shares withdraw
            amount = self.request.data.get('amount')
            payment_method =  self.request.data.get('payment_method')
            selected_account = self.request.data.get('account')
            send_sms         = self.request.data.get('send_sms')
            transaction_date = self.request.data.get('transaction_date')
            number_of_shares = self.request.data.get('number_of_shares')
            customer_id = self.request.data.get('customer_id')
            current_share_value = self.request.data.get('current_share_value')
            account_id = self.request.data.get('account_id')
            save_trans = None
            # Begin validation
            # validate savings account
            savings_account = SavingAccount.objects.filter(id=account_id).first()
            if not savings_account:
                return Response({"message":"No saving account found"}, status=status.HTTP_200_OK)
                
            # validate debiting chart of account
            credit_chart = OrganisationSubAccount.objects.get(pk=selected_account)
            if not credit_chart:
                return Response({"message":"No chart of account found"}, status=status.HTTP_200_OK)

            # validate customer
            customer = Customer.objects.get(pk=customer_id)
            if not customer:
                return Response({"message":"No customer found"}, status=status.HTTP_200_OK)
            
            # end of validation 

            heading = 'Share withdraw by '+ customer.name+'  ('+ customer.member_number +')'
            share_holder = ShareHolders.objects.filter(customer=customer).first()
            if not share_holder:
                share_holder = ShareHolders.objects.create(customer=customer, share_holder_added_by=request.user)
            
            branch_id = get_current_user(request, 'organisation_branch_id', None)
            organisation_branch = OrganisationBranch.objects.get(pk=branch_id)

            # debit manual member shares chart of account
            debit_chart_code = "sys-311"
            debit_chart = OrganisationSubAccount.objects.filter(account_code=debit_chart_code, account_organisation=organisation_branch.branch_organisation).first()

            # Generate reference number
            reference_no = generate_reference_no(debit_chart.account_line, organisation_branch.branch_organisation.id, 'w-sh')

            # make the transaction to update the ledger
            transaction = SystemTransactions.objects.create(amount=amount, heading=heading, reference_no=reference_no, payment_method=payment_method, voucher_no='', debit_chart=debit_chart, credit_chart=credit_chart, branch_id=branch_id, added_by=request.user, record_date=transaction_date)
            if transaction:
                share_transaction = { "shareholder":share_holder, "share_holder_trans_added_by":request.user,
                        "no_of_shares":number_of_shares, "current_share_value":current_share_value,
                        "transaction_type":"withdrawal", "organisation_branch":organisation_branch, "system_transaction":transaction}
                
                save_trans = SharesTransaction.objects.create(**share_transaction)

                saved_transaction_fields = {
                            "transaction_type":'deposit',
                            "customer_account_id":account_id,
                            "transaction_id":transaction.id
                        }

                SavingAccountTransactions.objects.create(**saved_transaction_fields) 
            if send_sms and save_trans:
                data = {"sms_key":"share_withraw_sms","customer":customer,"user":self.request.user,"branch_id":branch_id,"save_trans":save_trans}
                send_customer_sms(data)

            return Response({"message": "Withdraw successfully"})

        elif process_type == 'transfer':
            amount = self.request.data.get('amount')
            send_sms         = self.request.data.get('send_sms')
            transaction_date = self.request.data.get('transaction_date')
            number_of_shares = self.request.data.get('number_of_shares')
            transfering_customer_id = self.request.data.get('transfering_customer_id')
            receiving_customer_id = self.request.data.get('receiving_customer_id')
            current_share_value = self.request.data.get('current_share_value')
            charge_account_id = self.request.data.get('charge_account_id')

            # validate customers
            transfering_customer = Customer.objects.get(pk=transfering_customer_id)
            if not transfering_customer:
                return Response({"message":"No customer found"}, status=status.HTTP_200_OK)
            
            receiving_customer = Customer.objects.get(pk=receiving_customer_id)
            if not receiving_customer:
                return Response({"message":"No customer found"}, status=status.HTTP_200_OK)

            heading = 'Share Transfer: From '+ transfering_customer.member_number +' To ' + receiving_customer.member_number

            # validate shareholders
            transfering_share_holder = ShareHolders.objects.filter(customer=transfering_customer).first()
            if not transfering_share_holder:
                transfering_share_holder = ShareHolders.objects.create(customer=transfering_customer, share_holder_added_by=request.user)
            
            receiving_share_holder = ShareHolders.objects.filter(customer=receiving_customer).first()
            if not receiving_share_holder:
                receiving_share_holder = ShareHolders.objects.create(customer=receiving_customer, share_holder_added_by=request.user)
            
            # debit manual member shares chart of account
            branch_id = get_current_user(request, 'organisation_branch_id', None)
            organisation_branch = OrganisationBranch.objects.get(pk=branch_id)
            debit_chart_code = "sys-311"
            debit_chart = OrganisationSubAccount.objects.filter(account_code=debit_chart_code, account_organisation=organisation_branch.branch_organisation).first()

            # shares transfer account to facilitate the transfer
            credit_chart = OrganisationSubAccount.objects.filter(account_code='sys-3212', account_organisation=organisation_branch.branch_organisation).first()

            # Generate reference number
            reference_no = generate_reference_no(debit_chart.account_line, organisation_branch.branch_organisation.id, 't-sh')

            transaction_1 = None
            transaction_2 = None

            transfering_share_client_tran = None
            receiving_share_client_tran   = None
            
            # InterBranch chart
            interbranch_chart = get_inter_branch_chart(receiving_customer.customer_branch, transfering_customer.customer_branch)


            # Move the transfer first to cash account
            if receiving_customer.customer_branch.id != transfering_customer.customer_branch.id:

                # Handle inter-branch transactions  -> soure branch 
                heading = 'Inter-branch Share Transfer: From '+ transfering_customer.member_number +' To ' + receiving_customer.member_number
                transaction_1  = SystemTransactions.objects.create(amount=amount, heading=heading, reference_no=reference_no, payment_method='settlement', voucher_no='', debit_chart=debit_chart, credit_chart=interbranch_chart, branch_id=transfering_customer.customer_branch.id, added_by=request.user, record_date=transaction_date)
                
                # Handle inter-branch transactions  -> destination branch 
                transaction_2 = SystemTransactions.objects.create(amount=amount, heading=heading, reference_no=reference_no, payment_method='settlement', voucher_no='', debit_chart=interbranch_chart, credit_chart=debit_chart, branch_id=receiving_customer.customer_branch.id, added_by=request.user, record_date=transaction_date)
                
                # Reconcile inter-branch transactions
                if transaction_2:
                    inter_branch_trans_field = {
                        "source_transaction":transaction_1,
                        "destination_transaction":transaction_2,
                        "added_by":request.user,
                    }
                    InterBranchTransactions.objects.create(**inter_branch_trans_field)
                
                # sending transaction
                transfering_share_transaction = { "shareholder":transfering_share_holder, "share_holder_trans_added_by":request.user,
                        "no_of_shares":number_of_shares, "current_share_value":current_share_value,
                        "transaction_type":"transfer-out", "organisation_branch":transfering_customer.customer_branch, "system_transaction":transaction_1}
            
                transfering_share_client_tran = SharesTransaction.objects.create(**transfering_share_transaction)

                # receiving transaction
                receiving_share_transaction = { "shareholder":receiving_share_holder, "share_holder_trans_added_by":request.user,
                    "no_of_shares":number_of_shares, "current_share_value":current_share_value, 
                    "transaction_type":"transfer-in", "organisation_branch":receiving_customer.customer_branch, "system_transaction":transaction_2}

                receiving_share_client_tran = SharesTransaction.objects.create(**receiving_share_transaction)
            else:

                # Transfer transaction
                transaction_1 = SystemTransactions.objects.create(amount=amount, heading=heading, reference_no=reference_no, payment_method='cash', voucher_no='', debit_chart=debit_chart, credit_chart=credit_chart, branch_id=branch_id, added_by=request.user, record_date=transaction_date)
                transfering_share_transaction = { "shareholder":transfering_share_holder, "share_holder_trans_added_by":request.user,
                        "no_of_shares":number_of_shares, "current_share_value":current_share_value,
                        "transaction_type":"transfer-out", "organisation_branch":organisation_branch, "system_transaction":transaction_1}
                
                transfering_share_client_tran = SharesTransaction.objects.create(**transfering_share_transaction)

                # Receiving transaction
                transaction_2 = SystemTransactions.objects.create(amount=amount, heading=heading, reference_no=reference_no, payment_method='cash', voucher_no='', debit_chart=credit_chart, credit_chart= debit_chart, branch_id=branch_id, added_by=request.user, record_date=transaction_date)
                receiving_share_transaction = { "shareholder":receiving_share_holder, "share_holder_trans_added_by":request.user,
                    "no_of_shares":number_of_shares, "current_share_value":current_share_value, 
                    "transaction_type":"transfer-in", "organisation_branch":organisation_branch, "system_transaction":transaction_2}

                receiving_share_client_tran = SharesTransaction.objects.create(**receiving_share_transaction)

            # Save shares transfer relationship
            SharesTransfer.objects.create(from_customer=transfering_customer, to_customer=receiving_customer, share_transfer_added_by=request.user, organisation_branch_loosing= organisation_branch, organisation_branch_receiving= receiving_customer.customer_branch, transfering_share_transaction=transfering_share_client_tran, receiving_share_transaction= receiving_share_client_tran )

            # Apply transfer charge if configured
            shares_settings = SharesSettings.objects.filter(organisation=organisation).first()
            if shares_settings and shares_settings.transfer_charge and float(shares_settings.transfer_charge) > 0 and charge_account_id and shares_settings.transfer_charge_chart:
                charge_saving_account = SavingAccount.objects.filter(pk=charge_account_id).first()
                if charge_saving_account:
                    charge_debit_chart = charge_saving_account.account_product.accounts_chart
                    charge_reference_no = generate_reference_no(shares_settings.transfer_charge_chart.account_line, organisation_branch.branch_organisation.id)
                    charge_heading = 'Share Transfer Charge: ' + transfering_customer.member_number
                    charge_transaction = SystemTransactions.objects.create(
                        amount=shares_settings.transfer_charge,
                        heading=charge_heading,
                        reference_no=charge_reference_no,
                        payment_method='cash',
                        voucher_no='',
                        debit_chart=charge_debit_chart,
                        credit_chart=shares_settings.transfer_charge_chart,
                        branch_id=transfering_customer.customer_branch.id,
                        added_by=request.user,
                        record_date=transaction_date
                    )
                    SavingAccountTransactions.objects.create(
                        customer_account=charge_saving_account,
                        transaction=charge_transaction,
                        transaction_type='withdrawal_charge'
                    )

            if send_sms and transfering_share_client_tran:
                f_amount  = f"{float(transaction_1.amount):,}"
                sms_msg   = 'Dear '+transfering_customer.name.capitalize()+'('+transfering_customer.member_number+')'+', you have transfered shares: '+f_amount+' to '+receiving_customer.name.capitalize()+'('+receiving_customer.member_number+')'
                data      = {"sms_key":"share_transfer_sms","customer":transfering_customer,"user":self.request.user,"branch_id":branch_id,"save_trans":transfering_share_client_tran,"sms_msg":sms_msg}
                send_customer_sms(data)
                        
            if send_sms and receiving_share_client_tran:
                f_amount  = f"{float(transaction_1.amount):,}"
                sms_msg   = 'Dear '+receiving_customer.name.capitalize()+'('+receiving_customer.member_number+')'+', you have recieved shares: '+f_amount+' from '+transfering_customer.name.capitalize()+'('+transfering_customer.member_number+')'
                data      = {"sms_key":"share_transfer_sms","customer":receiving_customer,"user":self.request.user,"branch_id":branch_id,"save_trans":receiving_share_client_tran,"sms_msg":sms_msg}
                send_customer_sms(data)
            
            return Response({"message": "Transfer successfully"})

        elif process_type == 'list_dividends':
        
            start_date = self.request.data.get('start_date')
            end_date = self.request.data.get('end_date')
            bought_shares_by = self.request.data.get('bought_shares_by')
            dividends_to_savings = self.request.data.get('dividends_to_savings')
            dividends_to_shares = self.request.data.get('dividends_to_shares')
            share_flat_amount = self.request.data.get('share_flat_amount')
            shares_cap_shares = self.request.data.get('shares_cap_shares')
            with_full_share_value = self.request.data.get('with_full_share_value')

            branch_id = get_current_user(request, 'organisation_branch_id', None)
            organisation_branch = OrganisationBranch.objects.get(pk=branch_id)

            start_year = start_date.split('-')[0]
            end_year = end_date.split('-')[0]

            # validate = ShareDividends.objects.filter((Q(start_date__icontains=start_year) | Q(end_date__icontains=end_year)), organisation_branch=organisation_branch).first()
            # if not validate:
            share_dividends = ShareDividends.objects.create(start_date=start_date, end_date=end_date, bought_shares_by=bought_shares_by, dividends_to_savings=dividends_to_savings, status="approved",
                dividends_to_shares=dividends_to_shares, shares_cap_shares=shares_cap_shares, share_dividend_added_by=request.user, organisation_branch=organisation_branch, 
                share_flat_amount=float(share_flat_amount), with_full_share_value=with_full_share_value)
            

            if share_dividends:
                # get customers to share the dividends
                dividends_sharing_customers(share_dividends, request.user, start_date, end_date)
                return Response({"message": "Initiated successfully"}, status=status.HTTP_200_OK)
                
                # return Response({"message": "Error while Initiating"}, status=status.HTTP_208_ALREADY_REPORTED)

            return Response({"message": "The Initiation for the selected year, already exists"}, status=status.HTTP_208_ALREADY_REPORTED)
        
        elif process_type == 'process-dividends-sharing':
            id = self.request.data.get('id')
            if not id:
                return Response({"message": "Missing sharing dividends id"}, status=status.HTTP_208_ALREADY_REPORTED)
            
            process_share_dividends(id, request.user, organisation_id)
            return Response({"Message": "Processed successfully"})
        
        # reverse 
        elif process_type == 'reverse-dividends-sharing':
            organisation_branch = OrganisationBranch.objects.get(pk=branch_id)
            id = self.request.data.get('id')
            if not id:
                return Response({"message": "Missing sharing dividends id"}, status=status.HTTP_208_ALREADY_REPORTED)
            
            process_dividends = threading.Thread(target=process_reverse_share_dividends, args=(id, request.user,organisation_branch))
            # starting uploading divideds thread 
            process_dividends.start()
            return Response({"Message": "Processed successfully"})
        
        # delete 
        elif process_type == 'delete-dividends-sharing':
            organisation_branch = OrganisationBranch.objects.get(pk=branch_id)
            id = self.request.data.get('id')
            if not id:
                return Response({"message": "Missing sharing dividends id"}, status=status.HTTP_208_ALREADY_REPORTED)
        
            share_dividend = ShareDividends.objects.filter(id=id).first()
            details = ShareDividendsSerializer(share_dividend,read_only=True).data
            message = f'Deleted Dividend Sharing For: {share_dividend.organisation_branch.name} From {share_dividend.start_date.strftime("%Y-%m-%d")} To {share_dividend.end_date.strftime("%Y-%m-%d")}'
            add_system_audit_trail('share_management','delete_dividend_sharing',message,'',details,{},request.user,organisation_branch) 
            from django.utils import timezone
            share_dividend.deleted = True
            share_dividend.deleted_at = timezone.now()
            share_dividend.deleted_by = request.user
            share_dividend.save()
            # audit trail already fired above
            return Response({"Message": "Processed successfully"})
        
        # if unknown process type is submitted
        return Response({"message": "Unknown process type"}, status=status.HTTP_204_NO_CONTENT)
        
        
    def get(self, request, format=None):
        process_type    = request.GET.get('process_type', None)
        search  = request.GET.get('search',None)
        start_date  = request.GET.get('start_date',None)
        end_date  = request.GET.get('end_date',None)

        if not process_type:
            return Response({"message": "missing process type"})

        if process_type == 'list_purchases':
            branch_id = get_current_user(request, 'organisation_branch_id', None)
            list_share_purchases = list_shares_purchase(search,branch_id,start_date,end_date)
            return Response({"count":len(list_share_purchases), "results":list_share_purchases})
        
        if process_type == 'list_shareholders':
            branch_id = get_current_user(request, 'organisation_branch_id', None)
            list_shares_holder = list_share_holders(search,branch_id,start_date,end_date)

            return Response({"count":len(list_shares_holder), "results":list_shares_holder})
        
        if process_type == 'list_transfers':
            branch_id = get_current_user(request, 'organisation_branch_id', None)
            list_share_purchases = list_shares_transfer(search,branch_id,start_date,end_date)

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

        if process_type == 'get_client_shares_bal':
            client_id    = request.GET.get('client_id', None)
            if not client_id:
                return Response({"Message": "missing client id"})
            
            customer = Customer.objects.get(pk=client_id)
            if not customer:
                return Response({"Message": "client not found"})
            
            print("=========================================================== here we are ")
            
            client_shares_balance = get_client_shares_balance(customer)
            return Response({"count": 0 if client_shares_balance== False else 1 , "results": {"total_share_value":0, "share_value":0, "no_of_shares":0 } if client_shares_balance == False else client_shares_balance })

        if process_type == 'list_withdrawal':
            branch_id = get_current_user(request, 'organisation_branch_id', None)
            list_share_withdrawal = list_shares_withdrawal(search,branch_id,start_date,end_date)

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

        # if unknown process type is submitted
        return Response({"Message": "Unknown process type"})
    
    def put(self, request, pk, format=None):
        branch_id = get_current_user(request, 'organisation_branch_id', None)
        branch    = OrganisationBranch.objects.get(pk=branch_id)
    
        old_share_dividend  = ShareDividends.objects.filter(id=pk).first()
        share_dividend      = ShareDividends.objects.filter(id=pk).update(**request.data)
        update_dividend     = ShareDividends.objects.filter(id=pk).first()

        if share_dividend:
            s_old_details   = ShareDividendsSerializer(old_share_dividend,read_only=True).data
            s_new_details   = ShareDividendsSerializer(update_dividend,read_only=True).data

            s_message = f'Updated Share Dividends For: {update_dividend.organisation_branch.name} From {update_dividend.start_date.strftime("%Y-%m-%d")} To {update_dividend.end_date.strftime("%Y-%m-%d")}'
            add_system_audit_trail('share_management','update_dividend_sharing',s_message,'',s_old_details,s_new_details,request.user,branch) 
        
            from django.utils import timezone
            ShareDividendCustomers.objects.filter(share_dividend=update_dividend).update(
                deleted=True, deleted_at=timezone.now(), deleted_by=request.user)
            add_system_audit_trail('share_management', 'delete_dividend_customers',
                f'Cleared Dividend Customers for update: {update_dividend.organisation_branch.name}',
                '', {}, {}, request.user, branch)
            start_date = update_dividend.start_date.strftime("%Y-%m-%d")
            end_date = update_dividend.end_date.strftime("%Y-%m-%d")

            process_dividends = threading.Thread(target=dividends_sharing_customers, args=(update_dividend, request.user, start_date, end_date))
            # starting uploading divideds thread 
            process_dividends.start()

            return Response({"message":"updated successfully"}, status=status.HTTP_200_OK)
        return Response({"message":"error occurred"}, status=status.HTTP_400_BAD_REQUEST)

class ManageSharesDividendsView(viewsets.ModelViewSet):
    serializer_class = ShareDividendsSerializer

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

class ListSharesDividendsCustomersView(viewsets.ModelViewSet):
    serializer_class = ShareDividendCustomersSerializer

    def get_queryset(self):
        id = self.request.GET.get('id', None)
        return ShareDividendCustomers.objects.filter(share_dividend__id=id)

class ImportShareDividendsView(APIView):
    parser_classes = (MultiPartParser,)
    
    def allowed_file(self, filename):
        return '.' in filename.name and \
            filename.name.split('.')[1].lower() in ["csv"]
    
    def post(self, request, format=None):
        branch_id = get_current_user(request, 'organisation_branch_id', None)
        organisation_branch = OrganisationBranch.objects.get(pk=branch_id)
        
        start_date  = make_aware(datetime.strptime(self.request.data.get('start_date'), '%Y-%m-%d')) 
        end_date  = make_aware(datetime.strptime(self.request.data.get('end_date'), '%Y-%m-%d'))
        bought_shares_by  = make_aware(datetime.strptime(self.request.data.get('bought_shares_by'), '%Y-%m-%d'))
        transaction_date  = make_aware(datetime.strptime(self.request.data.get('transaction_date'), '%Y-%m-%d'))

        file_obj = request.FILES["file"]
        if file_obj and self.allowed_file(file_obj):
            # save savings upload
            # current dateTime
            now = datetime.now()
            date_time_str = now.strftime("%m%Y%d%H%M%S")
            # row count 
            file_obj.seek(0)
            reader = csv.DictReader(io.StringIO(file_obj.read().decode('utf-8')))
            rows_count = list(reader)
            total_rows = len(rows_count)
            if total_rows > 0:
                share_dividends = ShareDividends.objects.create(start_date=start_date, end_date=end_date, bought_shares_by=bought_shares_by, share_dividend_added_by=request.user, organisation_branch=organisation_branch, status='approved')

                if share_dividends:
                    # create upload thread
                    upload_dividends = threading.Thread(target=upload_dividends_thread, args=(request.user, transaction_date, file_obj, share_dividends, organisation_branch.branch_organisation.id))
                    # starting upload_savings thread 
                    upload_dividends.start()

                    return Response({"message": "Initiated successfully"}, status=status.HTTP_200_OK)
                return Response({"message": "Error while Initiating"}, status=status.HTTP_208_ALREADY_REPORTED)
            return Response({"message": "No records to upload"}, status=status.HTTP_208_ALREADY_REPORTED)
        return Response({"message": "Invalid file type"}, status=status.HTTP_208_ALREADY_REPORTED)

class GenerateSharesReportView(APIView):
    permission_classes = [AllowAny]

    def post(self, request, format=None):
        organisation_id = request.data.get('organisation_id', None)
        thread = threading.Thread(target=generate_sacco_shares_files, args=(organisation_id,))
        thread.start()

        return Response({"message": 'created'}, status=status.HTTP_200_OK)
        
class SharesReportView(APIView):

    def get(self, request, format=None):
        results = {"count": 0, "results": []}
        as_at        = self.request.query_params.get('as_at', None)
        order_by        = self.request.query_params.get('order_by', None)
        branch_id       = self.request.query_params.get('branch_id', None)
        customer_type   = self.request.query_params.get('customer_type', None)
        organisation_id = get_current_user(self.request, 'organisation_id', None)

        if as_at:
            as_at = as_at.split(' ')[0]
        
        results = get_client_shares_holders(organisation_id, order_by, branch_id,  customer_type, as_at)

        return Response(results, status=status.HTTP_200_OK)
    
class NonShareHoldersReportView(APIView):
    def get(self,request,format=None):
        results = {"count": 0, "results": []}
        page_size = self.request.query_params.get('page_size', 15)
        response = []
        as_at           = self.request.query_params.get('as_at', None)
        branch_id       = self.request.query_params.get('branch_id', None)
        customer_type   = self.request.query_params.get('customer_type', None)
        organisation_id = get_current_user(self.request, 'organisation_id', None)
        search          = self.request.query_params.get("search", None)

        filters = {
            "customer_branch__branch_organisation__id": organisation_id,
            "is_deleted":False
        }
        if branch_id:
            filters['customer_branch__id'] = branch_id
        if customer_type:
            filters['branch_customer_type__id'] = customer_type
        if as_at:
            filters['date_added__date__lte'] = as_at

        if search:
            customers = Customer.objects.filter(Q(name__icontains=search) | Q(member_number__icontains=str(search)) | Q(old_member_number__icontains=str(search))| Q(telephone__icontains=search),**filters)
        else:
            customers = Customer.objects.filter(**filters)

        paginator = PageNumberPagination()
        paginator.page_size = page_size
        paginated_results = paginator.paginate_queryset(customers, request)
        
        for customer in paginated_results:
            shares_balance = get_client_shares_balance(customer)
            if shares_balance and shares_balance['all_total_share_value'] == 0:
                response.append(
                    {"id":customer.id, "name":customer.name, "member_number":customer.old_member_number, "new_member_number":customer.member_number, "customer_type":customer.branch_customer_type.customer_type,"telephone":customer.telephone}
                )
        results['count'] = paginator.page.paginator.count
        results['results'] = response
        return Response(results)

class SharesTransferReportView(APIView):
    def get(self,request,format=None):
        results = {"count": 0, "results": []}
        page_size = self.request.query_params.get('page_size', 15)
        search          = self.request.query_params.get("search", None)
        as_at           = self.request.query_params.get('as_at', None)
        branch_id       = self.request.query_params.get('branch_id', None)
        customer_type   = self.request.query_params.get('customer_type', None)
        organisation_id = get_current_user(self.request, 'organisation_id', None)
        filters = {
            "organisation_branch__branch_organisation__id": organisation_id
        }
        if branch_id:
            filters['organisation_branch__id'] = branch_id
        if customer_type:
            filters['shareholder__customer__branch_customer_type__id'] = customer_type
        if as_at:
            filters['date_added__date__lte'] = as_at

        if search:
            list_transfers =  SharesTransaction.objects.filter(Q(shareholder__customer__name__icontains=search) | Q(system_transaction__reference_no=search),transaction_type = 'transfer-out',**filters).order_by("-id")
        else:
            list_transfers =  SharesTransaction.objects.filter(transaction_type = 'transfer-out',**filters).order_by('id')
        paginator = PageNumberPagination()
        paginator.page_size = page_size
        paginated_results = paginator.paginate_queryset(list_transfers, request)

        serializer = SharesTransactionSerializer(paginated_results, many=True)
        
        results['count'] = paginator.page.paginator.count
        results['results'] = serializer.data
        return Response(results)
    
class SharesLedgerView(APIView):

    def get(self, request, format=None):
        data = {
            'shares':[]
        }
        '''
        Member statement.
        '''
        end = request.GET.get('e', None)
        if end:
            end = end + ' 23:59:59'

        customer_id = request.GET.get('id', None)

        customer = Customer.objects.get(pk=customer_id)
        if customer:
            # Generate balance BF
            share_transactions = SharesTransaction.objects.filter(shareholder__customer = customer, is_deleted=False, deleted=False).order_by('id')
            # serializer = SharesLedgerSerializer(share_transactions, many=True)
        data = {
            'shares':SharesLedgerSerializer(share_transactions, many=True).data
        }

        return Response(data, status=status.HTTP_200_OK)

class GroupMemberssharesView(APIView):
    
    def get(self, request, format=None):
        action     = request.GET.get('action', None)
        if action == 'ledger':
            end = request.GET.get('e', None)
            group_id = request.GET.get('id', None)
            member_list = []
            memberships = GroupMembership.objects.filter(group__id=group_id, active=True)
            if memberships:
                for membership in memberships:
                    filter_array = {"membership":membership}
                    if end:
                        filter_array['shares__system_transaction__record_date__lte'] = make_aware(datetime.strptime(end + ' 23:59', '%Y-%m-%d %H:%M'))
                    transactons  = {}
                    group_shares = GroupShareTransaction.objects.filter(**filter_array).values_list('shares', flat=True)
                    if group_shares:
                       share_transactions = SharesTransaction.objects.filter(id__in = group_shares).order_by('id')
                       transactons = SharesLedgerSerializer(share_transactions, many=True).data
                    member_list.append({
                        "id":membership.member.id,
                        "customer_name":membership.member.name,
                        "customer_member_number":membership.member.member_number,
                        "customer_type":membership.member.branch_customer_type.customer_type,
                        "transactions":transactons,
                    })
            return Response({"count":len(member_list), "results":member_list}, status=status.HTTP_200_OK)
        
        if action == 'member_transaction':
            end      = request.GET.get('e', None)
            group_id = request.GET.get('id', None)
            member   = request.GET.get('member', None)
            transactons = []
            membership  = GroupMembership.objects.filter(group__id=group_id, member__id=member,active=True).first()
            if membership:
                filter_array = {"membership":membership}
                if end:
                    filter_array['shares__system_transaction__record_date__lte'] = make_aware(datetime.strptime(end + ' 23:59', '%Y-%m-%d %H:%M'))
                group_shares = GroupShareTransaction.objects.filter(**filter_array).values_list('shares', flat=True)
                if group_shares:
                    share_transactions = SharesTransaction.objects.filter(id__in = group_shares).order_by('id')
                    transactons = SharesLedgerSerializer(share_transactions, many=True).data
            return Response({"count":len(transactons), "results":transactons}, status=status.HTTP_200_OK)
        return Response({"count":0, "results":[]}, status=status.HTTP_200_OK)
    
    def post(self, request):
        action   = request.data.get('action')
        if action == 'purchase':
            branch_id = get_current_user(request, 'organisation_branch_id', None)
            response = process_group_share_purchases(request.user,self.request,branch_id)
            return Response(response, status=status.HTTP_200_OK)
        if action == 'withdraw':
            branch_id = get_current_user(request, 'organisation_branch_id', None)
            response = process_group_share_withraws(request.user,self.request,branch_id)
            return Response(response, status=status.HTTP_200_OK)
