"""Immutable received-money evidence, receipts and delivery exceptions."""
from django.conf import settings
from django.db import models
from .models import Record, amount

METHODS = [(x, x.replace("_", " ").title()) for x in ("CASH", "CARD", "BANK_TRANSFER", "MOBILE_FINANCIAL_SERVICE", "OTHER")]
PAYMENT_PRIVACY = "References only. Never record card numbers, CVV, banking credentials, wallet secrets or OTPs."


class ReceiptSequence(models.Model):
    service_center = models.OneToOneField("organization.ServiceCenter", primary_key=True, on_delete=models.PROTECT)
    next_value = models.PositiveBigIntegerField(default=1)

    class Meta:
        constraints = [models.CheckConstraint(condition=models.Q(next_value__gte=1), name="payment_sequence_positive")]


class ServicePayment(Record):
    invoice = models.ForeignKey("commercial.ServiceInvoice", on_delete=models.PROTECT, related_name="payments")
    service_center = models.ForeignKey("organization.ServiceCenter", on_delete=models.PROTECT)
    amount = amount()
    currency = models.CharField(max_length=3)
    method = models.CharField(max_length=24, choices=METHODS)
    reference = models.CharField(max_length=200, blank=True, help_text=PAYMENT_PRIVACY)
    status = models.CharField(max_length=6, choices=[("POSTED", "Posted"), ("VOIDED", "Voided")], default="POSTED")
    received_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT)
    received_at = models.DateTimeField()
    note = models.TextField(max_length=2000, blank=True, help_text=PAYMENT_PRIVACY)

    class Meta(Record.Meta):
        permissions = [("receive_servicepayment", "Receive and allocate service payment"), ("reverse_servicepayment", "Reverse service payment")]
        constraints = [
            models.CheckConstraint(condition=models.Q(amount__gt=0, currency__regex=r"^[A-Z]{3}$", status__in=["POSTED", "VOIDED"], method__in=[x[0] for x in METHODS]), name="payment_values_valid"),
            models.CheckConstraint(condition=models.Q(method="CASH") | ~models.Q(reference=""), name="payment_external_reference"),
            models.CheckConstraint(condition=~models.Q(method="OTHER") | ~models.Q(note=""), name="payment_other_explanation"),
            models.UniqueConstraint(fields=("service_center", "method", "reference"), condition=~models.Q(reference=""), name="payment_reference_unique"),
        ]


class PaymentAllocation(Record):
    payment = models.OneToOneField(ServicePayment, on_delete=models.PROTECT, related_name="allocation")
    invoice = models.ForeignKey("commercial.ServiceInvoice", on_delete=models.PROTECT, related_name="payment_allocations")
    amount = amount()
    allocated_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT)

    class Meta(Record.Meta):
        constraints = [models.CheckConstraint(condition=models.Q(amount__gt=0), name="payment_allocation_positive")]


class ServicePaymentReceipt(Record):
    payment = models.OneToOneField(ServicePayment, on_delete=models.PROTECT, related_name="receipt")
    service_center = models.ForeignKey("organization.ServiceCenter", on_delete=models.PROTECT)
    number = models.CharField(max_length=22)
    amount = amount()
    currency = models.CharField(max_length=3)
    method = models.CharField(max_length=24, choices=METHODS)
    reference = models.CharField(max_length=200, blank=True)
    customer_snapshot = models.JSONField()
    center_snapshot = models.JSONField()
    invoice_number = models.CharField(max_length=22)
    issued_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT)
    issued_at = models.DateTimeField()

    class Meta(Record.Meta):
        constraints = [models.UniqueConstraint(fields=("service_center", "number"), name="payment_receipt_number_unique"),
            models.CheckConstraint(condition=models.Q(number__regex=r"^RCT-[0-9]{8,18}$", amount__gt=0,
                currency__regex=r"^[A-Z]{3}$", method__in=[x[0] for x in METHODS]), name="payment_receipt_valid")]

    def __str__(self):
        return self.number


class PaymentReversal(Record):
    payment = models.OneToOneField(ServicePayment, on_delete=models.PROTECT, related_name="reversal")
    amount = amount()
    reversed_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT)
    reversed_at = models.DateTimeField()
    reason = models.TextField(max_length=2000, help_text=PAYMENT_PRIVACY)

    class Meta(Record.Meta):
        constraints = [models.CheckConstraint(condition=models.Q(amount__gt=0) & ~models.Q(reason=""), name="payment_reversal_valid")]


class ServiceFinancialRelease(Record):
    invoice = models.ForeignKey("commercial.ServiceInvoice", on_delete=models.PROTECT, related_name="financial_releases")
    outstanding_amount = amount()
    authorized_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT)
    authorized_at = models.DateTimeField()
    reason = models.TextField(max_length=2000, help_text=PAYMENT_PRIVACY)
    reference = models.CharField(max_length=200, blank=True, help_text=PAYMENT_PRIVACY)

    class Meta(Record.Meta):
        permissions = [("authorize_due_release", "Authorize delivery with outstanding customer balance")]
        constraints = [models.CheckConstraint(condition=models.Q(outstanding_amount__gt=0) & ~models.Q(reason=""), name="payment_release_valid")]
