from django.conf import settings
from django.core.exceptions import ValidationError
from django.db import models
from django.utils import timezone

from shop_management.models.base import DataRoot


class FinancialOpeningBalance(DataRoot):
    class BalanceType(models.TextChoices):
        CUSTOMER = "CUSTOMER", "Customer receivable"
        SUPPLIER = "SUPPLIER", "Supplier payable"
        ACCOUNT = "ACCOUNT", "Cash, bank or Sarafi account"

    class Status(models.TextChoices):
        DRAFT = "DRAFT", "Draft"
        POSTED = "POSTED", "Posted"
        VOID = "VOID", "Cancelled"

    opening_number = models.CharField(max_length=40, unique=True, null=True, blank=True)
    shop = models.ForeignKey("shop_management.Shop", on_delete=models.PROTECT, related_name="financial_opening_balances")
    balance_type = models.CharField(max_length=12, choices=BalanceType.choices)
    customer = models.ForeignKey("shop_management.Customer", null=True, blank=True, on_delete=models.PROTECT, related_name="controlled_openings")
    supplier = models.ForeignKey("shop_management.Supplier", null=True, blank=True, on_delete=models.PROTECT, related_name="controlled_openings")
    financial_account = models.ForeignKey("shop_management.Account", null=True, blank=True, on_delete=models.PROTECT, related_name="controlled_openings")
    currency = models.ForeignKey("shop_management.Currency", on_delete=models.PROTECT, related_name="financial_opening_balances")
    amount = models.DecimalField(max_digits=18, decimal_places=2)
    exchange_rate_used = models.DecimalField(max_digits=20, decimal_places=6, default=1)
    opening_date = models.DateField(default=timezone.localdate)
    reference = models.CharField(max_length=100, blank=True)
    notes = models.TextField(blank=True)
    status = models.CharField(max_length=10, choices=Status.choices, default=Status.DRAFT)
    journal_entry = models.OneToOneField("shop_management.JournalEntry", null=True, blank=True, on_delete=models.PROTECT, related_name="financial_opening_balance")
    posted_at = models.DateTimeField(null=True, blank=True)
    posted_by = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, on_delete=models.PROTECT, related_name="financial_openings_posted")
    voided_at = models.DateTimeField(null=True, blank=True)
    voided_by = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, on_delete=models.PROTECT, related_name="financial_openings_voided")
    void_reason = models.TextField(blank=True)

    class Meta:
        ordering = ["-opening_date", "-id"]

    def clean(self):
        target = {self.BalanceType.CUSTOMER: self.customer_id, self.BalanceType.SUPPLIER: self.supplier_id, self.BalanceType.ACCOUNT: self.financial_account_id}.get(self.balance_type)
        selected = sum(bool(value) for value in (self.customer_id, self.supplier_id, self.financial_account_id))
        if not target or selected != 1:
            raise ValidationError("Select exactly one target matching the opening balance type.")
        related = self.customer or self.supplier or self.financial_account
        if related.shop_id != self.shop_id or self.currency.shop_id != self.shop_id:
            raise ValidationError("Target and currency must belong to the selected branch.")
        if self.exchange_rate_used <= 0:
            raise ValidationError("Exchange rate must be greater than zero.")
        if self.balance_type == self.BalanceType.ACCOUNT:
            if self.amount == 0:
                raise ValidationError("Account opening amount cannot be zero.")
        elif self.amount <= 0:
            raise ValidationError("Customer and supplier opening amounts must be greater than zero.")

    def delete(self, *args, **kwargs):
        if self.status != self.Status.DRAFT:
            raise ValidationError("Posted or void opening balances cannot be deleted.")
        return super().delete(*args, **kwargs)
