from django.shortcuts import render
from rest_framework import viewsets
from rest_framework.filters import SearchFilter, OrderingFilter
from django_filters.rest_framework import DjangoFilterBackend
from .serializers import *
from .models import *
from .permissions import *
from questbanker_api.utils import get_current_user
from ledgers.ledgers_helper import *
from savings.savings_bal_helper import *
from users.audit_log_helper import add_system_audit_trail
from rest_framework.views import APIView
from savings.savings_helper import over_draft_auto_payments, compute_over_due_payment,make_manual_over_draft_payment
from rest_framework.response import Response
from rest_framework import status
from rest_framework import viewsets
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.permissions import AllowAny
import threading
from .permissions import *
from questbanker_api.utils import get_current_user
import threading
from datetime import datetime
from savings.models import *
from ledgers.ledgers_helper import generate_reference_no
import pytz


class OverDraftProductViewSet(viewsets.ModelViewSet):
    serializer_class = OverDraftProductsSerializer
    filter_backends = (SearchFilter, OrderingFilter, DjangoFilterBackend, )
    filterset_fields = ['product_name', 'id',]
    search_fields = ('product_name', )

    def get_queryset(self):
        organisation_id = get_current_user(self.request, 'organisation_id', None)
        return OverDraftProducts.objects.filter(is_deleted=False, 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( organisation=organisation, over_draft_product_added_by=self.request.user)

    def perform_update(self, serializer):
        id = self.request.data.get('id', None)
        action = self.request.data.get('action', None)
        if action == 'delete':
            OverDraftProducts.objects.get(pk=id).delete()
        else:
            serializer.save(over_draft_product_last_updated_by=self.request.user)
         


class OverDraftViewSet(viewsets.ModelViewSet):
    serializer_class = OverDraftsSerializer
    filter_backends = (SearchFilter, OrderingFilter, DjangoFilterBackend, )
    filterset_fields = ['saving_account']

    def get_queryset(self):
        organisation_id = get_current_user(self.request, 'organisation_id', None)
        search          = self.request.query_params.get("search", None)
        customer        = self.request.query_params.get('customer',None)

        filter_query    = {"over_draft_product__organisation__id":organisation_id }
    
        if customer:
            return OverDrafts.objects.filter(**filter_query, saving_account__account_customer__id=customer).order_by("-id")
        if search:
            return OverDrafts.objects.filter(Q(saving_account__account_customer__member_number__icontains=search) | Q(saving_account__account_customer__name__icontains=search)| Q(saving_account__account_customer__old_member_number__icontains=search),**filter_query).order_by("-id")
        else:
            return OverDrafts.objects.filter(**filter_query).order_by("-id")
    
    def get_serializer_context(self):
        context = super().get_serializer_context()
        context.update({"request": self.request})
        return context

    def perform_create(self, serializer):
        organisation_id = get_current_user(self.request, 'organisation_id', None)
        organisation_branch_id = get_current_user(self.request, 'organisation_branch_id', None)
        branch = OrganisationBranch.objects.get(pk=organisation_branch_id)
        organisation = Organisation.objects.get(pk=organisation_id)

        amount = self.request.data.get('amount')
        transaction_date = self.request.data.get('transaction_date')
        debit_chart_code = "sys-116"
        debit_chart = get_chart_of_account_by_code(debit_chart_code, organisation) 

        savings_account = SavingAccount.objects.get(pk=self.request.data.get('credit_chart')) 
        member_number = savings_account.account_customer.member_number
        if savings_account.account_customer.old_member_number:
            member_number = savings_account.account_customer.old_member_number 

        heading = 'Over Draft Application for ' + savings_account.account_customer.name + '-' + member_number 

        reference_no = generate_reference_no(savings_account.account_product.accounts_chart.account_line, organisation_id, 'ov-dft')
        over_draft_product=OverDraftProducts.objects.get(pk=self.request.data.get('over_draft_product'))

        saved_transaction = SystemTransactions.objects.create(
                amount=amount,
                heading=heading, 
                reference_no=reference_no,
                debit_chart=debit_chart,
                credit_chart=savings_account.account_product.accounts_chart,
                branch=branch, 
                payment_method = 'offset',
                record_date=transaction_date,
                added_by = self.request.user)
        
        if saved_transaction:
            serializer.save( 
                saving_account=savings_account, 
                branch=branch,
                reference_transaction=saved_transaction,
                over_draft_added_by=self.request.user,
                over_draft_product=over_draft_product,
                withdraw_allowance_period=over_draft_product.withdraw_allowance_period,
                charge_rate=over_draft_product.charge_rate,
                charge_type=over_draft_product.charge_type,
                penalty_rate =over_draft_product.penalty_rate,
                penalty_type=over_draft_product.penalty_type,
                penalty_interval=over_draft_product.penalty_interval,
                penalty_grace_period=over_draft_product.penalty_grace_period
            )
        
            saved_transaction_fields = {
                    "transaction_type":'over-draft',
                    "customer_account_id":savings_account.id,
                    "transaction_id":saved_transaction.id
                }
            SavingAccountTransactions.objects.create(**saved_transaction_fields) 

    def perform_update(self, serializer, user_id=None):
        action = self.request.data.get('action', None)
        delete_type   = self.request.data.get('type', None)
        id = self.request.data.get('id', None)
        updated_instance = serializer.save(over_draft_last_updated_by=self.request.user)
        if action == 'update':
            if updated_instance:
                over_draft_product=OverDraftProducts.objects.get(pk=self.request.data.get('over_draft_product'))
            serializer.save(over_draft_last_updated_by=self.request.user, over_draft_product=over_draft_product)
        elif action == 'terminate':
            over_draft_instance = OverDrafts.objects.get(pk=id)
            system_transaction_instance = over_draft_instance.reference_transaction
            add_system_audit_trail(
                'overdraft', 'terminate_overdraft',
                f'Terminated OverDraft for {over_draft_instance.saving_account.account_customer.name}',
                '', {}, {}, self.request.user,
                over_draft_instance.branch
            )
            from django.utils import timezone
            now = timezone.now()
            df = dict(deleted=True, deleted_by_id=self.request.user.id, deleted_at=now)
            OverDrafts.objects.filter(pk=over_draft_instance.pk).update(**df)
            SavingAccountTransactions.objects.filter(transaction=system_transaction_instance).update(**df)
            SystemTransactions.objects.filter(pk=system_transaction_instance.pk).update(**df)

        elif action == 'cancel':
            repayments = OverDraftPayment.objects.filter(over_draft=id, transaction__transaction_type='normal').select_related('over_draft')
            written_off_payments = OverDraftPaymentWrittenOff.objects.filter(over_draft=id)
            if repayments:
                for repayment in repayments:
                    principal_paid = repayment.principal_paid
                    interest_paid = repayment.interest_paid
                    penalty_paid = repayment.penalty_paid

                    if principal_paid:
                        debit_chart_code = "sys-116"
                        debit_chart = get_chart_of_account_by_code(debit_chart_code, updated_instance.branch.branch_organisation)
                        princ_transaction_details = {
                            "heading": "Over Draft Principal Cancellation: " + updated_instance.saving_account.account_customer.name + " - " + updated_instance.saving_account.account_no,
                            "amount": principal_paid,
                            "record_date":  timezone.now(),
                            "debit_chart_id": debit_chart.id,
                            "credit_chart_id":updated_instance.saving_account.account_product.accounts_chart.id,
                            "payment_method": 'offset',
                            "voucher_no": "",
                            "ref_no_prefix": 'ov-princ-py-t',
                            "organisation_id": updated_instance.branch.branch_organisation.id,
                            "branch_id":updated_instance.branch.id,
                            "user_id": user_id
                        }
                        # Save deduction transaction
                        princ_transaction = post_transaction(princ_transaction_details)
                        if princ_transaction:
                            # Link deduction to savings account
                            account_transaction = SavingAccountTransactions(customer_account=updated_instance.saving_account, transaction=princ_transaction, transaction_type=' over-draft-principal-cancel-payment')
                            account_transaction.save()

                    if interest_paid:
                        int_transaction_details = {
                            "heading": "Over Draft Interest Payment: " + updated_instance.saving_account.account_customer.name + " - " + updated_instance.saving_account.account_no,
                            "amount": interest_paid,
                            "record_date":  timezone.now(),
                            "debit_chart_id": updated_instance.over_draft_product.interest_income_chart.id,
                            "credit_chart_id":updated_instance.saving_account.account_product.accounts_chart.id,
                            "payment_method": 'offset',
                            "voucher_no": "",
                            "ref_no_prefix": 'ov-drf-int-py-t',
                            "organisation_id": updated_instance.branch.branch_organisation.id,
                            "branch_id":updated_instance.branch.id,
                            "user_id": user_id
                        }
                        # Save deduction transaction
                        interest_transaction = post_transaction(int_transaction_details)
                        if interest_transaction:
                        # Link deduction to savings account
                            account_transaction = SavingAccountTransactions(customer_account=updated_instance.saving_account, transaction=interest_transaction, transaction_type='over-draft-interest-cancel-payment')
                            account_transaction.save()

                    if penalty_paid:
                        pen_transaction_details = {
                            "heading": "Over Draft Penalty Payment: " + updated_instance.saving_account.account_customer.name + " - " + updated_instance.saving_account.account_no,
                            "amount": penalty_paid,
                            "record_date":  timezone.now(),
                            "debit_chart_id": updated_instance.over_draft_product.interest_income_chart.id,
                            "credit_chart_id":updated_instance.saving_account.account_product.accounts_chart.id,
                            "payment_method": 'offset',
                            "voucher_no": "",
                            "ref_no_prefix": 'ov-drf-int-py-t',
                            "organisation_id": updated_instance.branch.branch_organisation.id,
                            "branch_id":updated_instance.branch.id,
                            "user_id": user_id
                        }
                    # Save deduction transaction
                        penalty_transaction = post_transaction(pen_transaction_details)
                        if penalty_transaction:
                        # Link deduction to savings account
                            account_transaction = SavingAccountTransactions(customer_account=updated_instance.saving_account, transaction=penalty_transaction, transaction_type='over-draft-penalty-cancel-payment')
                           

                overdraft_instance = OverDrafts.objects.get(pk=id)
                system_transaction_instance = overdraft_instance.reference_transaction
                overdraft_instance.status = 'in-progress'
                overdraft_instance.save()

                add_system_audit_trail(
                    'overdraft', 'cancel_overdraft_payments',
                    f'Cancelled OverDraft payments for {overdraft_instance.saving_account.account_customer.name}',
                    '', {}, {}, self.request.user,
                    overdraft_instance.branch
                )

                from django.utils import timezone
                now = timezone.now()
                df = dict(deleted=True, deleted_by_id=self.request.user.id, deleted_at=now)
                if not delete_type:
                    OverDraftPayment.objects.filter(pk=repayment.pk).update(**df)
                OverDraftPayment.objects.filter(over_draft=id, transaction__transaction_type='normal').update(**df)
                SavingAccountTransactions.objects.filter(transaction=system_transaction_instance).update(**df)
                SystemTransactions.objects.filter(pk=system_transaction_instance.pk).update(**df)

            if written_off_payments:
                from django.utils import timezone
                now = timezone.now()
                df = dict(deleted=True, deleted_by_id=self.request.user.id, deleted_at=now)
                for written_off in written_off_payments:
                    written_off.over_draft.status = 'in-progress'
                    written_off.over_draft.save()
                    if not delete_type:
                        OverDraftPaymentWrittenOff.objects.filter(pk=written_off.pk).update(**df)
                    SystemTransactions.objects.filter(pk=written_off.transaction.pk).update(**df)
                OverDraftPaymentWrittenOff.objects.filter(over_draft=id).update(**df)
                

        else:
            serializer.save(over_draft_last_updated_by=self.request.user)


class OverDraftPaymentCronJobView(APIView):
    permission_classes = [AllowAny, IsPostOnly]

    def post(self, request, format=None):
        auto_over_draft_payments = threading.Thread(target=over_draft_auto_payments, args=())
        auto_over_draft_payments.start()

        return Response({"message":"Cron initiated successfully"})


class ManualOverDraftPaymentView(APIView):
    serializer_class = OverDraftsSerializer
    def post(self, request, format=None):
        over_draft_id = self.request.data.get('over_draft_id')
        overdraft = OverDrafts.objects.get(pk=over_draft_id)
        closing_date = request.data.get('closing_date')
        closing_interest_amount = request.data.get('interest_charge_due')
        closing_penalty_amount= request.data.get('penalty_charge_due')
        if overdraft:
            as_at = datetime.now().date()
            eat_timezone = pytz.timezone("Africa/Nairobi")
            payment_date = datetime.strptime(closing_date, "%Y-%m-%d %H:%M:%S")
    
            payment_details = {
                "status":"close",
                "overdraft":overdraft,
                "as_at":as_at,
                "payment_date":payment_date,
                "user_id":self.request.user.id,
                "closing_int_amount":closing_interest_amount,
                "closing_pen_amount":closing_penalty_amount
            }
            make_manual_over_draft_payment(payment_details)

            # post balances for manual to expenses
            over_draft_due_payment =  compute_over_due_payment(overdraft,as_at,eat_timezone,payment_date)
            princ_to_pay = float(over_draft_due_payment["principal_balance"])
            if princ_to_pay > 0:
                debit_chart_code = "sys-116"
                debit_chart = get_chart_of_account_by_code(debit_chart_code, overdraft.branch.branch_organisation)
                over_draft_loss_transaction_details = {
                    "heading": "Over Draft Loss: " + overdraft.saving_account.account_customer.name + " - " + overdraft.saving_account.account_no,
                    "amount": princ_to_pay,
                    "record_date":  timezone.now(),
                    "debit_chart_id": debit_chart.id,
                    "credit_chart_id":overdraft.over_draft_product.expense_account.id,
                    "payment_method": 'offset',
                    "voucher_no": "",
                    "ref_no_prefix": 'ov-drf-loss',
                    "organisation_id": overdraft.branch.branch_organisation.id,
                    "branch_id":overdraft.branch.id,
                    "user_id": self.request.user.id
                }
                # Save deduction transaction
                princ_transaction = post_transaction(over_draft_loss_transaction_details)
                over_draft_details = {
                    "over_draft": overdraft,
                    "transaction":princ_transaction,
                    "date_added":timezone.now(),
                }
            
                OverDraftPaymentWrittenOff.objects.create(**over_draft_details)
                overdraft.status = 'closed'
                overdraft.save()
        return Response({"message": "Over Draft Payments updated successfully"}, status=status.HTTP_200_OK)



