from django.conf import settings
from .models import *
from ledgers.models import SystemTransactions, InterBranchTransactions
from ledgers.ledgers_helper import *
from customers.models import Customer
from savings.models import SavingAccountTransactions, SavingAccount, GroupMembership, GroupSavingTransaction, AccountBookings
from savings.serializers import SavingAccountSerializer
from shares.models import SharesTransaction
from organisations.models import OrganisationSetting, WorkingHours, StaffWorkingHours
# from users.models import ExtraTime
from ledgers.ledgers_helper import get_inter_branch_chart
from .data import sms_key_suscriptions
import requests
import json
import re
import certifi
from django.db import connection
from datetime import datetime, timedelta
from django.db.models import Sum


def send_customer_sms(data):
    data_keys = data.keys()
    interest_amount = data.get('interest_amount', 0)
    if 'sms_msg' not in data_keys:
        data['sms_msg'] = ''
    if 'save_trans' not in data_keys:
        data['save_trans'] = None
    if data['sms_key'] in sms_key_suscriptions["savings_sms_types"]:
        send_customer_savings_sms(data['sms_key'], data['customer_account'], data['user'], data['branch_id'], data['save_trans'], data['sms_msg'], data['customer'], interest_amount)
    elif data['sms_key'] in sms_key_suscriptions["shares_sms_types"]:
        send_customer_share_sms(data['sms_key'], data['customer'], data['user'], data['branch_id'], data['save_trans'], data['sms_msg'])
    elif data['sms_key'] in sms_key_suscriptions["loan_sms_types"]:
        send_customer_loan_sms(data['sms_key'], data['customer'], data['user'], data['branch_id'], data['sms_msg'], data['loan'])
    elif data['sms_key'] in sms_key_suscriptions["inventory_sms_types"]:
        send_inventory_sms(data['sms_key'], data['telephone'], data['user'], data['branch_id'], data['sms_msg'])
    else:
        send_customer_other_sms(data['sms_key'], data['customer'], data['user'], data['branch_id'], data['sms_msg'])


def get_customer_sms_charge_account(customer):
    customer_account = None
    savings_accounts = SavingAccount.objects.filter(account_customer=customer, status='active')
    if not savings_accounts:
        if customer:
            organisation_id = customer.customer_branch.branch_organisation.id
            vsla_customer_setting = OrganisationSetting.objects.filter(org_setting__id=organisation_id, setting_key='group_savings_customers').first()
            if vsla_customer_setting:
                vsla_customer_types = []
                if vsla_customer_setting.setting_value:
                    vsla_customer_types = json.loads(vsla_customer_setting.setting_value)
                
                if not isinstance(vsla_customer_types, list):
                    vsla_customer_types = list(vsla_customer_types.split(","))

                membership = GroupMembership.objects.filter(member=customer).first()
                if membership:
                    savings_accounts = SavingAccount.objects.filter(account_customer__branch_customer_type__id__in=vsla_customer_types, account_customer=membership.group, status='active')
                    
    if savings_accounts:
        for savings_account in savings_accounts:
            account_balance_data = SavingAccountSerializer(savings_account).data
            account_balance = account_balance_data['account_balance']['balance_raw']
            customer_account = savings_account
            if account_balance > 0:
                break
    return customer_account


def send_customer_other_sms(sms_key, customer, user, branch_id, sms_msg=''):
    subscriber_data = is_customer_subscriber(sms_key, customer, branch_id)
    customer_account = get_customer_sms_charge_account(customer)
    if subscriber_data['is_subscribed']:
        subscriber_data["branch"] = OrganisationBranch.objects.get(pk=branch_id)
        subscriber_data["customer_account"] = customer_account
        subscriber_data["message"] = sms_msg
        subscriber_data["sent_by"] = user
        subscriber_data["sms_unique_key"] = ''
        save_user_sms(subscriber_data)


def send_inventory_sms(sms_key, telephone, user, branch_id, sms_msg=''):
    """Send an inventory notification SMS directly to a phone number (e.g. service provider).
    Charged to sacco. Uses the organisation's subscription for the given sms_key."""
    branch = OrganisationBranch.objects.get(pk=branch_id)
    organisation_sms_subscription = OrganisationSmsSubscription.objects.filter(
        organisation=branch.branch_organisation,
        is_subscribed=True,
        sms_type__sms_type_key=sms_key,
        sms_type__status='active'
    ).first()
    if not organisation_sms_subscription:
        return
    subscriber_data = {
        "sms_type": organisation_sms_subscription.sms_type,
        "charge": organisation_sms_subscription.charge,
        "charged_to": 'sacco',
        "telephone": telephone,
        "reciever_name": telephone,
        "recieved_by": 0,
        "is_subscribed": True,
        "debit_chart": None,
        "free_sms": 0,
        "recieved_by_type": 'external',
        "branch": branch,
        "customer_account": None,
        "message": sms_msg,
        "sent_by": user,
        "sms_unique_key": '',
    }
    save_user_sms(subscriber_data)

# def get_depositor_name(user):
#     if hasattr(user, "user_staff") and user.user_staff and user.user_staff.name:
#         return user.user_staff.name.capitalize()
#     if user.first_name:
#         return user.first_name.capitalize()
#     return user.username.capitalize()

def send_customer_savings_sms(sms_key, customer_account, user, branch_id, save_trans=None, sms_msg='', customer=None, interest_amount=0):
    subscriber_data = is_customer_subscriber(sms_key, customer, branch_id)
    
    # handling VSLA Group Admin Saving Sms - for admin notifications, check group account subscription
    if sms_key in ["deposit_notify_admin", "withdraw_notify_admin"]:
        # For group admin notifications, use group account subscription if individual is not subscribed
        if not subscriber_data['is_subscribed']:
            base_sms_key = "cash_deposit_sms" if sms_key == "deposit_notify_admin" else "cash_withdraw_sms"
            subscriber_data = is_customer_subscriber(base_sms_key, customer_account.account_customer, branch_id)

    if subscriber_data['is_subscribed']:
        if subscriber_data['charged_to'] == 'member':
            subscriber_data["debit_chart"] = customer_account.account_product.accounts_chart
        subscriber_data["branch"] = OrganisationBranch.objects.get(pk=branch_id)
        subscriber_data["customer_account"] = customer_account
       
        account_balance = SavingAccountSerializer(SavingAccount.objects.get(pk=customer_account.id, status='active')).data
        available_bal = account_balance['account_balance']['balance_raw'] - subscriber_data["charge"]
        balance_actual = account_balance['account_balance']['balance_actual'] - subscriber_data["charge"]
        if ((available_bal >= 0 and subscriber_data['charged_to'] == 'member') or subscriber_data['charged_to'] == 'sacco') and sms_key in sms_key_suscriptions["savings_sms_types"]:
            balance_actual = round(balance_actual, 0)
            if sms_key == "cash_deposit_sms":
                depositor_name = getattr(save_trans.transaction, "depositor_name", None) 
                sms_msg = (
                    f"Dear {customer.name.capitalize()}, "
                    f"Credit Deposit UGX: {save_trans.transaction.amount:,} "
                    f"by {depositor_name} "
                    f"on A/C: {customer_account.account_no}. "
                    f"Balance UGX: {balance_actual:,}. "
                    f"Thanks for saving with {subscriber_data['branch'].branch_organisation.name}"
          )
            if sms_key == "salary_upload_sms":
                sms_msg = (
                    f"Dear {customer.name.capitalize()}, "
                    f"UGX {save_trans.transaction.amount:,.0f} has been credited "
                    f"to your A/C: {customer_account.account_no}. "
                    f"Reason: {save_trans.transaction.heading.split(' for ')[0] if save_trans.transaction.heading else 'Salary Payment'}. "
                    f"Balance UGX: {balance_actual:,}. "
                    f"Thanks for saving with {subscriber_data['branch'].branch_organisation.name}"
                )
            if sms_key == "cash_withdraw_sms":
                sms_msg = 'Dear ' + customer.name.capitalize() + ', Cash Withdraw UGX: ' + f"{save_trans.transaction.amount:,}" + 'from A/C: ' + customer_account.account_no + '. Balance UGX: ' + f"{balance_actual:,}" + ' .Thanks for saving with ' + subscriber_data["branch"].branch_organisation.name
            
            if sms_key == "cash_transfer_sms":
                sms_msg = sms_msg + '. Balance UGX: ' + f"{balance_actual:,}" + '. Thanks for saving with ' + subscriber_data["branch"].branch_organisation.name
            
            if sms_key == "savings_interest_sms":
                sms_msg = 'Dear ' + customer.name.capitalize() + ', Saving Interest Payment UGX: ' + f"{save_trans.transaction.amount:,}" + ' on A/C: ' + customer_account.account_no + '. Balance UGX: ' + f"{balance_actual:,}" + ' .Thanks for saving with ' + subscriber_data["branch"].branch_organisation.name
            
            if sms_key == "fixed_deposit_closure_sms":
               fd_amount = save_trans.transaction.amount if save_trans else 0
               sms_msg = (
                  f'Dear {customer.name.capitalize()}, your fixed deposit of UGX: {fd_amount:,} '
                  f'has matured. Your Interest of UGX: {interest_amount:,} has been paid to your account. '
                  f'Thank you for banking with {subscriber_data["branch"].branch_organisation.name}'
                )
            
            # Handle Group Balance
            if sms_key == "deposit_notify_admin":
                sms_msg = sms_msg + '. Balance UGX: ' + f"{balance_actual:,}" + '. Thanks for saving with ' + subscriber_data["branch"].branch_organisation.name
            if sms_key == "withdraw_notify_admin":
                sms_msg = sms_msg + '. Balance UGX: ' + f"{balance_actual:,}" + '. Thanks for saving with ' + subscriber_data["branch"].branch_organisation.name
            
            subscriber_data["message"] = sms_msg
            subscriber_data["sent_by"] = user
            subscriber_data["sms_unique_key"] = ''

            response = save_user_sms(subscriber_data)
            if customer.id != customer_account.account_customer.id and response['status']:
                saved_transaction = SavingAccountTransactions.objects.filter(transaction=response['transaction']).first()
                membership = GroupMembership.objects.filter(member=customer, group=customer_account.account_customer, active=True).first()
                if membership and saved_transaction:
                    group_trans_field = {
                        "membership": membership,
                        "savings": saved_transaction
                    }
                    GroupSavingTransaction.objects.create(**group_trans_field)


def send_customer_share_sms(sms_key, customer, user, branch_id, save_trans=None, sms_msg=''):
    subscriber_data = is_customer_subscriber(sms_key, customer, branch_id)
    account_balance = -1
    dr_amount = 0
    cr_amount = 0
    share_balance = 0
    customer_account = None
    share_transactions = SharesTransaction.objects.filter(shareholder__customer=customer).order_by('id')
    if share_transactions:
        for share_transaction in share_transactions:
            if share_transaction.transaction_type == 'withdrawal' or share_transaction.transaction_type == 'transfer-out':
                dr_amount += share_transaction.system_transaction.amount
            if share_transaction.transaction_type == 'purchase' or share_transaction.transaction_type == 'transfer-in':
                cr_amount += share_transaction.system_transaction.amount
    share_balance = cr_amount - dr_amount

    if subscriber_data['is_subscribed'] and save_trans:
        customer_account = get_customer_sms_charge_account(customer)
        if customer_account:
            account_balance_data = SavingAccountSerializer(customer_account).data
            account_balance = account_balance_data['account_balance']['balance_raw'] - subscriber_data["charge"]
            if subscriber_data['charged_to'] == 'member':
                subscriber_data["debit_chart"] = customer_account.account_product.accounts_chart
            subscriber_data["branch"] = OrganisationBranch.objects.get(pk=branch_id)
            subscriber_data["customer_account"] = customer_account

        if ((((account_balance >= 0) or sms_key in sms_key_suscriptions["allow_over_draw"]) and subscriber_data['charged_to'] == 'member') or subscriber_data['charged_to'] == 'sacco') and sms_key in sms_key_suscriptions["shares_sms_types"]:
            # Share Balance
            if sms_key != "share_dividends_sms":
                f_amount = f"{float(save_trans.system_transaction.amount):,}"
                f_balance = f"{share_balance:,}"

            if sms_key == "purchase_share_sms":
                sms_msg = 'Dear ' + customer.name.capitalize() + ', Share Purchase UGX: ' + f_amount + '. Balance UGX: ' + f_balance + '\n' + subscriber_data["branch"].branch_organisation.name
            
            if sms_key == "share_withraw_sms":
                sms_msg = 'Dear ' + customer.name.capitalize() + ', you have withdrawn UGX: ' + f_amount + 'to A/C: ' + customer_account.account_no + '. Balance UGX: ' + f_balance + '\n' + subscriber_data["branch"].branch_organisation.name
            
            if sms_key == "share_transfer_sms":
                sms_msg = sms_msg + '. Balance: ' + f_balance + '\n ' + subscriber_data["branch"].branch_organisation.name
            
            if sms_key == "share_dividends_sms":
                new_sms_msg = sms_msg
                name = customer_account.account_customer.name.capitalize()
                sms_msg = 'Dear ' + name + ', ' + new_sms_msg + '\n' + subscriber_data["branch"].branch_organisation.name
            
            subscriber_data["message"] = sms_msg
            subscriber_data["sent_by"] = user
            subscriber_data["sms_unique_key"] = ''
            save_user_sms(subscriber_data)
            

def send_customer_loan_sms(sms_key, customer, user, branch_id, sms_msg='', loan=None):
    account_balance = -1
    customer_account = None
    branch = OrganisationBranch.objects.get(pk=branch_id)
    subscriber_data = is_customer_subscriber(sms_key, customer, branch_id)
    if loan and loan.loan_group:
        subscriber_data = is_customer_subscriber(sms_key, loan.customer, branch_id)
    subscriber_data["branch"] = branch
    if subscriber_data['is_subscribed']:
        customer_account = get_customer_sms_charge_account(customer)
        if loan.loan_group:
            customer_account = get_customer_sms_charge_account(loan.loan_group)
        if customer_account:
            account_balance_data = SavingAccountSerializer(customer_account).data
            account_balance = account_balance_data['account_balance']['balance_raw'] - subscriber_data["charge"]
            if subscriber_data['charged_to'] == 'member':
                subscriber_data["debit_chart"] = customer_account.account_product.accounts_chart
            subscriber_data["customer_account"] = customer_account

        if ((((account_balance >= 0) or sms_key in sms_key_suscriptions["allow_over_draw"]) and subscriber_data['charged_to'] == 'member') or subscriber_data['charged_to'] == 'sacco') and sms_key in sms_key_suscriptions["loan_sms_types"]:
            subscriber_data["message"] = sms_msg
            subscriber_data["sent_by"] = user
            subscriber_data["sms_unique_key"] = ''
            response = save_user_sms(subscriber_data)
            if response['status'] and response['transaction']:
                saved_transaction = SavingAccountTransactions.objects.filter(transaction=response['transaction']).first()
                if loan and loan.loan_group and saved_transaction:
                    membership = GroupMembership.objects.filter(member=loan.customer, group=loan.loan_group, active=True).first()
                    if membership:
                        group_trans_field = {
                            "membership": membership,
                            "savings": saved_transaction
                        }
                        GroupSavingTransaction.objects.create(**group_trans_field)

def send_customer_mm_banking_sms(sms_key, customer, telephone, user, branch_id, sms_msg, sms_unique_key):
    subscriber_data = is_customer_subscriber(sms_key, customer, branch_id)
    customer_account = None
    branch = OrganisationBranch.objects.get(pk=branch_id)
    response_msg = ''
    if subscriber_data['is_subscribed']:
        customer_account = get_customer_sms_charge_account(customer)
        if customer_account or subscriber_data['charged_to'] == 'sacco':
            if subscriber_data['charged_to'] == 'member':
                subscriber_data["debit_chart"] = customer_account.account_product.accounts_chart
            subscriber_data["branch"] = branch
            subscriber_data["customer_account"] = customer_account
            subscriber_data["message"] = sms_msg
            subscriber_data["sent_by"] = user
            subscriber_data["sms_unique_key"] = sms_unique_key
            subscriber_data["telephone"] = telephone
            response = save_user_sms(subscriber_data)
            if customer.id != customer_account.account_customer.id and response['status']:
                saved_transaction = SavingAccountTransactions.objects.filter(transaction=response['transaction']).first()
                membership = GroupMembership.objects.filter(member=customer, group=customer_account.account_customer, active=True).first()
                if membership and saved_transaction:
                    group_trans_field = {
                        "membership": membership,
                        "savings": saved_transaction
                    }
                    GroupSavingTransaction.objects.create(**group_trans_field)
            return response
        else:
            response_msg = "Customer savings account not found"
    else:
        response_msg = "Customer not subscribed to this message type"
    return {"status": False, "comment": response_msg, "transaction": None}

def send_mm_pin(subscription, sms_text, user):
    customer = subscription.customer
    if customer:
        org_sub = OrganisationSmsSubscription.objects.filter(
            sms_type__sms_type_key='mm_welcome_sms', organisation=customer.customer_branch.branch_organisation, is_subscribed=True).first()
        if org_sub:
            member_sms_sub = MemberSmsSubscription.objects.filter(
                org_subscription=org_sub, customer=customer).first()
            if not member_sms_sub:
                data = {"customer": customer, "org_subscription": org_sub, "sub_added_by": user, "sub_last_updated_by": user, "is_subscribed": True}
                MemberSmsSubscription.objects.create(**data)
            send_customer_mm_banking_sms('mm_welcome_sms', customer, subscription.telephone_no, user, customer.customer_branch.id, sms_text, '')

def send_customer_single_sms(customer, user, branch_id, sms_msg, sms_unique_key):
    subscriber_data = is_customer_subscriber('customer_single_sms', customer, branch_id)
    account_balance = 0
    customer_account = None
    branch = OrganisationBranch.objects.get(pk=branch_id)
    response_msg = ''
    if subscriber_data['is_subscribed']:
        customer_account = get_customer_sms_charge_account(customer)
        if customer_account or subscriber_data['charged_to'] == 'sacco':
            if subscriber_data['charged_to'] == 'member':
                subscriber_data["debit_chart"] = customer_account.account_product.accounts_chart
            subscriber_data["branch"] = branch
            subscriber_data["customer_account"] = customer_account

            if ((account_balance >= 0 and subscriber_data['charged_to'] == 'member') or subscriber_data['charged_to'] == 'sacco'):
                subscriber_data["message"] = sms_msg
                subscriber_data["sent_by"] = user
                subscriber_data["sms_unique_key"] = sms_unique_key
                response = save_user_sms(subscriber_data)
                return response
        else:
            response_msg = "Customer savings account not found"
    else:
        response_msg = "Customer not subscribed to this message type"
    return {"status": False, "comment": response_msg, "transaction": None}
    
def send_non_subscriber_single_sms(customer, user, branch_id, sms_msg='', unique_key=''):
    recieved_by_type = 'external'
    recieved_by = 0
    if customer:
        recieved_by_type = 'customer'
        recieved_by = customer.id
    subscriber_data = {
        "sms_type": None,
        "charge": 0,
        "charged_to": 'sacco',
        "telephone": customer.telephone,
        "recieved_by": recieved_by,
        "is_subscribed": False,
        "debit_chart": None,
        "free_sms": 0,
        "recieved_by_type": recieved_by_type
    }
    branch = OrganisationBranch.objects.get(pk=branch_id)
    subscriber_data["message"] = sms_msg
    subscriber_data["sent_by"] = user
    subscriber_data["branch"] = branch
    organisation_sms_subscription = OrganisationSmsSubscription.objects.filter(organisation=branch.branch_organisation, is_subscribed=True,
                                                                             sms_type__sms_type_key='customer_single_sms', sms_type__status='active').first()
    if organisation_sms_subscription:
        subscriber_data["sms_unique_key"] = unique_key
        subscriber_data["sms_type"] = organisation_sms_subscription.sms_type
        subscriber_data["charge"] = organisation_sms_subscription.charge
        response = save_user_sms(subscriber_data)
        return response
    else:
        return {"status": False, "comment": 'Sacco not subscribed to this message type', "transaction": None}

def send_external_single_sms(telephone, user, branch_id, sms_msg='', unique_key=''):
    subscriber_data = {
        "sms_type": None,
        "charge": 0,
        "charged_to": 'sacco',
        "telephone": telephone,
        "reciever_name": telephone,
        "recieved_by": 0,
        "is_subscribed": False,
        "debit_chart": None,
        "free_sms": 0,
        "recieved_by_type": 'external'
    }
    branch = OrganisationBranch.objects.get(pk=branch_id)
    subscriber_data["message"] = sms_msg
    subscriber_data["sent_by"] = user
    subscriber_data["branch"] = branch
    organisation_sms_subscription = OrganisationSmsSubscription.objects.filter(
        organisation=branch.branch_organisation,
        is_subscribed=True,
        sms_type__sms_type_key='customer_single_sms',
        sms_type__status='active'
    ).first()
    if organisation_sms_subscription:
        subscriber_data["sms_unique_key"] = unique_key
        subscriber_data["sms_type"] = organisation_sms_subscription.sms_type
        subscriber_data["charge"] = organisation_sms_subscription.charge
        response = save_user_sms(subscriber_data)
        return response
    else:
        return {"status": False, "comment": 'Sacco not subscribed to this message type', "transaction": None}

def send_otp_pin(subscriber_data):
    organisation_sms_subscription = OrganisationSmsSubscription.objects.filter(organisation=subscriber_data["branch"].branch_organisation, is_subscribed=True,
                                                                             sms_type__sms_type_key='2fa_sms', sms_type__status='active').first()
    if not organisation_sms_subscription:
        sms_charge = SMSTypes.objects.filter(sms_type_key='2fa_sms').first()
        data = {"sms_type": sms_charge, "organisation": subscriber_data["branch"].branch_organisation, "charge": sms_charge.base_fee,
                "added_by": subscriber_data["sent_by"], "last_updated_by": subscriber_data["sent_by"], "is_subscribed": True}
        organisation_sms_subscription = OrganisationSmsSubscription.objects.create(**data)

    if organisation_sms_subscription:
        subscriber_data["sms_type"] = organisation_sms_subscription.sms_type
        subscriber_data["charge"] = organisation_sms_subscription.charge
        response = save_user_sms(subscriber_data)
        return response
    else:
        return {"status": False, "comment": 'Sacco not subscribed to this message type', "transaction": None}
        
def is_customer_subscriber(sms_type_key, customer, branch_id):
    sms_data = {
        "sms_type": None,
        "charge": 0,
        "charged_to": None,
        "telephone": customer.telephone,
        "recieved_by": customer.id,
        "is_subscribed": False,
        "debit_chart": None,
        "free_sms": 0,
        "recieved_by_type": 'customer'
    }
    branch = OrganisationBranch.objects.get(pk=branch_id)

    subscriber = MemberSmsSubscription.objects.filter(
        customer=customer, is_subscribed=True, org_subscription__is_subscribed=True,
        org_subscription__sms_type__sms_type_key=sms_type_key, org_subscription__sms_type__status='active').first()
    
    # If member, compute the charge 
    if subscriber:
        sms_data['sms_type'] = subscriber.org_subscription.sms_type
        sms_data['is_subscribed'] = subscriber.is_subscribed
        charge = subscriber.org_subscription.charge
        sms_data['charged_to'] = subscriber.org_subscription.charged_to
        if charge < 0:
            charge = 0
        sms_data['charge'] = charge
       
    # If organisation, don't charge i.e charge  equals to zero
    if sms_data['charged_to'] != 'member':
        sms_data['charge'] = 0
    
    # If sms diabled at organisation level, disable sending sms
    is_sms_on = OrganisationSetting.objects.filter(org_setting__id=branch.branch_organisation.id, setting_key="is_sms_on", setting_value="on").first()
    if not is_sms_on:
        sms_data['is_subscribed'] = False

    return sms_data

def save_user_sms(sms_data):
    branch = sms_data["branch"]
    sms_balances = get_current_sms_balance(branch.branch_organisation.id)
    sms_balance = sms_balances['balance']
    base_sms_cost = sms_balances["base_sms_cost"]
    charged_to = sms_data["charged_to"]
    sms_charge_cost = sms_data["charge"]
    is_free = False
    if sms_data['recieved_by_type'] == 'customer' and charged_to == 'member':
        # If send sms to customer check if the customer has free sms left
        free_sms_balance = get_customer_sms_balance(sms_data["recieved_by"])
        if free_sms_balance['balance'] > 0:
            is_free = True    
    # validate sacco top up balance remaining
    if float(sms_balance) >= float(base_sms_cost):
        user_sms_data = {
            "message": sms_data["message"],
            "sms_type": sms_data["sms_type"],
            "telephone": sms_data["telephone"][0:50],
            "reciever_name": sms_data["telephone"][0:255],
            "recieved_by": sms_data["recieved_by"],
            "sms_cost": sms_charge_cost,
            "base_cost": base_sms_cost,
            "branch": sms_data["branch"],
            "recieved_by_type": sms_data["recieved_by_type"],
            "is_free": is_free,
            "sms_unique_key": sms_data["sms_unique_key"]
        }
        if charged_to != 'member':
            user_sms_data["sms_cost"] = base_sms_cost
        
        if sms_data['recieved_by_type'] == 'customer':
            user_sms_data['reciever_name'] = Customer.objects.get(pk=sms_data["recieved_by"]).name
        else:
            user_sms_data['reciever_name'] = sms_data['reciever_name']

        user_sms = UserSms.objects.create(**user_sms_data)
        if user_sms:
            response = send_sms(user_sms.telephone, user_sms.message)
            if response['status']:
                user_sms.is_sent = True
                user_sms.save()
                if charged_to == 'member':
                    sms_trans_data = {
                        "recieved_by": sms_data["recieved_by"],
                        "charge": sms_charge_cost,
                        "branch": sms_data["branch"],
                        "sms_type": sms_data["sms_type"],
                        "sent_by": sms_data["sent_by"].id,
                        "debit_chart": sms_data["debit_chart"],
                        "recieved_by_type": sms_data["recieved_by_type"],
                    }
                    # If  customer has free sms, don't charge them i.e. ignore charge transactions
                    if sms_data["customer_account"] and is_free == False:
                        account_balance_data = SavingAccountSerializer(sms_data["customer_account"]).data
                        sms_trans_data['debit_chart'] = sms_data["customer_account"].account_product.accounts_chart
                        account_balance = account_balance_data['account_balance']['balance_raw'] - sms_charge_cost
                        if account_balance >= 0:
                            sms_trans_data["customer_account"] = sms_data["customer_account"]
                            sms_transaction = save_user_sms_transaction(sms_trans_data)
                            if sms_transaction:
                                sms_user_trans = {"transaction": sms_transaction, "user_sms": user_sms}
                                UserSmsTransactions.objects.create(**sms_user_trans)
                                sms_charge_fields = {"customer_account": sms_data["customer_account"], "transaction": sms_transaction, "transaction_type": "sms_charge"}
                                SavingAccountTransactions.objects.create(**sms_charge_fields)
                            return {"status": True, "comment": '', "transaction": sms_transaction}
                        elif sms_data["sms_type"].sms_type_key in sms_key_suscriptions["allow_over_draw"]:
                            heading = 'SMS Charge: ' + sms_data['sms_type'].sms_type_name
                            credit_income_from_sms_chart = get_chart_of_account_by_code('sys-4221', sms_data["branch"].branch_organisation)
                            booking_fields = {
                                "heading": heading,
                                "amount": sms_charge_cost,
                                "booking_type": 'income',
                                "chart": credit_income_from_sms_chart,
                                "account": sms_data["customer_account"],
                                "record_date": datetime.now(),
                                "added_by": sms_data["sent_by"].id,
                            }
                            AccountBookings.objects.create(**booking_fields)
                    
                return {"status": True, "comment": '', "transaction": None}
            else:
                user_sms.comment = response['comment']
                user_sms.save()
                return {"status": False, "comment": response['comment'], "transaction": None}
    else:
        return {"status": False, "comment": "Insufficient balance", "transaction": None}

def get_current_sms_balance(organisation_id):
    base_sms_cost = 50
    total_purchase = 0
    total_sms_used = 0
    total_free_sms = 0
    base_sms_cost_obj = OrganisationSetting.objects.filter(org_setting__id=organisation_id, setting_key='unit_sms_cost').first()
    if base_sms_cost_obj:
        base_sms_cost = float(base_sms_cost_obj.setting_value)

    purchase_sum = SMSRequest.objects.filter(organisation__id=organisation_id, status="approved").aggregate(total=Sum('approved_amount'))['total']
    if purchase_sum:
        total_purchase = purchase_sum
    sms_free_sum = OrganisationFreeSmsAward.objects.filter(organisation__id=organisation_id).aggregate(total=Sum('amount'))['total']
    if sms_free_sum:
        total_free_sms = sms_free_sum
    sms_used_sum = UserSms.objects.filter(branch__branch_organisation__id=organisation_id, is_sent=True).aggregate(total=Sum('base_cost'))['total']
    if sms_used_sum:
        total_sms_used = sms_used_sum
    return {"total_purchase": (total_purchase + total_free_sms), "total_sms_used": total_sms_used, "balance": (total_purchase + total_free_sms) - total_sms_used, "base_sms_cost": base_sms_cost}

def send_sms(telephone, message):
    if len(telephone) >= 9:
        telephone = format_phone_number(telephone)

    if len(telephone) == 12:
        # Check if SMS URL is configured
        if not settings.QB_SMS_URL:
            print(f"SMS URL not configured. Message to {telephone}: {message}")
            return {"status": False, "comment": "SMS URL not configured"}
        
        try:
            sms_url = (settings.QB_SMS_URL or "").strip()

            def _post_sms(url):
                return requests.post(
                    url,
                    data={"to": telephone, "message": message},
                    verify=certifi.where(),
                    timeout=20,
                )

            # Use certifi CA bundle explicitly to avoid failures from bad system/env CA paths.
            response = _post_sms(sms_url)

            # Common production issue: endpoint configured without trailing slash.
            if response.status_code == 404 and sms_url and not sms_url.endswith('/'):
                response = _post_sms(f"{sms_url}/")

            if response.status_code in [201, 200]:
                print(f"SMS sent successfully to {telephone}")
                return {"status": True, "comment": ""}
            else:
                provider_msg = (response.text or "").strip().replace("\n", " ")
                provider_msg = provider_msg[:160]
                print(f"SMS failed to {telephone}: HTTP {response.status_code} - {provider_msg}")
                if provider_msg:
                    return {"status": False, "comment": f"HTTP {response.status_code}: {provider_msg}"}
                return {"status": False, "comment": f"HTTP {response.status_code}: Message not sent"}
        except requests.exceptions.RequestException as e:
            print(f"SMS request failed to {telephone}: {str(e)}")
            return {"status": False, "comment": f"Request failed: {str(e)}"}
        except Exception as e:
            print(f"SMS error to {telephone}: {str(e)}")
            return {"status": False, "comment": f"Error: {str(e)}"}
    else:
        print(f"Invalid phone number format: {telephone}")
        return {"status": False, "comment": "Invalid phone number."}

def save_user_sms_transaction(trans_data):
    if trans_data['recieved_by_type'] == 'customer':
        customer = Customer.objects.get(pk=trans_data['recieved_by'])
        branch = trans_data['branch']
        sms_transaction = None

        account_organisation = branch.branch_organisation
        # InterBranch chart
        interbranch_chart = get_inter_branch_chart(branch, trans_data["customer_account"].customer_branch)

        credit_income_from_sms_chart = get_chart_of_account_by_code('sys-4221', account_organisation)
        reference_no = generate_reference_no(credit_income_from_sms_chart.account_line, account_organisation.id)
        heading = 'SMS Charge: ' + trans_data['sms_type'].sms_type_name + ' for ' + customer.member_number + '-' + customer.name
        added_by = User.objects.filter(id=trans_data['sent_by']).first()
        
        sms_charge_fields = {
            "heading": heading,
            "coment": heading,
            "amount": trans_data['charge'],
            "credit_chart": credit_income_from_sms_chart,
            "debit_chart": trans_data['debit_chart'],
            "reference_no": reference_no,
            "payment_method": 'offset',
            "record_date": datetime.now(),
            "added_by": added_by,
            "branch": branch,
        }

        if trans_data["customer_account"]:
            heading = 'SMS Charge: ' + trans_data['sms_type'].sms_type_name + ' for ' + customer.member_number + '-' + customer.name
            if branch.id != trans_data["customer_account"].customer_branch.id:
                heading = 'Inter-branch SMS Charge: ' + trans_data['sms_type'].sms_type_name + ' for ' + customer.member_number + '-' + customer.name
                sms_charge_fields["heading"] = heading
                sms_charge_fields["branch"] = branch
                sms_charge_fields["debit_chart"] = interbranch_chart

        sms_transaction = SystemTransactions.objects.create(**sms_charge_fields)
        if sms_transaction:
            # Handle inter-branch transactions update  -> destination branch 
            if branch.id != trans_data["customer_account"].customer_branch.id:
                sms_charge_fields["credit_chart"] = interbranch_chart
                sms_charge_fields["branch"] = trans_data["customer_account"].customer_branch
                sms_charge_fields["debit_chart"] = trans_data['debit_chart']
                sms_charge_fields["payment_method"] = 'settlement'
                inter_branch_trans = SystemTransactions.objects.create(**sms_charge_fields)
                    
                # Reconcile inter-branch transactions
                if inter_branch_trans:
                    inter_branch_trans_field = {
                        "source_transaction": sms_transaction,
                        "destination_transaction": inter_branch_trans,
                        "added_by": User.objects.get(pk=trans_data['sent_by']),
                    }
                    InterBranchTransactions.objects.create(**inter_branch_trans_field)
                    sms_transaction = inter_branch_trans
    return sms_transaction


def get_customer_sms_balance(customer_id):
    free_sms_balance = {
        'awarded': 0,
        'used': 0,
        'balance': 0,
    }
    total_awarded = 0  # CustomerFreeSmsAward.objects.filter(customer__id=customer_id).aggregate(total=Sum('sms_count'))['total']
    if total_awarded:
        free_sms_balance['awarded'] = total_awarded

    total_used = UserSms.objects.filter(recieved_by=customer_id, recieved_by_type="customer", is_free=True).count()
    if total_used:
        free_sms_balance['used'] = total_used
    
    free_sms_balance['balance'] = free_sms_balance['awarded'] - free_sms_balance['used']
    
    return free_sms_balance

def execute_custom_query(query, parameters):
    rows = []
    with connection.cursor() as cursor:
        cursor.execute(query, parameters)
        rows = cursor.fetchall()
    return rows


def format_phone_number(phone_number):

    # Split the input by '/' or ',' to handle multiple phone numbers
    phone_numbers = re.split(r'[\/,]', phone_number)

    # Get the first phone number from the list
    phone_number = phone_numbers[0].strip()

    # Remove any non-digit characters except the leading +
    phone_number = re.sub(r'\D', '', phone_number.lstrip('+'))

    # Check if the phone number starts with 256
    if phone_number.startswith('256'):
        return phone_number

    # If the phone number starts with 0, replace the 0 with 256
    if phone_number.startswith('0'):
        return '256' + phone_number[1:]

    # Otherwise, just prepend 256
    return '256' + phone_number


def validate_working_hours(user, user_role):
    print("-------------------- start checking 1. ")
    print(user.user_organisation_branch.id)
    print(str(user_role))

    is_working_days_active = WorkingHours.objects.filter(organ_branch=user.user_organisation_branch, is_checked=True)
    if is_working_days_active:
        print("-------------------- start checking 2. ")
        today = datetime.today()
        work_day = today.strftime('%A')
        working_hours = WorkingHours.objects.filter(organ_branch=user.user_organisation_branch, week_days=work_day, is_checked=True).first()
        if working_hours:
            start_time = working_hours.start_time
            end_time = working_hours.end_time
            print("-------------------- start checking 3. ")

            # Get the current time
            current_time = datetime.now().time()
            is_allowed_time = start_time <= current_time < end_time
            print(is_allowed_time)
            print("-------------------- start checking 4. ")
            if not is_allowed_time:
                if not user_role:
                    return False

                # check if role is allowed to work beyond time
                staff_working_hours = StaffWorkingHours.objects.filter(organ_branch=user.user_organisation_branch, user_role=user_role).first()
                print(staff_working_hours)
                print("-------------------- start checking 5. ")
                if not staff_working_hours:
                    return False

                staff_start_time = staff_working_hours.start_time
                staff_end_time = staff_working_hours.end_time
                return staff_start_time <= current_time < staff_end_time

            # check when its already opening time for organisation but not yet for the role/staff...
            if user_role:
                # check if role is allowed to login
                staff_working_hours = StaffWorkingHours.objects.filter(organ_branch=user.user_organisation_branch, user_role=user_role, is_checked=True).first()
                if staff_working_hours:
                    staff_start_time = staff_working_hours.start_time
                    end_time = staff_working_hours.end_time
                    return staff_start_time <= current_time < end_time
            return is_allowed_time
        return False
    return True
