from __future__ import annotations

from pathlib import Path
from decimal import Decimal, ROUND_HALF_UP
from uuid import uuid4

from django.conf import settings
from django.core.validators import FileExtensionValidator
from django.core.validators import MinValueValidator
from django.db import models
from django.db.models import Sum
from django.db.models.signals import post_delete
from django.dispatch import receiver
from django.utils import timezone
from django.utils.text import slugify


VAT_RATE = Decimal("0.05")


class TimestampedModel(models.Model):
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        abstract = True


class VerificationClaim(TimestampedModel):
    class Status(models.TextChoices):
        DRAFT = "draft", "Draft"
        SUBMITTED = "submitted", "Submitted"

    claim_reference_number = models.CharField(max_length=64, unique=True)
    date_received = models.DateField()
    applicant_name_arabic = models.CharField(max_length=255)
    applicant_name_english = models.CharField(max_length=255)
    status = models.CharField(max_length=16, choices=Status.choices, default=Status.SUBMITTED)
    last_autosaved_at = models.DateTimeField(blank=True, null=True)
    autosave_snapshot = models.JSONField(default=dict, blank=True)
    first_document_request_date = models.DateField(blank=True, null=True)
    second_reminder_date = models.DateField(blank=True, null=True)
    final_report_without_missing_docs_date = models.DateField(blank=True, null=True)
    initial_report_with_missing_docs_date = models.DateField(blank=True, null=True)
    followup_reminder_date = models.DateField(blank=True, null=True)
    final_report_submission_date = models.DateField(blank=True, null=True)

    created_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        related_name="verification_claims_created",
        blank=True,
        null=True,
    )
    updated_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        related_name="verification_claims_updated",
        blank=True,
        null=True,
    )

    class Meta:
        ordering = ["-updated_at", "-id"]
        indexes = [
            models.Index(fields=["claim_reference_number"]),
            models.Index(fields=["date_received"]),
            models.Index(fields=["updated_at"]),
        ]

    def __str__(self) -> str:
        return self.claim_reference_number

    @property
    def current_status(self) -> str:
        if self.status == self.Status.DRAFT:
            return "Draft"
        prefetched_logs = getattr(self, "_prefetched_objects_cache", {}).get("activity_logs")
        if prefetched_logs is not None:
            if any(log.action != VerificationActivityLog.Action.CREATE for log in prefetched_logs):
                return "Updated"
            return "New"

        if self.activity_logs.exclude(action=VerificationActivityLog.Action.CREATE).exists():
            return "Updated"
        return "New"

    @property
    def invoice_count(self) -> int:
        return self.invoices.count()

    def totals(self) -> dict[str, Decimal]:
        aggregates = self.invoices.aggregate(
            total_amount_paid=Sum("total_amount_paid"),
            total_vat_amount=Sum("vat_amount"),
            total_vat_approved=Sum("vat_approved"),
            total_vat_paid=Sum("vat_paid"),
        )
        return {
            key: aggregates.get(key) or Decimal("0.00")
            for key in (
                "total_amount_paid",
                "total_vat_amount",
                "total_vat_approved",
                "total_vat_paid",
            )
        }


class VerificationInvoice(TimestampedModel):
    class TOS(models.TextChoices):
        CONTRACTOR = "contractor", "Contractor"
        CONSULTANT = "consultant", "Consultant"
        SUPPLIER = "supplier", "Supplier"

    claim = models.ForeignKey(VerificationClaim, on_delete=models.CASCADE, related_name="invoices")
    goods_services = models.CharField(max_length=120, default="SERVICE")
    transaction_type = models.CharField(max_length=120, default="TAX INVOICE")
    transaction_number = models.CharField(max_length=64)
    transaction_date = models.DateField()
    line_item = models.CharField(max_length=32)
    supplier_trn = models.CharField(max_length=32)
    supplier_name = models.CharField(max_length=255)
    plot_number = models.CharField(max_length=64, blank=True, default="")
    tos = models.CharField(max_length=16, choices=TOS.choices, blank=True, default="")
    total_amount_paid = models.DecimalField(
        max_digits=14,
        decimal_places=2,
        validators=[MinValueValidator(Decimal("0.00"))],
    )
    vat_amount = models.DecimalField(
        max_digits=14,
        decimal_places=2,
        validators=[MinValueValidator(Decimal("0.00"))],
        default=Decimal("0.00"),
    )
    vat_approved = models.DecimalField(
        max_digits=14,
        decimal_places=2,
        validators=[MinValueValidator(Decimal("0.00"))],
        default=Decimal("0.00"),
    )
    vat_paid = models.DecimalField(
        max_digits=14,
        decimal_places=2,
        validators=[MinValueValidator(Decimal("0.00"))],
        default=Decimal("0.00"),
    )
    description = models.CharField(max_length=255, blank=True, default="OK")
    sort_order = models.PositiveIntegerField(default=1)

    class Meta:
        ordering = ["sort_order", "id"]
        indexes = [
            models.Index(fields=["claim", "sort_order"]),
            models.Index(fields=["transaction_date"]),
        ]

    def __str__(self) -> str:
        return f"{self.claim.claim_reference_number} - {self.transaction_number}"

    def save(self, *args, **kwargs):
        self.vat_amount = (self.total_amount_paid * VAT_RATE).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
        super().save(*args, **kwargs)


def verification_document_upload_to(instance, filename: str) -> str:
    extension = Path(filename).suffix.lower()
    safe_stem = slugify(Path(filename).stem) or "document"
    claim_ref = slugify(instance.invoice.claim.claim_reference_number) or f"claim-{instance.invoice.claim_id}"
    invoice_ref = slugify(instance.invoice.transaction_number) or f"invoice-{instance.invoice_id}"
    return f"verification/{claim_ref}/{invoice_ref}/{instance.category}/{safe_stem}-{uuid4().hex[:10]}{extension}"


class VerificationInvoiceDocument(TimestampedModel):
    class Category(models.TextChoices):
        INVOICE = "invoice", "Invoice"
        PAYMENT_PROOF = "payment_proof", "Payment Proof"

    class ReviewStatus(models.TextChoices):
        PENDING = "pending", "Pending"
        APPROVED = "approved", "Approved"
        DECLINED = "declined", "Declined"

    class ReviewReason(models.TextChoices):
        WRONG_TRN = "wrong_trn", "Wrong TRN"
        MISSING_PROOF = "missing_proof", "Missing Proof"
        AMOUNT_MISMATCH = "amount_mismatch", "Amount Mismatch"
        UNREADABLE_FILE = "unreadable_file", "Unreadable File"
        DUPLICATE_INVOICE = "duplicate_invoice", "Duplicate Invoice"
        INVALID_TAX_INVOICE = "invalid_tax_invoice", "Invalid Tax Invoice"
        OTHER = "other", "Other"

    invoice = models.ForeignKey(VerificationInvoice, on_delete=models.CASCADE, related_name="documents")
    category = models.CharField(max_length=24, choices=Category.choices)
    file = models.FileField(
        upload_to=verification_document_upload_to,
        validators=[FileExtensionValidator(allowed_extensions=["pdf", "png", "jpg", "jpeg"])],
    )
    original_name = models.CharField(max_length=255)
    review_status = models.CharField(max_length=16, choices=ReviewStatus.choices, default=ReviewStatus.PENDING)
    review_reason = models.CharField(max_length=32, choices=ReviewReason.choices, blank=True, default="")
    reviewed_at = models.DateTimeField(blank=True, null=True)
    uploaded_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        related_name="verification_documents_uploaded",
        blank=True,
        null=True,
    )
    reviewed_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        related_name="verification_documents_reviewed",
        blank=True,
        null=True,
    )

    class Meta:
        ordering = ["category", "-created_at", "-id"]
        indexes = [
            models.Index(fields=["invoice", "category"]),
            models.Index(fields=["created_at"]),
            models.Index(fields=["review_status", "created_at"]),
        ]

    def __str__(self) -> str:
        return f"{self.invoice.transaction_number} - {self.get_category_display()} - {self.original_name}"


class VerificationDescriptionOption(TimestampedModel):
    label = models.CharField(max_length=255, unique=True)
    is_active = models.BooleanField(default=True)
    sort_order = models.PositiveIntegerField(default=1)

    class Meta:
        ordering = ["sort_order", "label", "id"]
        indexes = [
            models.Index(fields=["is_active", "sort_order"], name="verificatio_is_acti_68984a_idx"),
        ]

    def __str__(self) -> str:
        return self.label


@receiver(post_delete, sender=VerificationInvoiceDocument)
def cleanup_verification_document_file(sender, instance, **kwargs):
    if instance.file:
        instance.file.delete(save=False)


class VerificationActivityLog(models.Model):
    class Action(models.TextChoices):
        CREATE = "create", "Create"
        UPDATE = "update", "Update"
        DELETE = "delete", "Delete"

    claim = models.ForeignKey(VerificationClaim, on_delete=models.CASCADE, related_name="activity_logs")
    invoice = models.ForeignKey(
        VerificationInvoice,
        on_delete=models.SET_NULL,
        related_name="activity_logs",
        blank=True,
        null=True,
    )
    actor = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        related_name="verification_activity_logs",
        blank=True,
        null=True,
    )
    action = models.CharField(max_length=16, choices=Action.choices)
    message = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ["-created_at", "-id"]
        indexes = [
            models.Index(fields=["claim", "created_at"]),
            models.Index(fields=["action", "created_at"]),
        ]

    def __str__(self) -> str:
        return f"{self.claim.claim_reference_number} - {self.action}"
