# apps/credits/models.py
from django.db import models, transaction
from django.conf import settings
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from apps.core.models import BaseModel, Document, AuditLog, Notification
from apps.members.models import Member
from apps.banking.models import Transaction, Account, TransactionCategory

class CreditProduct(BaseModel):
    """Définit les différents types de produits de crédit offerts."""
    name = models.CharField(max_length=255, unique=True, verbose_name=_("Nom du produit de crédit"))
    description = models.TextField(blank=True, verbose_name=_("Description"))
    min_amount = models.DecimalField(max_digits=15, decimal_places=2, verbose_name=_("Montant minimum"))
    max_amount = models.DecimalField(max_digits=15, decimal_places=2, verbose_name=_("Montant maximum"))
    interest_rate = models.DecimalField(max_digits=5, decimal_places=2, verbose_name=_("Taux d'intérêt annuel"))
    max_duration_months = models.PositiveIntegerField(verbose_name=_("Durée maximale en mois"))

    def __str__(self):
        return self.name

    class Meta(BaseModel.Meta):
        verbose_name = _("Produit de crédit")
        verbose_name_plural = _("Produits de crédit")
        db_table = "credits_credit_product"

class LoanApplication(BaseModel):
    """Représente une demande de prêt soumise par un membre."""
    class ApplicationStatus(models.TextChoices):
        PENDING = 'PENDING', _('En attente')
        APPROVED = 'APPROVED', _('Approuvée')
        REJECTED = 'REJECTED', _('Rejetée')
        CANCELLED = 'CANCELLED', _('Annulée')

    member = models.ForeignKey(Member, on_delete=models.CASCADE, related_name="loan_applications", verbose_name=_("Membre"))
    credit_product = models.ForeignKey(CreditProduct, on_delete=models.PROTECT, verbose_name=_("Produit de crédit"))
    requested_amount = models.DecimalField(max_digits=15, decimal_places=2, verbose_name=_("Montant demandé"))
    requested_duration_months = models.PositiveIntegerField(verbose_name=_("Durée demandée en mois"))
    application_date = models.DateTimeField(auto_now_add=True, verbose_name=_("Date de la demande"))
    status = models.CharField(max_length=20, choices=ApplicationStatus.choices, default=ApplicationStatus.PENDING, verbose_name=_("Statut"))
    approved_amount = models.DecimalField(max_digits=15, decimal_places=2, null=True, blank=True, verbose_name=_("Montant approuvé"))
    approved_duration_months = models.PositiveIntegerField(null=True, blank=True, verbose_name=_("Durée approuvée en mois"))
    approval_date = models.DateTimeField(null=True, blank=True, verbose_name=_("Date d'approbation"))
    approved_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True, related_name='approved_loan_applications', limit_choices_to={'is_staff': True}, verbose_name=_("Approuvé par"))
    rejection_reason = models.TextField(blank=True, verbose_name=_("Motif de rejet"))
    supporting_documents = models.ManyToManyField(Document, blank=True, verbose_name=_("Pièces justificatives"))

    def __str__(self):
        return f"Demande de {self.requested_amount} par {self.member} ({self.get_status_display()})"

    def approve(self, by_user, approved_amount=None, approved_duration=None):
        from django.db import transaction
        if self.status != self.ApplicationStatus.PENDING: return
        self.status = self.ApplicationStatus.APPROVED
        self.approved_by = by_user
        self.approval_date = timezone.now()
        self.approved_amount = approved_amount or self.requested_amount
        self.approved_duration_months = approved_duration or self.requested_duration_months
        self.save()
        transaction.on_commit(lambda: self._create_approval_records(by_user))

    def reject(self, by_user, reason):
        from django.db import transaction
        if self.status != self.ApplicationStatus.PENDING: return
        self.status = self.ApplicationStatus.REJECTED
        self.approved_by = by_user
        self.rejection_reason = reason
        self.save()
        transaction.on_commit(lambda: self._create_rejection_records(by_user, reason))

    def _create_approval_records(self, by_user):
        AuditLog.objects.create(user=by_user, action_type=AuditLog.ActionType.APPROVAL, content_object=self, description=f"Approbation de la demande de prêt de {self.member.user.email} pour un montant de {self.approved_amount}.")
        Notification.objects.create(recipient=self.member.user, title="Votre demande de prêt a été approuvée", message=f"Félicitations, votre demande de prêt de {self.approved_amount} a été approuvée. Nous vous contacterons bientôt pour le décaissement.", notification_type=Notification.NotificationType.SUCCESS)

    def _create_rejection_records(self, by_user, reason):
        AuditLog.objects.create(user=by_user, action_type=AuditLog.ActionType.REJECTION, content_object=self, description=f"Rejet de la demande de prêt de {self.member.user.email}. Motif : {reason}")
        Notification.objects.create(recipient=self.member.user, title="Votre demande de prêt a été rejetée", message=f"Nous sommes au regret de vous informer que votre demande de prêt a été rejetée. Motif : {reason}", notification_type=Notification.NotificationType.ERROR)

    class Meta(BaseModel.Meta):
        verbose_name = _("Demande de prêt")
        verbose_name_plural = _("Demandes de prêt")
        db_table = "credits_loan_application"

class Loan(BaseModel):
    """Représente un prêt approuvé et décaissé."""
    class LoanStatus(models.TextChoices):
        ACTIVE = 'ACTIVE', _('Actif')
        PAID_OFF = 'PAID_OFF', _('Soldé')
        DEFAULTED = 'DEFAULTED', _('En défaut')
        CANCELLED = 'CANCELLED', _('Annulé')

    loan_application = models.OneToOneField(LoanApplication, on_delete=models.PROTECT, related_name="loan_instance", verbose_name=_("Demande de prêt"))
    member = models.ForeignKey(Member, on_delete=models.CASCADE, related_name="loans", verbose_name=_("Membre"))
    credit_product = models.ForeignKey(CreditProduct, on_delete=models.PROTECT, verbose_name=_("Produit de crédit"))
    disbursed_amount = models.DecimalField(max_digits=15, decimal_places=2, verbose_name=_("Montant décaissé"))
    disbursement_date = models.DateTimeField(null=True, blank=True, verbose_name=_("Date de décaissement"))
    interest_rate = models.DecimalField(max_digits=5, decimal_places=2, verbose_name=_("Taux d'intérêt"))
    duration_months = models.PositiveIntegerField(verbose_name=_("Durée en mois"))
    status = models.CharField(max_length=20, choices=LoanStatus.choices, default=LoanStatus.ACTIVE, verbose_name=_("Statut"))

    def __str__(self):
        return f"Prêt de {self.disbursed_amount} à {self.member} ({self.get_status_display()})"

    @property
    def total_repaid(self):
        """Calcule le montant total déjà remboursé pour ce prêt."""
        from django.db.models import Sum
        return self.repayments.aggregate(total=Sum('amount_paid'))['total'] or 0

    @transaction.atomic
    def disburse(self, by_user, from_account):
        """Débloque les fonds pour le prêt, créant la transaction financière associée."""
        if self.disbursement_date is not None: raise ValueError("Ce prêt a déjà été décaissé.")
        try:
            to_account = Account.objects.get(owner=self.member.user, type=Account.AccountType.CURRENT)
        except Account.DoesNotExist:
            raise ValueError(f"Le membre {self.member.user.email} n'a pas de compte courant pour recevoir les fonds.")
        except Account.MultipleObjectsReturned:
            raise ValueError(f"Le membre {self.member.user.email} a plusieurs comptes courants. Décaissement impossible.")
        
        Transaction.objects.create(from_account=from_account, to_account=to_account, amount=self.disbursed_amount, category=TransactionCategory.LOAN_DISBURSEMENT, description=f"Décaissement du prêt ID {self.id} pour {self.member.user.email}", created_by=by_user)
        self.disbursement_date = timezone.now()
        self.save()
        AuditLog.objects.create(user=by_user, action_type=AuditLog.ActionType.LOAN_DISBURSEMENT, content_object=self, description=f"Décaissement de {self.disbursed_amount} pour le prêt ID {self.id} de {from_account} vers {to_account}.")
        Notification.objects.create(recipient=self.member.user, title="Votre prêt a été décaissé", message=f"Les fonds pour votre prêt d'un montant de {self.disbursed_amount} ont été virés sur votre compte courant.", notification_type=Notification.NotificationType.SUCCESS)

    @transaction.atomic
    def make_repayment(self, amount, by_user, from_account, to_account):
        """Enregistre un remboursement pour le prêt."""
        if self.status != self.LoanStatus.ACTIVE:
            raise ValueError("Les remboursements ne sont possibles que pour les prêts actifs.")

        # 1. Créer la transaction financière du membre vers la coopérative
        financial_transaction = Transaction.objects.create(
            from_account=from_account,
            to_account=to_account,
            amount=amount,
            category=TransactionCategory.LOAN_REPAYMENT,
            description=f"Remboursement pour le prêt ID {self.id}",
            created_by=by_user
        )

        # 2. Créer l'enregistrement de remboursement
        Repayment.objects.create(
            loan=self,
            amount_paid=amount,
            financial_transaction=financial_transaction,
            created_by=by_user
        )

        # 3. Vérifier si le prêt est entièrement remboursé
        # Note: une logique plus complexe serait nécessaire pour gérer le principal et les intérêts
        if self.total_repaid >= self.disbursed_amount:
            self.status = self.LoanStatus.PAID_OFF
            self.save()

        # 4. Créer un log d'audit
        AuditLog.objects.create(
            user=by_user,
            action_type=AuditLog.ActionType.LOAN_REPAYMENT,
            content_object=self,
            description=f"Remboursement de {amount} reçu pour le prêt ID {self.id}."
        )

        # 5. Envoyer une notification au membre
        Notification.objects.create(
            recipient=self.member.user,
            title="Confirmation de remboursement",
            message=f"Nous confirmons la réception de votre remboursement de {amount} pour le prêt ID {self.id}. Montant total remboursé : {self.total_repaid}.",
            notification_type=Notification.NotificationType.SUCCESS
        )

    class Meta(BaseModel.Meta):
        verbose_name = _("Prêt")
        verbose_name_plural = _("Prêts")
        db_table = "credits_loan"

class RepaymentSchedule(BaseModel):
    """Définit les échéances de remboursement pour un prêt."""
    loan = models.ForeignKey(Loan, on_delete=models.CASCADE, related_name="repayment_schedule", verbose_name=_("Prêt"))
    due_date = models.DateField(verbose_name=_("Date d'échéance"))
    principal_due = models.DecimalField(max_digits=15, decimal_places=2, verbose_name=_("Principal dû"))
    interest_due = models.DecimalField(max_digits=15, decimal_places=2, verbose_name=_("Intérêt dû"))

    def __str__(self):
        return f"Échéance du {self.due_date} pour le Prêt {self.loan.id}"

    class Meta(BaseModel.Meta):
        verbose_name = _("Échéancier de remboursement")
        verbose_name_plural = _("Échéanciers de remboursement")
        db_table = "credits_repayment_schedule"

class Repayment(BaseModel):
    """Enregistre un remboursement effectué par le membre."""
    loan = models.ForeignKey(Loan, on_delete=models.CASCADE, related_name="repayments", verbose_name=_("Prêt"))
    repayment_schedule_entry = models.ForeignKey(RepaymentSchedule, on_delete=models.SET_NULL, null=True, blank=True, related_name="actual_repayments", verbose_name=_("Échéance"))
    amount_paid = models.DecimalField(max_digits=15, decimal_places=2, verbose_name=_("Montant payé"))
    payment_date = models.DateTimeField(auto_now_add=True, verbose_name=_("Date de paiement"))
    financial_transaction = models.OneToOneField(Transaction, on_delete=models.PROTECT, related_name="loan_repayment", verbose_name=_("Transaction financière"))

    def __str__(self):
        return f"Remboursement de {self.amount_paid} pour le Prêt {self.loan.id}"

    class Meta(BaseModel.Meta):
        verbose_name = _("Remboursement")
        verbose_name_plural = _("Remboursements")
        db_table = "credits_repayment"
