from datetime import datetime
import time
from django.db import models
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin
from django.http import JsonResponse, HttpResponse
from django.utils import timezone
import requests
from django.conf import settings
import random
import base64
import os
import uuid
from cryptography import x509
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicKey
from cryptography.hazmat.backends import default_backend
from requests.auth import HTTPBasicAuth
# Create your models here.

class CustomUserManager(BaseUserManager):
    def create_user(self, password=None,phone=None, role="User", sacco_user_id=None, **extra_fields):
        # if not email:
        #     raise ValueError('The Email field must be set')
        # email = self.normalize_email(email)
        # user = self.model(phone=phone,email=email, sacco_user_id=sacco_user_id, **extra_fields)
        # user.set_password(password)
        # user.save(using=self._db)
        # return user
        if not phone:
            raise ValueError('The Phone field must be set')
        
        # Set `is_staff` and `is_superuser` only if the role is Admin or SuperAdmin
        if role in ["Admin", "SuperAdmin"]:
            extra_fields.setdefault('is_staff', True)
            extra_fields.setdefault('is_active', True)
            extra_fields.setdefault('is_superuser', role == "SuperAdmin")
        else:
            extra_fields.setdefault('is_staff', False)
            extra_fields.setdefault('is_superuser', False)
        user = self.model(phone=phone, sacco_user_id=sacco_user_id, **extra_fields)
        user.set_password(password)
        user.save(using=self._db)
        return user
    
    def create_superuser(self, phone, password=None, **extra_fields):
        extra_fields.setdefault('is_staff', True)
        extra_fields.setdefault('is_superuser', True)
        extra_fields.setdefault('role', CustomUser.Roles.SuperAdmin)
        return self.create_user(phone, password, **extra_fields)

class CustomUser(AbstractBaseUser):
    class Roles(models.TextChoices):
        SuperAdmin = "SuperAdmin", "SuperAdmin"
        Admin = "Admin" , "Admin"
        User = "User", "User"
    class Source(models.TextChoices):
         Kwara = 'Kwara', 'Kwara',
         InternetBanking = 'InternetBanking', 'Internet Banking'

    sacco_user_id = models.CharField(max_length=100, null=False, primary_key=True)
    title=models.CharField(max_length=255,null=True, blank=True)
    name=models.CharField(max_length=255,null=True, blank=True)
    email = models.EmailField(null=True, blank=True)
    phone = models.CharField(max_length=255,null=True, blank=True,unique=True)
    id_number = models.CharField(max_length=255,null=True, blank=True)
    id_type = models.CharField(max_length=255,null=True, blank=True)
    date_of_birth = models.CharField(max_length=255,null=True, blank=True)
    notes=models.CharField(max_length=255,null=True, blank=True)
    region=models.CharField(max_length=255,null=True, blank=True)
    # secondary_phone_number=models.CharField(max_length=255,null=True, blank=True)
    postal_address=models.CharField(max_length=255,null=True, blank=True) 
    physical_address=models.CharField(max_length=255,null=True, blank=True)
    marital_status=models.CharField(max_length=255,null=True, blank=True)
    marital_status_other=models.CharField(max_length=255,null=True, blank=True)
    gender=models.CharField(max_length=255,null=True, blank=True)
    gender_other=models.CharField(max_length=255,null=True, blank=True)
    historical_member_id=models.CharField(max_length=255,null=True, blank=True)
    profession=models.CharField(max_length=255,null=True, blank=True)
    employment_status=models.CharField(max_length=255,null=True, blank=True)
    terms_of_service=models.CharField(max_length=255,null=True, blank=True)
    currently_working=models.CharField(max_length=255,null=True, blank=True)
    joining_fee=models.CharField(max_length=255,null=True, blank=True)
    joining_fee_reference=models.CharField(max_length=255,null=True, blank=True)
    share_capital_contribution=models.CharField(max_length=255,null=True, blank=True)
    share_capital_reference=models.CharField(max_length=255,null=True, blank=True)
    jiinue_account_contribution= models.CharField(max_length=255,null=True,blank=True)
    jiinue_account_reference=models.CharField(max_length=255,blank=True,null=True)
    diaspora_account_contribution=models.CharField(max_length=255,null=True,blank=True)
    diaspora_account_reference=models.CharField(max_length=255, null=True, blank=True)
    boresha_biashara_account_contribution=models.CharField(max_length=255,null=True,blank=True)
    boresha_biashara_account_reference=models.CharField(max_length=255,null=True,blank=True)

    employer=models.CharField(max_length=255,null=True, blank=True)
    employer_phone_number=models.CharField(max_length=255,null=True, blank=True)
    business=models.CharField(max_length=255,null=True, blank=True)
    staff_id=models.CharField(max_length=255,null=True, blank=True)
    kra_pin=models.CharField(max_length=255,null=True, blank=True)
    subscribed_to_mbanking=models.CharField(max_length=255,null=True, blank=True)
    mobile_loan_disallowed=models.CharField(max_length=255,null=True, blank=True)
    referral = models.CharField(max_length=100, blank=True, null=True)
    verify_status = models.BooleanField(default=False)
    otp_digit = models.CharField(max_length=10, blank=True, null=True)
    profile_picture = models.ImageField(upload_to='profile_pictures/', null=True, blank=True)
    signature = models.ImageField(upload_to='signatures/', null=True, blank=True)
    registration_number=models.CharField(max_length=255,null=True, blank=True)
    registration_date=models.CharField(max_length=255,null=True, blank=True)
    business_location=models.CharField(max_length=255,null=True, blank=True)
    business_county=models.CharField(max_length=255,null=True, blank=True)
    company_type=models.CharField(max_length=255,null=True, blank=True)
    member_type=models.CharField(max_length=255,null=True, blank=True)
    member_ID=models.CharField(max_length=255,null=True, blank=True)
    business_description=models.CharField(max_length=255,null=True, blank=True)
    monthly_net_cashflow=models.CharField(max_length=255,null=True, blank=True)
    account_type=models.CharField(max_length=255,null=True, blank=True)
    services=models.CharField(max_length=255,null=True, blank=True)
    role = models.CharField(max_length=255,choices=Roles.choices,default=Roles.User)
    signing_mandate = models.CharField(max_length=255,blank=True,null=True)
    created_at = models.DateTimeField(default=timezone.now)

     # chama_fields
    chama_number_of_members = models.IntegerField(blank=True,null=True)
    # members = models.JSONField(blank=True,null=True)
    chama_name = models.CharField(max_length=255,blank=True, null=True)
    # postal_address already there
    # physical_address already there
    postal_code = models.CharField(max_length=255,blank=True, null=True)
    chama_town = models.CharField(max_length=255, blank=True, null=True)
    chama_telephone_office = models.CharField(max_length=255,blank=True, null=True)
    # email already there
    chama_nature_of_business = models.CharField(max_length=255, blank=True, null=True)
    chama_building = models.CharField(max_length=255,blank=True, null=True)
    chama_date_of_incorporation = models.CharField(max_length=255,blank=True, null=True)
    chama_certificate_of_incorporation_number = models.CharField(max_length=255,blank=True, null=True)
    # kra_pin already there
    chama_associate_company = models.CharField(max_length=255,blank=True, null=True)
    # other_accounts = models.JSONField(max_length=255,blank=True,null=True)
    chama_account_type = models.CharField(max_length=255,blank=True,null=True)
    chama_other_bank_accounts = models.JSONField(max_length=255, blank=True, null=True)
    chama_signing_mandate = models.CharField(max_length=255,blank=True,null=True)
    chama_signature = models.CharField(max_length=255, blank=True,null=True)

    fosa_account = models.TextField(blank=True,null=True)
    is_staff = models.BooleanField(default=False)
    is_superuser = models.BooleanField(default=False)
    is_active = models.BooleanField(default=True)
    kwara_active = models.BooleanField(default=False)
    source = models.CharField(max_length=100,choices=Source.choices,default=Source.InternetBanking)
    USERNAME_FIELD = 'phone'
    REQUIRED_FIELDS = []

    objects = CustomUserManager()

    def __str__(self):
        return f" {self.sacco_user_id} - {self.name}"  # Customize as per your requirement
    
    # Ensure `has_perm` and `has_module_perms` are defined for admin access
    def has_perm(self, perm, obj=None):
        return True

    def has_module_perms(self, app_label):
        return True
    
class ChamaMember(models.Model):
     group  = models.ForeignKey(CustomUser, related_name="members", on_delete=models.CASCADE)
     name = models.CharField(max_length=255)
     id_number = models.CharField(max_length=255)
     phone = models.CharField(max_length=255)
     address = models.CharField(max_length=255)
     position = models.CharField(max_length=255)

class ChamaOfficial(models.Model):
     group  = models.ForeignKey(CustomUser, related_name="officials", on_delete=models.CASCADE)
     name = models.CharField(max_length=255)
     id_number = models.CharField(max_length=255)
     phone = models.CharField(max_length=255)
     address = models.CharField(max_length=255)
     position = models.CharField(max_length=255)

class PassportPhoto(models.Model):
    document = models.ForeignKey('CorporateDocuments', related_name='passport_photos', on_delete=models.CASCADE)
    photo = models.FileField(upload_to='passport_photos/', null=True, blank=True)
    name = models.CharField(max_length=255, null=True, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return f"Passport Photo for {self.document.user.email} at {self.created_at}"
    
class KraPin(models.Model):
    document = models.ForeignKey('CorporateDocuments', related_name='kra_photos', on_delete=models.CASCADE)
    photo = models.FileField(upload_to='kra_photos/', null=True, blank=True)
    name = models.CharField(max_length=255, null=True, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return f"Kra Photo for {self.document.user.email} at {self.created_at}"
    
class BoardResolution(models.Model):
    document = models.ForeignKey('CorporateDocuments', related_name='board_resolutions', on_delete=models.CASCADE)
    file = models.FileField(upload_to='board_resolutions/', null=True, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return f"Board Resolution for {self.document.user.email} at {self.created_at}"
    
class NextOfKin(models.Model):
    user = models.ForeignKey(CustomUser, related_name='next_of_kin', on_delete=models.CASCADE)
    name = models.CharField(max_length=255, blank=False, null=False)
    percentage = models.CharField(max_length=255, blank=False, null=False)
    phone = models.CharField(max_length=255, blank=True, null=True)
    relationship = models.CharField(max_length=100, blank=False, null=False)
    is_deleted = models.BooleanField(default=False)
    created_at = models.DateTimeField(default=timezone.now)

    def __str__(self):
        return f"{self.name} ({self.relationship}) - {self.percentage}%"

class CorporateDocuments(models.Model):
    user = models.ForeignKey(CustomUser, related_name='corp_files', on_delete=models.CASCADE)
    reg_certificate = models.FileField(upload_to='corp_files/', null=True, blank=True)
    # kra_pin_doc = models.FileField(upload_to='corp_files/', null=True, blank=True)
    cr_12 = models.FileField(upload_to='corp_files/', null=True, blank=True)
    created_at = models.DateTimeField(default=timezone.now)

    def __str__(self):
        return f"{self.user}"

class IndividualDocuments(models.Model):
    user = models.ForeignKey(CustomUser, related_name='individual_files', on_delete=models.CASCADE)
    kra_pin_doc = models.FileField(upload_to='individual_files/', null=True, blank=True)
    birth_cert_doc = models.FileField(upload_to='individual_files/', null=True, blank=True)
    national_id = models.FileField(upload_to='individual_files/', null=True, blank=True)
    created_at = models.DateTimeField(default=timezone.now)

    def __str__(self):
        return f"{self.user}"
    
class SigningMandate(models.Model):
    user = models.ForeignKey(CustomUser, related_name='corp_signature', on_delete=models.CASCADE)
    name = models.CharField(max_length=255, blank=False, null=False)
    id_no = models.CharField(max_length=255, blank=True, null=True)
    signature = models.FileField(upload_to='corp_signatures/', null=True, blank=True)
    created_at = models.DateTimeField(default=timezone.now)

    def __str__(self):
        return f"{self.name}"
    
class ChamaSigningMandate(models.Model):
    user = models.ForeignKey(CustomUser, related_name='chama_signature_mandate', on_delete=models.CASCADE)
    first_name = models.CharField(max_length=255, blank=False, null=False)
    surname = models.CharField(max_length=255,blank=False,null=False)
    designation = models.CharField(max_length=255)
    id_number = models.CharField(max_length=255)
    mobile_no = models.CharField(max_length=255)
    physical_address = models.CharField(max_length=255)
    postal_address = models.CharField(max_length=255)
    postal_code = models.CharField(max_length=255)
    town = models.CharField(max_length=255)
    email = models.CharField(max_length=255)
    signature = models.FileField(upload_to='chama_signatures/', null=True, blank=True)
    passport_photo = models.FileField(upload_to='chama_passport_photo/', null=True, blank=True)
    created_at = models.DateTimeField(default=timezone.now)
    updated_at = models.DateTimeField(default=timezone.now)

class SavingsAccount(models.Model):
    user = models.ForeignKey(CustomUser, related_name='savings_account', on_delete=models.CASCADE)
    name = models.CharField(max_length=255, null=True, blank=True)

    def __str__(self):
        return f"{self.name}"
    
class UserActivityLog(models.Model):
    user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
    activity = models.CharField(max_length=255,null=True, blank=True) # Describe the user activity
    api_endpoint = models.CharField(max_length=255,null=True, blank=True)  # API path accessed
    timestamp = models.DateTimeField(default=timezone.now)  # Timestamp of activity
    is_archived = models.BooleanField(default=False)  # New field to track archived logs
    created_at = models.DateTimeField(auto_now_add=True)  # Time the log was created

    def __str__(self):

        return f"User {self.user.sacco_user_id} - {self.user.name} - Activity {self.activity}"
    
class SMSLog(models.Model):
    phone = models.CharField(max_length=20)  # Store phone number
    message = models.TextField()  # Store the message content
    response_status = models.CharField(max_length=50, blank=True, null=True)  # Response from API
    response_message = models.TextField(blank=True, null=True)  # Any additional response message
    timestamp = models.DateTimeField(auto_now_add=True)  # When the SMS was sent
    department = models.CharField(max_length=50, blank=True, null=True)  # Department sending the SMS
    is_archived = models.BooleanField(default=False)  # New field to track archived logs
    created_at = models.DateTimeField(auto_now_add=True, null=True)  # Time the log was created

    def __str__(self):
        return f"SMS to {self.phone} at {self.timestamp}"
    
class LoanApplication(models.Model):
    class Status(models.TextChoices):
        PendingAppraisal = "PendingAppraisal" , "Pending Appraisal"
        Appraised = "Appraised", "Appraised"
        Rejected = "Rejected", "Rejected"
    account_holder_id = models.ForeignKey(CustomUser, related_name='loan_application', on_delete=models.CASCADE, to_field='sacco_user_id', null=True)
    product_id = models.CharField(max_length=255)
    amount = models.DecimalField(max_digits=15, decimal_places=2)
    repayment_installments = models.IntegerField(blank=True, null=True)
    repayment_period = models.IntegerField(blank=True, null=True)
    repayment_period_unit = models.CharField(max_length=50, blank=True, null=True)
    anticipated_disbursement_date = models.CharField(max_length=255,null=True, blank=True)
    first_repayment_date = models.CharField(max_length=255,null=True, blank=True)
    disbursement_mode = models.CharField(max_length=50, blank=True, null=True)
    repayment_mode = models.CharField(max_length=100, blank=True, null=True)
    full_name = models.CharField(max_length=255, blank=True, null=True)
    idNumber = models.CharField(max_length=100, blank=True, null=True)
    dateOfBirth = models.CharField(max_length=255,null=True, blank=True)
    gender = models.CharField(max_length=10, blank=True, null=True)
    physical_address = models.CharField(max_length=255, blank=True, null=True)
    postal_address = models.CharField(max_length=255, blank=True, null=True)
    marital_status = models.CharField(max_length=20, blank=True, null=True)
    email = models.CharField(max_length=255,null=True, blank=True)
    phone = models.CharField(max_length=20, blank=True, null=True)
    telephone = models.CharField(max_length=20, blank=True, null=True)
    kra_pin = models.CharField(max_length=100, blank=True, null=True)
    employment_status = models.CharField(max_length=100, blank=True, null=True)
    employer = models.CharField(max_length=255, blank=True, null=True)
    staff_id = models.CharField(max_length=100, blank=True, null=True)
    work_email = models.EmailField(blank=True, null=True)
    work_postal_address = models.CharField(max_length=255, blank=True, null=True)
    position = models.CharField(max_length=255, blank=True, null=True)
    terms_of_service = models.CharField(max_length=20, blank=True, null=True)
    dateOfEmployement = models.CharField(max_length=20, blank=True, null=True)
    business = models.CharField(max_length=255, blank=True, null=True)
    business_email = models.CharField(max_length=255,null=True, blank=True)
    business_address = models.CharField(max_length=255, blank=True, null=True)
    business_location = models.CharField(max_length=255, blank=True, null=True)
    business_activity = models.CharField(max_length=255, blank=True, null=True)
    business_ownership = models.CharField(max_length=50, blank=True, null=True)
    loan_purpose = models.CharField(max_length=50,null=True, blank=True)
    sacco_loan_to_be_cleared = models.CharField(max_length=50,null=True, blank=True)
    processing_fee = models.DecimalField(max_digits=10, blank=True, null=True, decimal_places=2)
    processing_fee_reference = models.CharField(max_length=20, blank=True, null=True)
    created_at = models.DateTimeField(auto_now_add=True, blank=True, null=True)
    status = models.CharField(max_length=255,choices=Status.choices,default=Status.PendingAppraisal)
    rejection_comment = models.CharField(max_length=255,null=True,blank=True)

    def __str__(self):
        return f"Loan Application for {self.account_holder_id}"
    
    class Meta:
        unique_together = ('account_holder_id', 'product_id')

class Guarantor(models.Model):
    loan_application = models.ForeignKey(LoanApplication, related_name='guarantors', on_delete=models.CASCADE)
    guarantor_member_id = models.CharField(max_length=40, blank=True, null=True)
    guarantor_type = models.CharField(max_length=10, blank=True, null=True)
    guarantor_name = models.CharField(max_length=100)
    guarantor_phone = models.CharField(max_length=20)
    sender_name = models.CharField(max_length=255, null=True,blank=True)
    approved = models.BooleanField(default=False)
    rejected = models.BooleanField(default=False)
    confirming_id = models.CharField(max_length=255,null=True,blank=True)
    product_name = models.CharField(max_length=255,null=True,blank=True)
    rejection_comment = models.CharField(max_length=255,null=True,blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True, blank=True,null=True)

    def __str__(self):
        return f"Guarantor {self.guarantor_name} for Loan {self.product_name}"
    
class Security(models.Model):
    SECURITY_TYPES = (
        ('Title Deed', 'Title Deed'),
        ('Cash Collateral', 'Cash Collateral'),
        ('NSE Shares', 'NSE Shares'),
        ('Other', 'Other')
    )
    loan_application = models.ForeignKey(LoanApplication, related_name='securities', on_delete=models.CASCADE)
    security_type = models.CharField(max_length=50, choices=SECURITY_TYPES)
    reference_number = models.CharField(max_length=20, blank=True, null=True)
    description = models.TextField()
    value = models.DecimalField(max_digits=15, decimal_places=2)

    def __str__(self):
        return f"Security - {self.security_type}"

class LoanOriginationDocuments(models.Model):
    loan_application = models.ForeignKey(LoanApplication, related_name='LoanOriginationDocuments', on_delete=models.CASCADE)
    passport_photo = models.FileField(upload_to='documents/passport_photos/', null=True, blank=True)
    signature = models.FileField(upload_to='documents/signatures/', null=True, blank=True)
    payslip = models.FileField(upload_to='documents/payslips/', null=True, blank=True)
    # bank_statements = models.FileField(upload_to='documents/bank_statements/', null=True, blank=True)
    bank_statements = models.ManyToManyField('Document', related_name='bank_statements', blank=True)
    # mpesa_statements = models.FileField(upload_to='documents/mpesa_statements/', null=True, blank=True)
    mpesa_statements = models.ManyToManyField('Document', related_name='mpesa_statements', blank=True)
    utility_bill = models.FileField(upload_to='documents/utility_bills/', null=True, blank=True)
    # cash_flow_analysis = models.FileField(upload_to='documents/cash_flow_analysis/', null=True, blank=True)
    cash_flow_analysis = models.ManyToManyField('Document', related_name='cash_flow_analysis', blank=True)
    repayment_document = models.FileField(upload_to='documents/repayment_documents/', null=True, blank=True)

    def __str__(self):
        return f"Loan Origination Documents for - {self.loan_application}"
    
class Document(models.Model):
    file = models.FileField(upload_to='documents/')
    uploaded_at = models.DateTimeField(auto_now_add=True)
    loan_application = models.ForeignKey('LoanApplication', null=True, blank=True, on_delete=models.CASCADE)
    
class SavingsApplication(models.Model):
    account_holder_id = models.ForeignKey(CustomUser, related_name='savings_application', on_delete=models.CASCADE, to_field='sacco_user_id', null=True)
    product_id = models.CharField(max_length=10)
    name = models.CharField(max_length=50)
    product_name = models.CharField(max_length=50, blank=True, null=True)
    term = models.CharField(max_length=50, blank=True, null=True)
    monthly_remittance_amount = models.DecimalField(max_digits=10, decimal_places=2)
    created_at = models.DateTimeField(auto_now_add=True, blank=True, null=True)

    def __str__(self):
        return f"Savings Application for {self.account_holder_id}"
    
    class Meta:
        unique_together = ('account_holder_id', 'product_id')

class ContactMessage(models.Model):
    email = models.EmailField()
    subject = models.CharField(max_length=255)
    message = models.TextField()
    sent_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return f"Message from {self.email} - {self.subject}"
    
# Risk Claim Models
class SpouseClaim(models.Model):
    sacco_user = models.ForeignKey('CustomUser', on_delete=models.CASCADE, related_name='spouse_claims', blank=True, null=True)
    member_Number = models.CharField(max_length=100)
    member_name = models.CharField(max_length=255)
    PM_relation = models.CharField(max_length=100)
    deceased_PM_relation = models.CharField(max_length=100)
    memberID = models.CharField(max_length=100)
    ID_file = models.FileField(upload_to='SpouseClaim/id_files/')
    employer = models.CharField(max_length=255)
    DateofBirth = models.DateField()
    spouse_name = models.CharField(max_length=255)
    spouse_ID = models.CharField(max_length=100)
    spouse_ID_file = models.FileField(upload_to='SpouseClaim/spouse_id_files/')
    DeceasedDateofBirth = models.DateField()
    DeceasedDateofDeath = models.DateField()
    deathCertificate = models.CharField(max_length=255)
    deathCertificate_file = models.FileField(upload_to='SpouseClaim/death_certificates/')
    marriageCertificate = models.CharField(max_length=255)
    marriageCertificate_file = models.FileField(upload_to='SpouseClaim/marriage_certificates/')
    claimant_Name = models.CharField(max_length=50, blank=True, null=True)
    claimant_Mobile_No = models.CharField(max_length=50, blank=True, null=True)
    claimant_ID = models.CharField(max_length=50, blank=True, null=True)
    claimant_ID_file = models.FileField(blank=True, null=True, upload_to='SpouseClaim/claimant_ID_file/')
    paymentAccountType = models.CharField(max_length=50, blank=True, null=True)
    bank_name = models.CharField(max_length=255, blank=True, null=True)
    bank_branch = models.CharField(max_length=255, blank=True, null=True)
    account_name = models.CharField(max_length=255, blank=True, null=True)
    account_number = models.CharField(max_length=50, blank=True, null=True)
    created_at = models.DateTimeField(auto_now_add=True)
    status = models.CharField(max_length=50, blank=True, null=True)
    note = models.CharField(max_length=100, blank=True, null=True)

    def __str__(self):
        return self.member_name

class PrincipalClaim(models.Model):
    sacco_user = models.ForeignKey('CustomUser', on_delete=models.CASCADE, related_name='principal_claims', blank=True, null=True)
    member_Number = models.CharField(max_length=100)
    member_name = models.CharField(max_length=255)
    PM_relation = models.CharField(max_length=100)
    deceased_PM_relation = models.CharField(max_length=100)
    memberID = models.CharField(max_length=100)
    ID_file = models.FileField(upload_to='PrincipalClaim/id_files/')
    employer = models.CharField(max_length=255)
    DateofBirth = models.DateField()
    parent = models.CharField(max_length=255)
    parent_name = models.CharField(max_length=100)
    principalBirthCert = models.CharField(max_length=100, blank=True, null=True)
    principalBirthCert_file = models.FileField(blank=True, null=True, upload_to='PrincipalClaim/principal_birth_cert_files/')
    DeceasedDateofDeath = models.DateField()
    deathCertificate = models.CharField(max_length=255)
    deathCertificate_file = models.FileField(upload_to='PrincipalClaim/death_certificates/')
    claimant_Name = models.CharField(max_length=50, blank=True, null=True)
    claimant_Mobile_No = models.CharField(max_length=50, blank=True, null=True)
    claimant_ID = models.CharField(max_length=50, blank=True, null=True)
    claimant_ID_file = models.FileField(blank=True, null=True, upload_to='PrincipalClaim/claimant_ID_file/')
    paymentAccountType = models.CharField(max_length=50, blank=True, null=True)
    bank_name = models.CharField(max_length=255, blank=True, null=True)
    bank_branch = models.CharField(max_length=255, blank=True, null=True)
    account_name = models.CharField(max_length=255, blank=True, null=True)
    account_number = models.CharField(max_length=50, blank=True, null=True)
    created_at = models.DateTimeField(auto_now_add=True)
    status = models.CharField(max_length=50, blank=True, null=True)
    note = models.CharField(max_length=100, blank=True, null=True)

    def __str__(self):
        return self.member_name

class ParentClaim(models.Model):
    sacco_user = models.ForeignKey('CustomUser', on_delete=models.CASCADE, related_name='parent_claims', blank=True, null=True)
    member_Number = models.CharField(max_length=100)
    member_name = models.CharField(max_length=255)
    PM_relation = models.CharField(max_length=100)
    deceased_PM_relation = models.CharField(max_length=100)
    memberID = models.CharField(max_length=100)
    ID_file = models.FileField(upload_to='ParentClaim/id_files/')
    employer = models.CharField(max_length=255)
    DateofBirth = models.DateField()
    parent = models.CharField(max_length=255)
    parent_name = models.CharField(max_length=255)
    parent_ID = models.CharField(max_length=100)
    parent_ID_file = models.FileField(upload_to='ParentClaim/parent_id_files/')
    DeceasedDateofBirth = models.DateField()
    DeceasedDateofDeath = models.DateField()
    deathCertificate = models.CharField(max_length=255)
    deathCertificate_file = models.FileField(upload_to='ParentClaim/death_certificates/')
    principalBirthCert = models.CharField(max_length=255)
    principalBirthCert_file = models.FileField(upload_to='ParentClaim/principal_birth_cert_files/')
    claimant_Name = models.CharField(max_length=50, blank=True, null=True)
    claimant_Mobile_No = models.CharField(max_length=50, blank=True, null=True)
    claimant_ID = models.CharField(max_length=50, blank=True, null=True)
    claimant_ID_file = models.FileField(blank=True, null=True, upload_to='ParentClaim/claimant_ID_file/')
    paymentAccountType = models.CharField(max_length=50, blank=True, null=True)
    bank_name = models.CharField(max_length=255, blank=True, null=True)
    bank_branch = models.CharField(max_length=255, blank=True, null=True)
    account_name = models.CharField(max_length=255, blank=True, null=True)
    account_number = models.CharField(max_length=50, blank=True, null=True)
    created_at = models.DateTimeField(auto_now_add=True)
    status = models.CharField(max_length=50, blank=True, null=True)
    note = models.CharField(max_length=100, blank=True, null=True)

    def __str__(self):
        return self.member_name

class ParentInLawClaim(models.Model):
    sacco_user = models.ForeignKey('CustomUser', on_delete=models.CASCADE, related_name='parent_in_law_claims', blank=True, null=True)
    member_Number = models.CharField(max_length=100)
    member_name = models.CharField(max_length=255)
    PM_relation = models.CharField(max_length=100)
    deceased_PM_relation = models.CharField(max_length=100)
    memberID = models.CharField(max_length=100)
    ID_file = models.FileField(upload_to='ParentInLawClaim/id_files/')
    employer = models.CharField(max_length=255)
    DateofBirth = models.DateField()
    parent = models.CharField(max_length=255)
    parent_in_law_name = models.CharField(max_length=255)
    parent_in_law_ID = models.CharField(max_length=100)
    parent_in_law_ID_file = models.FileField(upload_to='ParentInLawClaim/parent_in_law_id_files/')
    DeceasedDateofBirth = models.DateField()
    DeceasedDateofDeath = models.DateField()
    deathCertificate = models.CharField(max_length=255)
    deathCertificate_file = models.FileField(upload_to='ParentInLawClaim/death_certificates/')
    spouseBirthCert = models.CharField(max_length=255)
    spouseBirthCert_file = models.FileField(upload_to='ParentInLawClaim/spouse_birth_certificates/')
    marriageCertificate = models.CharField(max_length=255)
    marriageCertificate_file = models.FileField(upload_to='ParentInLawClaim/marriage_certificates/')
    claimant_Name = models.CharField(max_length=50, blank=True, null=True)
    claimant_Mobile_No = models.CharField(max_length=50, blank=True, null=True)
    claimant_ID = models.CharField(max_length=50, blank=True, null=True)
    claimant_ID_file = models.FileField(blank=True, null=True, upload_to='ParentInLawClaim/claimant_ID_file/')
    paymentAccountType = models.CharField(max_length=50, blank=True, null=True)
    bank_name = models.CharField(max_length=255, blank=True, null=True)
    bank_branch = models.CharField(max_length=255, blank=True, null=True)
    account_name = models.CharField(max_length=255, blank=True, null=True)
    account_number = models.CharField(max_length=50, blank=True, null=True)
    created_at = models.DateTimeField(auto_now_add=True)
    status = models.CharField(max_length=50, blank=True, null=True)
    note = models.CharField(max_length=100, blank=True, null=True)

    def __str__(self):
        return self.member_name

class DependantClaim(models.Model):
    sacco_user = models.ForeignKey('CustomUser', on_delete=models.CASCADE, related_name='dependant_claims', blank=True, null=True)
    member_Number = models.CharField(max_length=100)
    member_name = models.CharField(max_length=255)
    PM_relation = models.CharField(max_length=100)
    deceased_PM_relation = models.CharField(max_length=100)
    memberID = models.CharField(max_length=100)
    ID_file = models.FileField(upload_to='DependantClaim/id_files/')
    employer = models.CharField(max_length=255)
    DateofBirth = models.DateField()
    dependantName = models.CharField(max_length=255)
    dependantID = models.CharField(max_length=100)
    collegeName = models.CharField(max_length=100)
    collegeAdmissionLetter = models.FileField(upload_to='DependantClaim/college_admission_letters/')
    DeceasedDateofBirth = models.DateField()
    DeceasedDateofDeath = models.DateField()
    deathCertificate = models.CharField(max_length=255)
    deathCertificate_file = models.FileField(upload_to='DependantClaim/death_certificates/')
    birthCertificate = models.CharField(max_length=255,blank=True, null=True)
    birthCertificate_file = models.FileField(blank=True, null=True, upload_to='DependantClaim/birth_certificates/')
    claimant_Name = models.CharField(max_length=50, blank=True, null=True)
    claimant_Mobile_No = models.CharField(max_length=50, blank=True, null=True)
    claimant_ID = models.CharField(max_length=50, blank=True, null=True)
    claimant_ID_file = models.FileField(blank=True, null=True, upload_to='DependantClaim/claimant_ID_file/')
    paymentAccountType = models.CharField(max_length=80, blank=True, null=True)
    bank_name = models.CharField(max_length=255, blank=True, null=True)
    bank_branch = models.CharField(max_length=255, blank=True, null=True)
    account_name = models.CharField(max_length=255, blank=True, null=True)
    account_number = models.CharField(max_length=50, blank=True, null=True)
    created_at = models.DateTimeField(auto_now_add=True)
    status = models.CharField(max_length=50, blank=True, null=True)
    note = models.CharField(max_length=100, blank=True, null=True)

    def __str__(self):
        return self.member_name
    
class InpatientClaim(models.Model):
    sacco_user = models.ForeignKey('CustomUser', on_delete=models.CASCADE, related_name='inpatient_claims', blank=True, null=True)
    member_name = models.CharField(max_length=255)
    memberID = models.CharField(max_length=100)
    employer = models.CharField(max_length=255)
    member_Number = models.CharField(max_length=100)
    employer_branch = models.CharField(max_length=255)
    ID_file = models.FileField(blank=True, null=True, upload_to='InpatientClaim/id_files/')
    employer_tel = models.CharField(max_length=255)
    dateOfAdmission = models.DateField()
    dateOfDischarge = models.DateField()
    hospitalNameAddress = models.CharField(max_length=255)
    memberSignatureFile = models.FileField(upload_to='InpatientClaim/member_signatures/')
    memberAgentSocietyMemberNumber = models.CharField(max_length=255)
    applicationDate = models.DateField()
    witnessSignatureFile = models.FileField(upload_to='InpatientClaim/witness_signatures/')
    witnessSocietyMemberNumber = models.CharField(max_length=255)
    witnessName = models.CharField(max_length=255)
    witnessAddress = models.CharField(max_length=255)
    doctorsComment = models.CharField(max_length=255)
    doctorRubberStampFile = models.FileField(upload_to="InpatientClaim/doctors_rubber_stamps/")
    doctorSignatureFile = models.FileField(upload_to='InpatientClaim/doctors_signatures/')
    doctorSigningDate = models.DateField()
    created_at = models.DateTimeField(auto_now_add=True)
    status = models.CharField(max_length=50, blank=True, null=True)
    note = models.CharField(max_length=100, blank=True, null=True)

    def __str__(self):
         return self.member_name
    
class TransactionLog(models.Model):
     user = models.ForeignKey(CustomUser,on_delete=models.CASCADE)
     transaction_type = models.CharField(max_length=255)
     created_at = models.DateTimeField(auto_now_add=True)
     transaction_id = models.CharField(max_length=255)
     description = models.CharField(max_length=255)
     account_no = models.CharField(max_length=255)
     amount = models.IntegerField()
     status = models.CharField(max_length=255)
     account_name = models.CharField(max_length=255,blank=True,null=True)
    #  member_name = models.CharField(max_length=255)
    #  member_id = models.CharField(max_length=255)

     def __str__(self):

        return f"User {self.user.sacco_user_id} - {self.user.name} - Transaction {self.transaction_type}"
    
class MpesaTransaction(models.Model):
    merchant_request_id = models.CharField(max_length=50)
    checkout_request_id = models.CharField(max_length=50)
    result_code = models.IntegerField()
    result_description = models.CharField(max_length=255)
    amount = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True)
    mpesa_receipt_number = models.CharField(max_length=20, null=True, blank=True)
    transaction_date = models.CharField(max_length=14, null=True, blank=True)
    phone_number = models.CharField(max_length=15, null=True, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return f"{self.phone_number} - {self.amount}"

class MpesaB2CCallback(models.Model):
    result_type = models.IntegerField()
    result_code = models.IntegerField()
    result_desc = models.TextField()
    originator_conversation_id = models.CharField(max_length=50, unique=True)
    conversation_id = models.CharField(max_length=50, unique=True)
    transaction_id = models.CharField(max_length=50, unique=True, null=True, blank=True)
    transaction_amount = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True)
    receiver_party_name = models.CharField(max_length=100, null=True, blank=True)
    transaction_completed_time = models.CharField(max_length=50, null=True, blank=True)

    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return f"{self.transaction_id} - {self.result_desc}"
    
class DepositRequest(models.Model):
    member_sacco_id = models.CharField(max_length=255)
    amount = models.IntegerField()
    request_time = models.DateTimeField(auto_now_add=True)
    payment_method = models.CharField(max_length=255)
    request_response = models.CharField(max_length=255)

class PaypalPayout(models.Model):
     payout_id = models.CharField(max_length=255)
     status = models.CharField(max_length=255)
     receiver = models.CharField(max_length=255)
     currency_sent = models.CharField(max_length=255)
     amount_sent = models.CharField(max_length=255)

class PaypalDeposit(models.Model):
     invoice = models.UUIDField(unique=True)
     amount = models.DecimalField(max_digits=10,decimal_places=2)
     member = models.CharField(max_length=200,null=False,blank=False)
     status = models.CharField(max_length=20,choices=[
          ('Pending', 'Pending'),
          ('Completed', 'Completed'),
          ('Failed', 'Failed'),
     ])
     created_at = models.DateTimeField(auto_now_add=True)
     updated_at = models.DateTimeField(auto_now=True)

class Payment(models.Model):
     member_sacco_id = models.CharField(max_length=255,blank=True,null=True)
     merchant_request_id = models.CharField(max_length=255)
     checkout_request_id = models.CharField(max_length=255)
     response_code = models.CharField(max_length=255)
     response_description = models.CharField(max_length=255)
     customer_message = models.CharField(max_length=255)
     amount = models.IntegerField()
     phone_number = models.CharField(max_length=255)
     account_reference = models.CharField(max_length=255)
     created_at = models.DateTimeField(auto_now_add=True)
     updated_at = models.DateTimeField(auto_now_add=True)
     account_name = models.CharField(max_length=255,blank=True,null=True)

class WithdrawRequest(models.Model):
    # user_id = models.ForeignKey('CustomUser', on_delete=models.CASCADE)
    member_sacco_id = models.CharField(max_length=255)
    amount = models.IntegerField()
    request_time = models.DateTimeField(auto_now_add=True)
    payment_method = models.CharField(max_length=255)
    request_response = models.CharField(max_length=255)
    conversation_id= models.CharField(max_length=255,unique=True,null=True)
    account_no = models.CharField(max_length=255,null=True,blank=True)

    @classmethod
    def create_swift_withdraw_request(cls, data):
        # Obtain access token
        access_token = return_stanbic_auth_token()
        request = data.get("request")

        # Extract necessary fields from data
        amount = data.get('amount')
        to_account = data.get('to_account')
        to_bank = data.get('to_bank')
        creditCurrency = data.get('creditCurrency')

        sacco_member_id = data.get("sacco_member_id")
        # fosa_details = return_fosa_details(request=request, sacco_member_id=sacco_member_id)
        # print("FOsa details from here")
        # print(fosa_details)
        fosa_balance = return_fosa_balance(sacco_member_id=sacco_member_id)
        fosa_account_no = return_fosa_account(sacco_member_id=sacco_member_id)
        if isinstance(fosa_account_no, tuple):
            fosa_account_no = fosa_account_no[0]
            # print("fosa")
            
        # print(fosa_account_no)

        if int(amount) > int(fosa_balance):
            return JsonResponse({"error": "Fosa balance not enough"})

        if not all([to_account, to_bank, amount, creditCurrency]):
            return JsonResponse({"error": "Missing required fields"}, status=400)

        # Stanbic Bank API details
        url = "https://api.connect.stanbicbank.co.ke/api/sandbox/swift-payments/"
        headers = {
            "Authorization": f"Bearer {access_token}",
            "content-type": "application/json",
            "accept": "application/json"
        }

        # Payload preparation
        from_account = settings.KBS_FROM_ACCOUNT
        phone = settings.KBS_FROM_PHONE
        from_bank = "SBICKENX"

        payload = {
            "originatorAccount": {
                "identification": {
                    "identification": from_account,
                    "debitCurrency": "KES",
                    "mobileNumber": phone
                }
            },
            "requestedExecutionDate": "2024-08-09",
            "dbsReferenceId": str(random.randrange(100000, 1000000)),
            "txnNarrative": "TESEAPS123",
            "callBackUrl": "https://clientdomain.com/client/Callback",
            "schedule": {
                "transferFrequency": "DAILY",
                "on": "12",
                "startDate": "2021-02-13",
                "endDate": "2022-01-03",
                "repeat": "3",
                "every": "1"
            },
            "transferTransactionInformation": {
                "instructedAmount": {
                    "amount": amount,
                    "creditCurrency": creditCurrency
                },
                "counterpartyAccount": {
                    "identification": {
                        "identification": to_account,
                        "correspondentBank": from_bank,
                        "beneficiaryBank": to_bank
                    }
                },
                "counterparty": {
                    "name": "TAAM OIL LTD",
                    "postalAddress": {
                        "addressLine": "UGANDA",
                        "postCode": "1100 ZZ",
                        "town": "Kampala",
                        "country": "UG"
                    }
                },
                "remittanceInformation": {
                    "type": "deposit",
                    "content": "deposit"
                },
                "endToEndIdentification": "5e1a3da132cc"
            }
        }

        # API request
        response = requests.post(url=url, headers=headers, json=payload)
        response_data = response.json()

        # Check the bank status
        bank_status = response_data.get("bankStatus")
        if bank_status == "PROCESSED":
            # Create WithdrawRequest if successful
            cls.objects.create(
                member_sacco_id=sacco_member_id,
                amount=amount,
                payment_method="swift",
                request_response="SUCCESS"
            )
            fosa_withdrawal(request=request, amount=amount, sacco_member_id=sacco_member_id, fosa_account=fosa_account_no)
            return JsonResponse({"message": "Withdrawal request processed successfully"}, status=200)
        else:
            # Handle failure
            cls.objects.create(
                member_sacco_id=sacco_member_id,
                amount=amount,
                payment_method="swift",
                request_response="FAILED"
            )
            return JsonResponse({
                "error": "Withdrawal request failed, bank error",
                "reason": response_data.get("reasonText")
            }, status=400)
        
    @classmethod
    def mpesa_withdraw_request(cls,data):
            amount = data.get('amount')
            request = data.get("request")
            phone = data.get('mpesa_number')
            sacco_member_id = data.get("sacco_member_id")
            formatted_phone = None
            # print(f"type of phone : {type(phone)}")
            if  str(phone).startswith('0'):
                phone = phone.lstrip("0")
                formatted_phone = '254' + phone
            if len(str(phone)) == 9:
                 formatted_phone = '254'+phone
            else:
                formatted_phone = phone

            user = CustomUser.objects.filter(sacco_user_id=sacco_member_id).first()

            if not user:
                return JsonResponse({
                    "error": "Withdrawal request failed",
                    "reason": "Try again later",
                }, status=400)
            # Normalize user's stored phone number
            user_phone = str(user.phone)
            if user_phone.startswith('+'):
                user_phone = user_phone.lstrip("+")
            if formatted_phone != user_phone:
                return JsonResponse({
                    "error": "Withdrawal request failed",
                    "reason": "Try again later",
                }, status=400)

            fosa_balance = return_fosa_balance(sacco_member_id=sacco_member_id)
            fosa_account_no = return_fosa_account(sacco_member_id=sacco_member_id)

            if fosa_balance is None or fosa_account_no is None:
                return JsonResponse({
                    "error": "Withdrawal request failed",
                    "reason": "Could not get FOSA details",
                }, status=400)
            if isinstance(fosa_account_no, tuple):
                fosa_account_no = fosa_account_no[0]

            # print(f"fosa account number {fosa_account_no}")

            # if int(amount) > fosa_balance:
            if (fosa_balance - int(amount) < 1000):
                return JsonResponse({"error": "Fosa balance not enough"})
            mpesa_access_token = generate_mpesa_b2c_access_token()
            security_credential = generate_safaricom_security_credential(password="!TkbInternet2025")
            unique_conversation_id = generate_uuid_code()
            # print(f"security credential: {security_credential}")
            headers = {
            'Content-Type': 'application/json',
            'Authorization': f'Bearer {mpesa_access_token}'
            }

            payload = {
                "OriginatorConversationID": unique_conversation_id,
                "InitiatorName": "TKBInt",
                "SecurityCredential": security_credential,
                "CommandID": "BusinessPayment",
                "Amount": amount,
                "PartyA": 991430,
                "PartyB": f"{formatted_phone}",
                "Remarks": "KBS Internet Withdraw from FOSA",
                "QueueTimeOutURL": "https://mydomain.com/b2c/queue",
                "ResultURL": "https://backend.kbs-internet-banking.net/api/payment_b2c_result",
                "Occasion": "KBS Internet Withdraw from FOSA",
                }
            response = requests.post(url='https://api.safaricom.co.ke/mpesa/b2c/v3/paymentrequest',headers=headers,json=payload)
            response_data = response.json()

            # print(f'response data: {response_data}')

            if response_data.get("ResponseCode") == "0":

                withdraw_request = cls.objects.create(
                    conversation_id=unique_conversation_id,
                    member_sacco_id=sacco_member_id,
                    amount=amount,
                    payment_method="mpesa",
                    request_response="PENDING",
                    account_no=formatted_phone
                )
                    
                # Wait and retry checking for transaction ID and result_code in MpesaB2CCallback
                transaction_id = None
                max_retries = 10
                retries = 0

                while retries < max_retries:
                    # print(f"trial number {retries}")
                    time.sleep(5)  # Wait 30 seconds before the first attempt
                    # unique_conversation_id = '20D2E-33D7B-92'
                    callback = MpesaB2CCallback.objects.filter(originator_conversation_id=f"{unique_conversation_id}").first()
                    if callback:
                        # print(f"callback found: {callback}")  
                        # print(f"result_code: {callback.result_code}, transaction_id: {callback.transaction_id}")

                        # Ensure both conditions are checked correctly
                        if callback.transaction_id and str(callback.result_code) == "0":
                            transaction_id = callback.transaction_id
                            # print("Transaction ID found, exiting loop.")
                            break  # Exit the loop as we now have a valid transaction_id

                    retries += 1
                if transaction_id:
                    # Update WithdrawRequest with transaction ID
                    withdraw_request.request_response = "SUCCESS"
                    withdraw_request.save()

                    # Proceed with FOSA withdrawal *only if MpesaB2CCallback result_code is "0"*
                    fosa_response = fosa_withdrawal(
                        request=request, amount=amount, sacco_member_id=sacco_member_id,
                        fosa_account=fosa_account_no, phone=formatted_phone, ref=transaction_id
                    )

                    if fosa_response.status_code == 201:
                        return JsonResponse({"message": "Withdrawal request processed successfully"}, status=200)
                    else:
                        return JsonResponse({"error": "Withdrawal request failed", "reason": "Try again later"}, status=400)
                else:
                    # If we couldn't get a valid transaction_id and result_code = 0 after retries, fail the request
                    withdraw_request.request_response = "FAILED"
                    withdraw_request.save()
                    return JsonResponse({
                        "error": "Withdrawal request failed",
                        "reason": "Transaction ID not received or Mpesa transaction failed",
                    }, status=400)


            else:
                    cls.objects.create(
                        conversation_id=unique_conversation_id,
                        member_sacco_id=sacco_member_id,
                        amount=amount,
                        payment_method="mpesa",
                        request_response="FAILED",
                        account_no=formatted_phone
                    )
                    return JsonResponse({
                    "error": "Withdrawal request failed",
                    "reason": "Try again later",
                }, status=400)
    
    @classmethod
    def loan_swift_withdraw_request(cls,data):

        access_token = return_stanbic_auth_token()
        request = data.get("request")
        amount = data.get("amount")
        to_account = data.get("account_number")
        
        to_bank = data.get("bank")
        sacco_member_id = data.get("account_holder_id")
        url = "https://api.connect.stanbicbank.co.ke/api/sandbox/swift-payments/"
        headers = {
            "Authorization": f"Bearer {access_token}",
            "content-type": "application/json",
            "accept": "application/json"
        }

        # Payload preparation
        from_account = settings.KBS_FROM_ACCOUNT
        phone = settings.KBS_FROM_PHONE
        from_bank = "SBICKENX"

        payload = {
            "originatorAccount": {
                "identification": {
                    "identification": from_account,
                    "debitCurrency": "KES",
                    "mobileNumber": phone
                }
            },
            "requestedExecutionDate": "2024-08-09",
            "dbsReferenceId": str(random.randrange(100000, 1000000)),
            "txnNarrative": "TESEAPS123",
            "callBackUrl": "https://clientdomain.com/client/Callback",
            "schedule": {
                "transferFrequency": "DAILY",
                "on": "12",
                "startDate": "2021-02-13",
                "endDate": "2022-01-03",
                "repeat": "3",
                "every": "1"
            },
            "transferTransactionInformation": {
                "instructedAmount": {
                    "amount": amount,
                    "creditCurrency": "UGX"
                },
                "counterpartyAccount": {
                    "identification": {
                        "identification": to_account,
                        "correspondentBank": from_bank,
                        "beneficiaryBank": to_bank
                    }
                },
                "counterparty": {
                    "name": "TAAM OIL LTD",
                    "postalAddress": {
                        "addressLine": "UGANDA",
                        "postCode": "1100 ZZ",
                        "town": "Kampala",
                        "country": "UG"
                    }
                },
                "remittanceInformation": {
                    "type": "deposit",
                    "content": "deposit"
                },
                "endToEndIdentification": "5e1a3da132cc"
            }
        }

        # API request
        response = requests.post(url=url, headers=headers, json=payload)
        response_data = response.json()
        # Check the bank status
        bank_status = response_data.get("bankStatus")
        if bank_status == "PROCESSED":
            # Create WithdrawRequest if successful
            cls.objects.create(
                member_sacco_id=sacco_member_id,
                amount=amount,
                payment_method="swift",
                request_response="SUCCESS"
            )
            # fosa_withdrawal(request=request, amount=amount, sacco_member_id=sacco_member_id, fosa_account=fosa_account_no)
            return JsonResponse({"message": "Loan disbursement processed successfully"}, status=200)
        else:
            cls.objects.create(
                member_sacco_id=sacco_member_id,
                amount=amount,
                payment_method="swift",
                request_response="FAILED"
            )
            return JsonResponse({
                "error": "Disbursement request failed",
                "reason": response_data.get("reasonText")
            }, status=400)

    @classmethod
    def loan_mpesa_withdraw_request(cls,data):
        amount = data.get('amount')
        request = data.get("request")
        sacco_member_id = data.get("account_holder_id")
        user = CustomUser.objects.get(sacco_user_id=sacco_member_id)
        phone = user.phone

          # Check if the phone number starts with '0'
        if phone.startswith('0'):
              # Replace the leading '0' with '254'
              formatted_phone = '254' + phone[1:]
        else:
              # If it doesn't start with '0', keep it unchanged or handle accordingly
              formatted_phone = phone
          # Example parameters - these should come from your request or be defined appropriately
        env = "sandbox"
        app_key = "SE0XXGYedB9EGpSUwmqXZpU8S4GGAxtE5CBAS0CzH2PMGTer"
        app_secret = "FYuKxMJ75xaruVqWXqG5lNiLy508SwEmTNk3A8zZqDjeCtPnYXBwK8MvyPQAvkD9"
        initiator_name = "testapi"
        security_credential = "VjPqX630LrWDuX2U+zQ1ovU8IdTWrsLyjeJWDeaOAYEelrUxTnW9pr6vkYsdlknLpdQqkT3Y0r1LLTA2KCzyAhqfArp46987pvCVISJa4mEpdG/Gae3ffkHyCCHxhxuXLXS0tA15MFydnAzM7ivSotExzfL/eguv0QPsFyeixEQEgSqjY5nkLagBUa5+pWAm0xIrUKfunQF06/Icq19EkpphOXE2GEF8W+vnzKDzTDboIyHoedxOim3MwjOKH/TYvcU0PypNXImQdObf+2sFm4oy0svBOswg8Xfnu7eotVxi2nQxC5whHiSur6GVS2QmG45DxAG3WBYeLgpbhWXrVg=="
        command_id = "BusinessPayment"
        amount = amount
        party_a = "174379"  # Example Shortcode
        party_b = formatted_phone  # Example Phone Number
        remarks = "Payment remarks"
        queue_timeout_url = "https://mydomain.com/b2c/queue"
        result_url = "https://mydomain.com/b2c/result"
        occassion = "optional_ocassion"
        # fosa_balance = return_fosa_balance(sacco_member_id=sacco_member_id)
        # fosa_account_no = return_fosa_account(sacco_member_id=sacco_member_id)
        # if isinstance(fosa_account_no, tuple):
        #       fosa_account_no = fosa_account_no[0]
        #       print("fosa")
              
        # print(fosa_account_no)
        # if int(amount) > fosa_balance:
        #       return JsonResponse({"error": "Fosa balance not enough"})
          # Create an instance of the B2C class
        # b2c = B2C(env=env, app_key=app_key, app_secret=app_secret)
        #   # Call the transact method with the necessary parameters
        # response = b2c.transact(
        #       initiator_name=initiator_name,
        #       security_credential=security_credential,
        #       command_id=command_id,
        #       amount=amount,
        #       party_a=party_a,
        #       party_b=party_b,
        #       remarks=remarks,
        #       queue_timeout_url=queue_timeout_url,
        #       result_url=result_url,
        #       occassion=occassion
        #   )
        response = {"success": "Here we are"}
          # Return the response as JSON
        if response.get("ResponseCode") == "0":
                  cls.objects.create(
                  member_sacco_id=sacco_member_id,
                  amount=amount,
                  payment_method="mpesa",
                  request_response="SUCCESS"
              )
                #   fosa_withdrawal(request=request, amount=amount, sacco_member_id=sacco_member_id, fosa_account=fosa_account_no)
                  return JsonResponse({"message": "Loan disbursement processed successfully"}, status=200)
              
        else:
                  cls.objects.create(
                      member_sacco_id=sacco_member_id,
                      amount=amount,
                      payment_method="mpesa",
                      request_response="FAILED"
                  )
                  return JsonResponse({
                  "error": "Withdrawal request failed",
                  "reason": "Try again later",
              }, status=400)
    

def return_stanbic_auth_token():
    client_id = settings.STANBIC_CLIENT_ID
    client_secret = settings.STANBIC_CLIENT_SECRET
    token_url = settings.STANBIC_TOKEN_URL
    scope = settings.STANBIC_SCOPE

    payload = {
    'grant_type': 'client_credentials',
    'client_id': client_id,
    'client_secret': client_secret,
    'scope': scope
    }
    response = requests.post(token_url,data=payload)
    access_token = response.json().get("access_token")
    # print(access_token)
    return access_token

def get_P_access_token():
    client_id = "hW5DhVcUHSb06W45ERIqdXX6D6C9CElC1KXbgkDWomQ"
    client_secret = "3tEroB79viucZc5sg6Fjqf6TpOevyXY_BQg-IaYxxpE"
    url = 'https://api.kwara.com/oauth/token'
    payload = {
        'grant_type': 'client_credentials',
        'client_id': client_id,
        'client_secret': client_secret,
    }
    response = requests.post(url, data=payload)

    if response.status_code == 200:
        access_token = response.json().get('access_token')
        if access_token:
            return access_token
        else:
            raise ValueError("Access token not found in the response")
    else:
        raise ValueError(f"Failed to fetch access token, status code: {response.status_code}")

def return_fosa_balance(sacco_member_id):
    access_token = get_P_access_token()

    url = f"https://api.kwara.com/members/{sacco_member_id}/savings"
    headers = {
        'Authorization': f'Bearer {access_token}',
        'accept': 'application/json',
    }
    try:
        response = requests.get(url, headers=headers)
        response.raise_for_status()
        savings_data = response.json().get('data', [])

        # Extract details of "FOSA Current" or product id "6006"
        fosa_savings = next(
            (s for s in savings_data if s.get('attributes', {}).get('product_name') == "FOSA Current" or s.get('attributes', {}).get('product_id') == "6006"),
            None
        )
        staff_savings = next(
            (s for s in savings_data if s.get('attributes', {}).get('product_name') == "Staff Account" or s.get('attributes', {}).get('product_id') == "6008"),
            None
        )

        # if fosa_savings:
        #     available_balance = fosa_savings['attributes'].get('available_balance')
        #     return available_balance
        # else:
        #      return None
        if fosa_savings and staff_savings:
            available_balance = staff_savings['attributes'].get('available_balance')
            return available_balance
            # account_number = staff_savings['id']
            # return account_number
        elif fosa_savings:
            available_balance = fosa_savings['attributes'].get('available_balance')
            return available_balance
            # account_number = fosa_savings['id']
            # return account_number
        elif staff_savings:
            available_balance = staff_savings['attributes'].get('available_balance')
            return available_balance
            # account_number = staff_savings['id']
            # return account_number
        else:
             return None

    except requests.exceptions.RequestException as e:
        return HttpResponse('Failed to fetch savings details: ' + str(e), content_type="application/json", status=500)

    except requests.exceptions.RequestException as e:
        return None
    
def return_fosa_account(sacco_member_id):
    access_token = get_P_access_token()

    url = f"https://api.kwara.com/members/{sacco_member_id}/savings"
    headers = {
        'Authorization': f'Bearer {access_token}',
        'accept': 'application/json',
    }
    try:
        response = requests.get(url, headers=headers)
        response.raise_for_status()
        savings_data = response.json().get('data', [])

        # Extract details of "FOSA Current" or product id "6006"
        fosa_savings = next(
            (s for s in savings_data if s.get('attributes', {}).get('product_name') == "FOSA Current" or s.get('attributes', {}).get('product_id') == "6006"),
            None
        )

        staff_savings = next(
            (s for s in savings_data if s.get('attributes', {}).get('product_name') == "Staff Account" or s.get('attributes', {}).get('product_id') == "6008"),
            None
        )

        if fosa_savings and staff_savings:
            account_number = staff_savings['id']
            return account_number
        elif fosa_savings:
            account_number = fosa_savings['id']
            return account_number
        elif staff_savings:
            account_number = staff_savings['id']
            return account_number
        else:
             return None

    except requests.exceptions.RequestException as e:
        return None
    
def return_fosa_details(request,sacco_member_id):
    access_token = get_P_access_token()
    # access_token = get_kwara_access_token()

    url = f"https://api.kwara.com/members/{sacco_member_id}/savings"
    headers = {
        'Authorization': f'Bearer {access_token}',
        'accept': 'application/json',
    }
    try:
        response = requests.get(url, headers=headers)
        response.raise_for_status()
        savings_data = response.json().get('data', [])

        # Extract details of "FOSA Current" or product id "6006"
        fosa_savings = next(
            (s for s in savings_data if s.get('attributes', {}).get('product_name') == "FOSA Current" or s.get('attributes', {}).get('product_id') == "6006"),
            None
        )

        if fosa_savings:
                return JsonResponse({
                    'account_number': fosa_savings['id'],
                    'available_balance': fosa_savings['attributes'].get('available_balance'),
                }, status=200)
        else:
                return JsonResponse({
                    'message': 'Login successful, but no FOSA Current savings account found',
                }, status=200)

    except requests.exceptions.RequestException as e:
                return JsonResponse({'error': 'Failed to fetch savings details: ' + str(e)}, status=500)

def check_fosa_balance(request,sacco_member_id):
    access_token = get_P_access_token()
    # access_token = get_kwara_access_token()

    url = f"https://api.kwara.com/members/{sacco_member_id}/savings"
    headers = {
        'Authorization': f'Bearer {access_token}',
        'accept': 'application/json',
    }
    try:
        response = requests.get(url, headers=headers)
        response.raise_for_status()
        savings_data = response.json().get('data', [])

        # Extract details of "FOSA Current" or product id "6006"
        fosa_savings = next(
            (s for s in savings_data if s.get('attributes', {}).get('product_name') == "FOSA Current" or s.get('attributes', {}).get('product_id') == "6006"),
            None
        )

        if fosa_savings:
                return JsonResponse({
                    'account_number': fosa_savings['id'],
                    'available_balance': fosa_savings['attributes'].get('available_balance'),
                }, status=200)
        else:
                return JsonResponse({
                    'message': 'Login successful, but no FOSA Current savings account found',
                }, status=200)    
        
    except requests.exceptions.RequestException as e:
                return JsonResponse({'error': 'Failed to fetch savings details: ' + str(e)}, status=500)
    
# def fosa_withdrawal(request,amount, sacco_member_id, fosa_account):
#     # print('fosa withdrawal accessed')
 
#     user = CustomUser.objects.get(sacco_user_id=sacco_member_id)

#     access_token = get_P_access_token()

#     url = "https://api.kwara.com/savings_transactions"

#     payload = { "data": { "attributes": {
#         "type": "WITHDRAWAL",
#         "savings_id": fosa_account,
#         "amount": amount,
#         "phone_number": user.phone,
#         # "authorization_hold_id": "9fbadd7e-3a9f-4215-85c6-eff64a1c47a5",
#         "bank_branch": "Central",
#         "notes": "Withdraw from FOSA",
#         "payment_method": "cash",
#         "manual_journal_entry": True,
#         "idempotency_key": "9fbadd7e-3a9f-4215-85c6-eff64a1c47a5",
#     } } }
#     headers = {
#         "accept": "application/json",
#         "Authorization": f"Bearer {access_token}"
#     }

#     response = requests.post(url, json=payload, headers=headers)

#     # print(response.json())

#     return JsonResponse(response.json(), status=response.status_code)

def fosa_withdrawal(request,amount, sacco_member_id, fosa_account,phone,ref):
    # print('fosa withdrawal accessed')
 
    user = CustomUser.objects.get(sacco_user_id=sacco_member_id)

    access_token = get_P_access_token()

    url = "https://api.kwara.com/savings_transactions"

    payload = { "data": { "attributes": {
        "type": "WITHDRAWAL",
        "savings_id": fosa_account,
        "amount": amount,
        "phone_number": user.phone,
        # "authorization_hold_id": "9fbadd7e-3a9f-4215-85c6-eff64a1c47a5",
        "bank_branch": "Central",
        "notes": f"Withdraw from FOSA {phone} - mpesa{ref}",
        "payment_method": "mobile_money",
        "manual_journal_entry": True,
        "idempotency_key": "9fbadd7e-3a9f-4215-85c6-eff64a1c47a5",
    } } }
    headers = {
        "accept": "application/json",
        "Authorization": f"Bearer {access_token}"
    }

    response = requests.post(url, json=payload, headers=headers)

    print(response.json())

    return JsonResponse(response.json(), status=response.status_code)

def generate_mpesa_b2c_access_token():

    consumer_key = 'lGKUvEWRrvPCfvTefDM5pqipkrVjDwMD'
    consumer_secret = 'BnELh0BjUguvOoiH'
    api_URL = "https://api.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials"

    try:
        r = requests.get(api_URL, auth=HTTPBasicAuth(consumer_key, consumer_secret))
    except:
        r = requests.get(api_URL, auth=HTTPBasicAuth(consumer_key, consumer_secret), verify=False)

    # print(r.text)

    json_response = (
        r.json()
    )  # {'access_token': 'orfE9Dun2qqCpuXsORjcWGzvrAIY', 'expires_in': '3599'}

    my_access_token = json_response["access_token"]
    # print(my_access_token)

    return my_access_token



def generate_safaricom_security_credential(password, cert_path=os.path.join(os.path.dirname(__file__), "ProductionCertificate.cer")):
    """
    Encrypts the Safaricom Daraja Sandbox initiator password using the public certificate.

    :param password: Initiator password for authentication
    :param cert_path: Path to the Safaricom Daraja Sandbox public certificate (.cer file)
    :return: Base64 encoded encrypted security credential
    """
    try:
        # Load the X.509 certificate from PEM format
        with open(cert_path, "rb") as cert_file:
            cert_pem = cert_file.read()

        # Parse the X.509 certificate (from PEM format)
        cert = x509.load_pem_x509_certificate(cert_pem, backend=default_backend())

        # Extract the public key
        public_key = cert.public_key()

        # Encrypt the password using the RSA public key
        encrypted_password = public_key.encrypt(
            password.encode(),
            padding.PKCS1v15()
        )

        # Convert to Base64 string (as required by Safaricom)
        security_credential = base64.b64encode(encrypted_password).decode()
        return security_credential

    except Exception as e:
        # print(f"Error generating security credential: {e}")
        return None
    
def generate_uuid_code():
    uid = uuid.uuid4().hex[:10].upper()  # Get first 10 chars of UUID (random)
    part1 = uid[:5]  # First 5 characters
    part2 = uid[5:]  # Next 5 characters
    part3 = random.randint(1, 99)  # Single digit
    
    unique_code = f"{part1}-{part2}-{part3}"
    return unique_code