import uuid
from django.conf import settings
from django.core.exceptions import ValidationError
from django.db import models
from apps.service.models import NO_CREDENTIALS


class HistoryQuerySet(models.QuerySet):
    def delete(self):
        raise ValidationError("Commercial history cannot be deleted.")


class Record(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    objects = HistoryQuerySet.as_manager()

    class Meta:
        abstract = True
        ordering = ("created_at", "pk")

    def save(self, *args, **kwargs):
        raise ValidationError("Use commercial services.")

    def delete(self, *args, **kwargs):
        raise ValidationError("Commercial history cannot be deleted.")

    def _persist(self):
        self.full_clean()
        super().save(force_insert=self._state.adding)


class QuotationSequence(models.Model):
    service_center = models.OneToOneField("organization.ServiceCenter", on_delete=models.PROTECT, primary_key=True)
    next_value = models.PositiveBigIntegerField(default=1)

    class Meta:
        constraints = [models.CheckConstraint(condition=models.Q(next_value__gte=1), name="commercial_sequence_positive")]


class QuotationFamily(Record):
    service_case = models.OneToOneField("service.ServiceCase", on_delete=models.PROTECT, related_name="quotation_family")
    service_center = models.ForeignKey("organization.ServiceCenter", on_delete=models.PROTECT)
    number = models.CharField(max_length=22)
    approval_obligation = models.BooleanField(default=False)

    class Meta(Record.Meta):
        constraints = [models.UniqueConstraint(fields=("service_center", "number"), name="commercial_number_unique"),
            models.CheckConstraint(condition=models.Q(number__regex=r"^EST-[0-9]{8,18}$"), name="commercial_number_format")]


def amount():
    return models.DecimalField(max_digits=14, decimal_places=2, default=0)


class ServiceQuotation(Record):
    class Status(models.TextChoices):
        DRAFT = "DRAFT"
        SUBMITTED = "SUBMITTED"
        APPROVED = "APPROVED"
        REJECTED = "REJECTED"
        SUPERSEDED = "SUPERSEDED"

    family = models.ForeignKey(QuotationFamily, on_delete=models.PROTECT, related_name="revisions")
    revision = models.PositiveIntegerField(default=1)
    is_current = models.BooleanField(default=True)
    status = models.CharField(max_length=10, choices=Status, default=Status.DRAFT)
    currency = models.CharField(max_length=3)
    customer_note = models.TextField(max_length=4000, blank=True, help_text=NO_CREDENTIALS)
    internal_note = models.TextField(max_length=4000, blank=True, help_text=NO_CREDENTIALS)
    valid_until = models.DateTimeField(null=True, blank=True)
    subtotal = amount()
    discount_total = amount()
    tax_total = amount()
    grand_total = amount()
    customer_pay_total = amount()
    covered_total = amount()
    context_snapshot = models.JSONField(default=dict, blank=True)
    scope_snapshot = models.JSONField(default=list, blank=True)
    created_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT, related_name="created_quotations")
    submitted_at = models.DateTimeField(null=True, blank=True)
    submitted_by = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, on_delete=models.PROTECT, related_name="submitted_quotations")

    class Meta(Record.Meta):
        ordering = ("family_id", "revision")
        permissions = [("manage_servicequotation", "Manage service quotations"),
                       ("record_quotationdecision", "Record customer quotation decisions")]
        constraints = [
            models.UniqueConstraint(fields=("family", "revision"), name="commercial_revision_unique"),
            models.UniqueConstraint(fields=("family",), condition=models.Q(is_current=True), name="commercial_one_current"),
            models.CheckConstraint(condition=models.Q(revision__gte=1), name="commercial_revision_positive"),
            models.CheckConstraint(condition=models.Q(status__in=["DRAFT", "SUBMITTED", "APPROVED", "REJECTED", "SUPERSEDED"]), name="commercial_status_valid"),
            models.CheckConstraint(condition=models.Q(currency__regex=r"^[A-Z]{3}$"), name="commercial_currency_format"),
            models.CheckConstraint(condition=models.Q(subtotal__gte=0, discount_total__gte=0, tax_total__gte=0,
                grand_total__gte=0, customer_pay_total__gte=0, covered_total__gte=0, discount_total__lte=models.F("subtotal"),
                grand_total=models.F("subtotal")-models.F("discount_total")+models.F("tax_total"),
                customer_pay_total=models.F("grand_total")-models.F("covered_total")), name="commercial_totals_coherent"),
            models.CheckConstraint(condition=(models.Q(status="DRAFT", submitted_at__isnull=True, submitted_by__isnull=True) |
                models.Q(status__in=["SUBMITTED", "APPROVED", "REJECTED", "SUPERSEDED"], submitted_at__isnull=False, submitted_by__isnull=False)), name="commercial_submission_coherent"),
        ]

    def __str__(self):
        return f"{self.family.number} / revision {self.revision}"


class QuotationLine(Record):
    class Kind(models.TextChoices):
        PART = "PART"
        LABOR = "LABOR"
        SERVICE = "SERVICE"

    class Responsibility(models.TextChoices):
        CUSTOMER = "CUSTOMER"
        WARRANTY = "WARRANTY"
        COMPANY = "COMPANY"

    quotation = models.ForeignKey(ServiceQuotation, on_delete=models.PROTECT, related_name="lines")
    position = models.PositiveIntegerField()
    kind = models.CharField(max_length=7, choices=Kind)
    responsibility = models.CharField(max_length=8, choices=Responsibility)
    description = models.CharField(max_length=500)
    coverage_reason = models.CharField(max_length=1000, blank=True, help_text=NO_CREDENTIALS)
    spare_part = models.ForeignKey("parts.SparePart", null=True, blank=True, on_delete=models.PROTECT)
    part_code = models.CharField(max_length=128, blank=True)
    part_name = models.CharField(max_length=500, blank=True)
    repair_action = models.ForeignKey("service.ServiceRepairAction", null=True, blank=True, on_delete=models.PROTECT)
    finding = models.ForeignKey("service.ServiceDiagnosticFinding", null=True, blank=True, on_delete=models.PROTECT)
    quantity = models.PositiveIntegerField(default=1)
    unit_price = amount()
    subtotal = amount()
    discount = amount()
    taxable_amount = amount()
    tax_rate = models.DecimalField(max_digits=5, decimal_places=2, default=0)
    tax_label = models.CharField(max_length=100, blank=True)
    tax = amount()
    total = amount()
    is_active = models.BooleanField(default=True)

    class Meta(Record.Meta):
        ordering = ("position", "pk")
        constraints = [
            models.UniqueConstraint(fields=("quotation", "position"), condition=models.Q(is_active=True), name="commercial_line_position_unique"),
            models.CheckConstraint(condition=models.Q(kind__in=["PART", "LABOR", "SERVICE"], responsibility__in=["CUSTOMER", "WARRANTY", "COMPANY"]), name="commercial_line_types_valid"),
            models.CheckConstraint(condition=(models.Q(kind="PART", spare_part__isnull=False) | models.Q(kind__in=["LABOR", "SERVICE"], spare_part__isnull=True)), name="commercial_line_part_coherent"),
            models.CheckConstraint(condition=models.Q(quantity__gte=1, quantity__lte=1000000, unit_price__gte=0,
                subtotal=models.F("quantity")*models.F("unit_price"), discount__gte=0, discount__lte=models.F("subtotal"),
                taxable_amount=models.F("subtotal")-models.F("discount"), tax_rate__gte=0, tax_rate__lte=100,
                tax__gte=0, total=models.F("taxable_amount")+models.F("tax")), name="commercial_line_money_coherent"),
        ]


class QuotationDecision(Record):
    quotation = models.OneToOneField(ServiceQuotation, on_delete=models.PROTECT, related_name="decision")
    outcome = models.CharField(max_length=8, choices=[("APPROVED", "Approved"), ("REJECTED", "Rejected")])
    recorded_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT)
    channel = models.CharField(max_length=10, choices=[(x, x) for x in ("IN_PERSON", "PHONE", "SMS", "WHATSAPP", "EMAIL", "OTHER")])
    recipient_name = models.CharField(max_length=200)
    reference = models.CharField(max_length=500)
    note = models.TextField(max_length=2000, blank=True, help_text=NO_CREDENTIALS)

    class Meta(Record.Meta):
        constraints = [models.CheckConstraint(condition=models.Q(outcome__in=["APPROVED", "REJECTED"], channel__in=["IN_PERSON", "PHONE", "SMS", "WHATSAPP", "EMAIL", "OTHER"]), name="commercial_decision_valid")]


class CommercialWorkAuthorization(Record):
    """The exact approved revision used by a later technical/physical operation."""
    quotation = models.ForeignKey(ServiceQuotation, on_delete=models.PROTECT, related_name="work_authorizations")
    operation = models.CharField(max_length=8, choices=[(x,x) for x in ("PERFORM", "CONSUME", "COMPLETE")])
    repair_action = models.ForeignKey("service.ServiceRepairAction", null=True, blank=True, on_delete=models.PROTECT)
    repair_execution = models.ForeignKey("service.ServiceRepairExecution", on_delete=models.PROTECT)
    spare_part = models.ForeignKey("parts.SparePart", null=True, blank=True, on_delete=models.PROTECT)
    quantity = models.PositiveIntegerField(default=0)
    actor = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT)

    class Meta(Record.Meta):
        constraints = [models.CheckConstraint(condition=(models.Q(operation="CONSUME", spare_part__isnull=False, repair_action__isnull=False, quantity__gte=1) |
            models.Q(operation="PERFORM", spare_part__isnull=True, repair_action__isnull=False, quantity=0) |
            models.Q(operation="COMPLETE", spare_part__isnull=True, repair_action__isnull=True, quantity=0)), name="commercial_authorization_coherent")]


from .invoice_models import InvoiceSequence, ServiceInvoice, InvoiceLine, InvoiceAllocation  # noqa: E402,F401
from .payment_models import ReceiptSequence, ServicePayment, PaymentAllocation, ServicePaymentReceipt, PaymentReversal, ServiceFinancialRelease  # noqa: E402,F401
