import datetime
import logging
from decimal import Decimal
import os
from uuid import uuid4
import nepali_datetime
from django.db import models
from django.utils.translation import gettext_lazy as _
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericForeignKey

from iems_backend.models import UUIDTimeStampedModel
from checkpoint.models import CheckPoint
from users.models import User

from .utils import generate_tracking_code, generate_reference_no

logger = logging.getLogger(__name__)


def document_upload_path(instance, filename):
    extension = os.path.splitext(filename)[1]
    return f"uploads/documents/{instance.submission_id}/{uuid4().hex}{extension}"


def _nepali_quarter_from_month(month):
    if month in (4, 5, 6):
        return 1
    if month in (7, 8, 9):
        return 2
    if month in (10, 11, 12):
        return 3
    return 4


def _parse_bs_date_parts(date_value):
    if not date_value:
        return None

    try:
        normalized = str(date_value).replace("/", "-").strip()
        year_str, month_str, day_str = normalized.split("-", 2)
        year = int(year_str)
        month = int(month_str)
        day = int(day_str[:2])
    except (TypeError, ValueError):
        return None

    if month < 1 or month > 12 or day < 1:
        return None

    return (year, month, day, _nepali_quarter_from_month(month))


def _fallback_submission_bs_parts(instance):
    if instance.nepali_year and instance.nepali_month and instance.nepali_day:
        return (
            instance.nepali_year,
            instance.nepali_month,
            instance.nepali_day,
            instance.nepali_quarter or _nepali_quarter_from_month(instance.nepali_month),
        )

    ref_date = (
        instance.submitted_at.date() if instance.submitted_at else datetime.date.today()
    )
    try:
        nd = nepali_datetime.date.from_datetime_date(ref_date)
    except Exception:
        return (None, None, None, None)

    return (nd.year, nd.month, nd.day, _nepali_quarter_from_month(nd.month))


class SubmissionType(UUIDTimeStampedModel):
    name = models.CharField(_("Name"), max_length=100, unique=True)
    rank = models.PositiveIntegerField(_("Rank"), default=0)
    code_prefix = models.CharField(
        _("Code Prefix"), max_length=10, unique=True, null=True, blank=True
    )
    category_description = models.TextField(
        _("Category Description"), blank=True, null=True
    )

    class Meta:
        db_table = "record_submission_type"
        verbose_name = _("Submission Type")
        verbose_name_plural = _("Submission Types")
        ordering = ["rank", "name"]

    def __str__(self):
        return self.name


class CommodityCategory(UUIDTimeStampedModel):
    name = models.CharField(_("Name"), max_length=100, unique=True)
    category_description = models.TextField(
        _("Category Description"), blank=True, null=True
    )

    class Meta:
        db_table = "record_commodity_category"
        verbose_name = _("Commodity Category")
        verbose_name_plural = _("Commodity Categories")
        ordering = ["name"]

    def __str__(self):
        return self.name


class CommodityType(UUIDTimeStampedModel):
    category = models.ForeignKey(
        CommodityCategory,
        on_delete=models.CASCADE,
        related_name="types",
        verbose_name=_("Commodity Category"),
    )
    name = models.CharField(_("Name"), max_length=100)
    description = models.TextField(_("Description"), blank=True, null=True)

    class Meta:
        db_table = "record_commodity_type"
        verbose_name = _("Commodity Type")
        verbose_name_plural = _("Commodity Types")
        ordering = ["name"]
        unique_together = ("category", "name")

    def __str__(self):
        return f"{self.category.name} - {self.name}"


class UnitOfMeasurement(UUIDTimeStampedModel):
    class DimensionChoices(models.TextChoices):
        WEIGHT = "weight", _("Weight")
        VOLUME = "volume", _("Volume")
        COUNT = "count", _("Count")
        LENGTH = "length", _("Length")
        AREA = "area", _("Area")
        OTHER = "other", _("Other")

    name = models.CharField(_("Name"), max_length=100)
    code = models.CharField(_("Code"), max_length=20, unique=True)
    dimension = models.CharField(
        _("Dimension"),
        max_length=20,
        choices=DimensionChoices.choices,
        blank=True,
        default="",
        help_text=_("Logical unit group used for conversion validation."),
    )

    class Meta:
        db_table = "record_unit_of_measurement"
        verbose_name = _("Unit of Measurement")
        verbose_name_plural = _("Units of Measurement")
        ordering = ["name"]

    def __str__(self):
        return f"{self.name} ({self.code})"


class UnitConversion(UUIDTimeStampedModel):
    from_unit = models.ForeignKey(
        UnitOfMeasurement,
        on_delete=models.CASCADE,
        related_name="outgoing_conversions",
        verbose_name=_("From Unit"),
    )
    to_unit = models.ForeignKey(
        UnitOfMeasurement,
        on_delete=models.CASCADE,
        related_name="incoming_conversions",
        verbose_name=_("To Unit"),
    )
    factor = models.DecimalField(_("Factor"), max_digits=20, decimal_places=10)

    class Meta:
        db_table = "record_unit_conversion"
        verbose_name = _("Unit Conversion")
        verbose_name_plural = _("Unit Conversions")
        ordering = ["from_unit__name", "to_unit__name"]
        constraints = [
            models.UniqueConstraint(
                fields=["from_unit", "to_unit"],
                name="record_unit_conversion_unique_pair",
            ),
            models.CheckConstraint(
                condition=models.Q(factor__gt=0),
                name="record_unit_conversion_factor_gt_0",
            ),
            models.CheckConstraint(
                condition=~models.Q(from_unit=models.F("to_unit")),
                name="record_unit_conversion_from_not_to",
            ),
        ]

    def __str__(self):
        return (
            f"1 {self.from_unit.code} = {self.factor.normalize()} "
            f"{self.to_unit.code}"
        )

    def clean(self):
        super().clean()
        from django.core.exceptions import ValidationError

        errors = {}

        if self.from_unit_id and self.to_unit_id:
            if self.from_unit_id == self.to_unit_id:
                errors["to_unit"] = _("From unit and to unit must be different.")

            from_dimension = (self.from_unit.dimension or "").strip()
            to_dimension = (self.to_unit.dimension or "").strip()

            if not from_dimension or not to_dimension:
                errors["from_unit"] = _(
                    "Both units must have a dimension before creating a conversion."
                )
                errors["to_unit"] = _(
                    "Both units must have a dimension before creating a conversion."
                )
            elif from_dimension != to_dimension:
                errors["to_unit"] = _("Units must belong to the same dimension.")

        if self.factor is not None and self.factor <= 0:
            errors["factor"] = _("Conversion factor must be greater than zero.")

        if errors:
            raise ValidationError(errors)

    def save(self, *args, **kwargs):
        self.full_clean()
        super().save(*args, **kwargs)


class SubmissionStatus(UUIDTimeStampedModel):
    name = models.CharField(_("Name"), max_length=20)
    code = models.CharField(_("Code"), max_length=20, unique=True)

    class Meta:
        db_table = "record_submission_status"
        verbose_name = _("Submission Status")
        verbose_name_plural = _("Submission Statuses")
        ordering = ["name"]

    def __str__(self):
        return self.name

    def clean(self):
        super().clean()
        if self.pk:
            from django.core.exceptions import ValidationError
            orig = type(self).objects.get(pk=self.pk)
            if orig.code != self.code:
                raise ValidationError({"code": _("The code field is a constant and cannot be changed once created.")})

    def save(self, *args, **kwargs):
        self.full_clean()
        super().save(*args, **kwargs)


class CurrencyChoice(models.TextChoices):
    NPR = "NPR", _("Nepalese Rupee")
    INR = "INR", _("Indian Rupee")

class Submission(UUIDTimeStampedModel):
    submission_type = models.ForeignKey(
        SubmissionType,
        on_delete=models.CASCADE,
        related_name="%(class)s_logs",
        verbose_name=_("Submission Type"),
    )

    checkpoint = models.ForeignKey(
        CheckPoint,
        on_delete=models.CASCADE,
        related_name="%(class)s_logs",
        verbose_name=_("Checkpoint"),
    )
    created_by = models.ForeignKey(
        User,
        on_delete=models.CASCADE,
        related_name="%(class)s_created",
        verbose_name=_("Created By"),
    )
    updated_by = models.ForeignKey(
        User,
        on_delete=models.CASCADE,
        related_name="%(class)s_updated",
        verbose_name=_("Updated By"),
    )
    refrence_no = models.CharField(
        _("Reference Number"),
        max_length=100,
        unique=True,
        db_index=True,
        editable=False,
        null=True,
        blank=True,
    )
    status = models.ForeignKey(
        SubmissionStatus,
        on_delete=models.CASCADE,
        related_name="%(class)s_logs",
        verbose_name=_("Status"),
    )
    tracking_code = models.CharField(
        _("Tracking Code"),
        max_length=20,
        unique=True,
        editable=False,
        null=True,
        blank=True,
        db_index=True,
    )
    submitted_at = models.DateTimeField(_("Submitted At"), auto_now_add=True)
    remarks = models.TextField(_("Remarks"), blank=True, null=True)

    # Nepali Date components — auto-populated on save for fast B.S. calendar reporting
    nepali_year = models.PositiveIntegerField(_("Nepali Year (B.S.)"), null=True, blank=True, db_index=True)
    nepali_month = models.PositiveSmallIntegerField(_("Nepali Month (B.S.)"), null=True, blank=True, db_index=True)
    nepali_day = models.PositiveSmallIntegerField(_("Nepali Day (B.S.)"), null=True, blank=True)
    nepali_quarter = models.PositiveSmallIntegerField(_("Nepali Quarter (B.S.)"), null=True, blank=True, db_index=True)


    class Meta:
        db_table = "record_submission"
        verbose_name = _("Submission")
        verbose_name_plural = _("Submissions")
        ordering = ["-submitted_at"]

    def __str__(self):
        return (
            f"{self.tracking_code} - {self.refrence_no}"
            if self.tracking_code
            else self.refrence_no
        )

    def save(self, *args, **kwargs):
        if not self.refrence_no:
            self.refrence_no = generate_reference_no(self)
        if not self.tracking_code:
            self.tracking_code = generate_tracking_code(self)


        ref_date = self.submitted_at.date() if self.submitted_at else datetime.date.today()
        try:
            nd = nepali_datetime.date.from_datetime_date(ref_date)
            self.nepali_year = nd.year
            self.nepali_month = nd.month
            self.nepali_day = nd.day
            self.nepali_quarter = _nepali_quarter_from_month(nd.month)
        except Exception:
            pass  

        super().save(*args, **kwargs)




class FinancialSubmission(Submission):
    currency = models.CharField(
        _("Currency"),
        max_length=5,
        choices=CurrencyChoice.choices,
        default=CurrencyChoice.NPR
    )
    amount_in_npr = models.DecimalField(
        _("Amount in NPR"),
        max_digits=15,
        decimal_places=2,
        blank=True,
        null=True
    )
    base_amount = models.DecimalField(
        _("Base Amount in the Currency"),
        max_digits=15,
        decimal_places=2,
        blank=False,
        null=False
    )

    class Meta:
        abstract = True

    def save(self, *args, **kwargs):
        if self.base_amount is not None:
            if self.currency == CurrencyChoice.INR:
                self.amount_in_npr = self.base_amount * Decimal('1.6')
            else:
                self.amount_in_npr = self.base_amount
        super().save(*args, **kwargs)


class ImportSubmission(FinancialSubmission):
    commodity_type = models.ForeignKey(
        CommodityType,
        on_delete=models.CASCADE,
        related_name="import_submissions",
        verbose_name=_("Commodity Type"),
    )
    detailed_description = models.TextField(
        _("Detailed Description"), blank=True, null=True
    )
    approval_no = models.CharField(
        _("Approval Number"), max_length=100, unique=True, db_index=True, blank=True, null=True
    )
    approval_time = models.DateTimeField(_("Approval Time"), blank=True, null=True)

    importer_name = models.CharField(
        _("Importer Name"), max_length=100, blank=True, null=True
    )
    importer_address = models.CharField(
        _("Importer Address"), max_length=100, blank=True, null=True
    )
    importer_contact = models.CharField(
        _("Importer Contact"), max_length=15, blank=True, null=True
    )

    exporter_name = models.CharField(
        _("Exporter Name"), max_length=100, blank=True, null=True
    )
    exporter_address = models.CharField(
        _("Exporter Address"), max_length=100, blank=True, null=True
    )
    exporter_contact = models.CharField(
        _("Exporter Contact"), max_length=15, blank=True, null=True
    )

    total_quantity = models.IntegerField(_("Total Quantity"), blank=True, null=True)
    bag_packet_count = models.IntegerField(_("Bag/Packet Count"), blank=True, null=True)
    import_date = models.CharField(_("Import Date"), max_length=20, null=True, blank=True)
    import_nepali_year = models.PositiveIntegerField(null=True, blank=True, db_index=True)
    import_nepali_month = models.PositiveSmallIntegerField(
        null=True, blank=True, db_index=True
    )
    import_nepali_day = models.PositiveSmallIntegerField(null=True, blank=True)
    import_nepali_quarter = models.PositiveSmallIntegerField(
        null=True, blank=True, db_index=True
    )

    manufacturing_date = models.CharField(_("Manufacturing Date"), max_length=20, null=True, blank=True)
    batch_no = models.CharField(_("Batch Number"), max_length=100, blank=True, null=True)
    

    health_certificate_no = models.CharField(
        _("Health Certificate Number"), max_length=100, blank=True, null=True
    )
    health_certificate_issue_date = models.CharField(_("Health Certificate Issue Date"), max_length=20, null=True, blank=True)
    health_certificate_issue_agency = models.CharField(
        _("Health Certificate Issue Agency"), max_length=100, blank=True, null=True
    )

    invoice_no = models.CharField(_("Invoice Number"), max_length=100, blank=True, null=True)
    invoice_date = models.CharField(_("Invoice Date"), max_length=20, null=True, blank=True)

    lab_testing_info = models.TextField(_("Lab Testing Info"), blank=True, null=True)
    origin_of_consignment = models.CharField(_("Origin of Consignment"), max_length=100, blank=True, null=True)

    unit_of_measurement = models.ForeignKey(
        UnitOfMeasurement,
        on_delete=models.CASCADE,
        related_name="import_submissions",
        verbose_name=_("Unit of Measurement"),
    )



    class Meta:
        db_table = "record_import_submission"
        verbose_name = _("Import Submission")
        verbose_name_plural = _("Import Submissions")

    def save(self, *args, **kwargs):
        parsed_parts = _parse_bs_date_parts(self.import_date)
        if parsed_parts:
            (
                self.import_nepali_year,
                self.import_nepali_month,
                self.import_nepali_day,
                self.import_nepali_quarter,
            ) = parsed_parts
        else:
            (
                self.import_nepali_year,
                self.import_nepali_month,
                self.import_nepali_day,
                self.import_nepali_quarter,
            ) = _fallback_submission_bs_parts(self)
            if self.import_date:
                logger.warning(
                    "Could not parse import_date '%s' for submission %s.",
                    self.import_date,
                    self.pk,
                )
        super().save(*args, **kwargs)


class CollectionCategory(UUIDTimeStampedModel):
    name = models.CharField(_("Name"), max_length=100, unique=True)
    description = models.TextField(_("Description"), blank=True, null=True)

    class Meta:
        db_table = "record_collection_category"
        verbose_name = _("Collection Category")
        verbose_name_plural = _("Collection Categories")

    def __str__(self):
        return self.name


class CollectionType(UUIDTimeStampedModel):
    category = models.ForeignKey(
        CollectionCategory,
        on_delete=models.CASCADE,
        related_name="types",
        verbose_name=_("Category"),
    )
    name = models.CharField(_("Name"), max_length=100)
    description = models.TextField(_("Description"), blank=True, null=True)

    class Meta:
        db_table = "record_collection_type"
        verbose_name = _("Collection Type")
        verbose_name_plural = _("Collection Types")
        unique_together = ("category", "name")

    def __str__(self):
        return self.name


class MetadataDeletionRequest(UUIDTimeStampedModel):
    class TargetType(models.TextChoices):
        COMMODITY_CATEGORY = "commodity_category", _("Commodity Category")
        COMMODITY_TYPE = "commodity_type", _("Commodity Type")
        COLLECTION_CATEGORY = "collection_category", _("Collection Category")
        COLLECTION_TYPE = "collection_type", _("Collection Type")

    class StatusChoices(models.TextChoices):
        PENDING = "pending", _("Pending")
        APPROVED = "approved", _("Approved")
        DENIED = "denied", _("Denied")

    target_type = models.CharField(
        _("Target Type"),
        max_length=40,
        choices=TargetType.choices,
    )
    target_id = models.UUIDField(_("Target ID"))
    target_name = models.CharField(
        _("Target Name Snapshot"),
        max_length=255,
        blank=True,
        default="",
    )
    reason = models.TextField(_("Reason"), blank=True, null=True)
    status = models.CharField(
        _("Status"),
        max_length=20,
        choices=StatusChoices.choices,
        default=StatusChoices.PENDING,
    )
    requested_by = models.ForeignKey(
        User,
        on_delete=models.CASCADE,
        related_name="metadata_delete_requests",
        verbose_name=_("Requested By"),
    )
    reviewed_by = models.ForeignKey(
        User,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="metadata_delete_reviews",
        verbose_name=_("Reviewed By"),
    )
    reviewed_at = models.DateTimeField(_("Reviewed At"), null=True, blank=True)
    review_note = models.TextField(_("Review Note"), blank=True, null=True)

    class Meta:
        db_table = "record_metadata_deletion_request"
        verbose_name = _("Master Data Deletion Request")
        verbose_name_plural = _("Category/Type of Collection/Commodity Data Deletion Requests")
        ordering = ["-created_at"]
        constraints = [
            models.UniqueConstraint(
                fields=["target_type", "target_id"],
                condition=models.Q(status="pending"),
                name="record_metadata_delete_request_unique_pending_target",
            )
        ]

    def __str__(self):
        return (
            f"{self.get_target_type_display()} [{self.target_id}] "
            f"- {self.get_status_display()}"
        )

    @classmethod
    def target_model_map(cls):
        return {
            cls.TargetType.COMMODITY_CATEGORY: CommodityCategory,
            cls.TargetType.COMMODITY_TYPE: CommodityType,
            cls.TargetType.COLLECTION_CATEGORY: CollectionCategory,
            cls.TargetType.COLLECTION_TYPE: CollectionType,
        }

    def get_target_model(self):
        return self.target_model_map().get(self.target_type)

    def resolve_target(self):
        target_model = self.get_target_model()
        if not target_model:
            return None
        return target_model.objects.filter(pk=self.target_id).first()


class RevenueFineSubmission(FinancialSubmission):
    collection_type = models.ForeignKey(
        CollectionType,
        on_delete=models.CASCADE,
        related_name="revenue_fine_submissions",
        verbose_name=_("Collection Type"),
    )

    # GenericForeignKey permits polymorphic ties gracefully
    target_content_type = models.ForeignKey(
        ContentType, on_delete=models.CASCADE, null=True, blank=True
    )
    target_object_id = models.UUIDField(null=True, blank=True)
    target_submission = GenericForeignKey("target_content_type", "target_object_id")
    revenue_date = models.CharField(_("Revenue Date"), max_length=20, null=True, blank=True)
    revenue_nepali_year = models.PositiveIntegerField(null=True, blank=True, db_index=True)
    revenue_nepali_month = models.PositiveSmallIntegerField(
        null=True, blank=True, db_index=True
    )
    revenue_nepali_day = models.PositiveSmallIntegerField(null=True, blank=True)
    revenue_nepali_quarter = models.PositiveSmallIntegerField(
        null=True, blank=True, db_index=True
    )

    payment_refrence = models.CharField(_("Payment Reference"), max_length=100, blank=True, null=True)
    reason = models.TextField(_("Reason"), blank=True, null=True)

    class Meta:
        db_table = "record_revenue_fine_submission"
        verbose_name = _("Revenue/Fine Submission")
        verbose_name_plural = _("Revenue/Fine Submissions")

    def save(self, *args, **kwargs):
        parsed_parts = _parse_bs_date_parts(self.revenue_date)
        if parsed_parts:
            (
                self.revenue_nepali_year,
                self.revenue_nepali_month,
                self.revenue_nepali_day,
                self.revenue_nepali_quarter,
            ) = parsed_parts
        else:
            (
                self.revenue_nepali_year,
                self.revenue_nepali_month,
                self.revenue_nepali_day,
                self.revenue_nepali_quarter,
            ) = _fallback_submission_bs_parts(self)
            if self.revenue_date:
                logger.warning(
                    "Could not parse revenue_date '%s' for submission %s.",
                    self.revenue_date,
                    self.pk,
                )
        super().save(*args, **kwargs)


class ExportSubmission(FinancialSubmission):
    commodity_type = models.ForeignKey(
        CommodityType,
        on_delete=models.CASCADE,
        related_name="export_submissions",
        verbose_name=_("Commodity Type"),
    )
    exporter_name = models.CharField(_("Exporter Name"), max_length=100)
    destination_country = models.CharField(_("Destination Country"), max_length=100)
    export_date = models.CharField(_("Export Date"), max_length=20, null=True, blank=True)
    quantity = models.DecimalField(_("Quantity"), max_digits=15, decimal_places=2)
    export_nepali_year = models.PositiveIntegerField(null=True, blank=True, db_index=True)
    export_nepali_month = models.PositiveSmallIntegerField(
        null=True, blank=True, db_index=True
    )
    export_nepali_day = models.PositiveSmallIntegerField(null=True, blank=True)
    export_nepali_quarter = models.PositiveSmallIntegerField(
        null=True, blank=True, db_index=True
    )
    goods_description = models.TextField(_("Goods Description"))

    unit_of_measurement = models.ForeignKey(
        UnitOfMeasurement,
        on_delete=models.CASCADE,
        related_name="export_submissions",
        verbose_name=_("Unit of Measurement"),
    )

    class Meta:
        db_table = "record_export_submission"
        verbose_name = _("Export Submission")
        verbose_name_plural = _("Export Submissions")

    def save(self, *args, **kwargs):
        parsed_parts = _parse_bs_date_parts(self.export_date)
        if parsed_parts:
            (
                self.export_nepali_year,
                self.export_nepali_month,
                self.export_nepali_day,
                self.export_nepali_quarter,
            ) = parsed_parts
        else:
            (
                self.export_nepali_year,
                self.export_nepali_month,
                self.export_nepali_day,
                self.export_nepali_quarter,
            ) = _fallback_submission_bs_parts(self)
            if self.export_date:
                logger.warning(
                    "Could not parse export_date '%s' for submission %s.",
                    self.export_date,
                    self.pk,
                )
        super().save(*args, **kwargs)


class HealthInspectionSubmission(Submission):
    commodity_type = models.ForeignKey(
        CommodityType,
        on_delete=models.CASCADE,
        related_name="health_inspection_submissions",
        verbose_name=_("Commodity Type"),
    )

    target_content_type = models.ForeignKey(
        ContentType, on_delete=models.CASCADE, null=True, blank=True
    )
    target_object_id = models.UUIDField(null=True, blank=True)
    target_submission = GenericForeignKey("target_content_type", "target_object_id")

    inspection_type = models.CharField(_("Inspection Type"), max_length=200)
    product_name = models.CharField(_("Product Name"), max_length=100)
    health_date = models.CharField(_("Health Date"), max_length=20, null=True, blank=True)
    quantity = models.DecimalField(_("Quantity"), max_digits=15, decimal_places=2)
    health_nepali_year = models.PositiveIntegerField(null=True, blank=True, db_index=True)
    health_nepali_month = models.PositiveSmallIntegerField(
        null=True, blank=True, db_index=True
    )
    health_nepali_day = models.PositiveSmallIntegerField(null=True, blank=True)
    health_nepali_quarter = models.PositiveSmallIntegerField(
        null=True, blank=True, db_index=True
    )

    unit_of_measurement = models.ForeignKey(
        UnitOfMeasurement,
        on_delete=models.CASCADE,
        related_name="health_inspection_submissions",
        verbose_name=_("Unit of Measurement"),
    )

    result = models.CharField(_("Result"), max_length=100, blank=True, null=True)
    notes = models.TextField(_("Notes"), blank=True, null=True)

    class Meta:
        db_table = "record_health_inspection_submission"
        verbose_name = _("Health Inspection Submission")
        verbose_name_plural = _("Health Inspection Submissions")

    def save(self, *args, **kwargs):
        parsed_parts = _parse_bs_date_parts(self.health_date)
        if parsed_parts:
            (
                self.health_nepali_year,
                self.health_nepali_month,
                self.health_nepali_day,
                self.health_nepali_quarter,
            ) = parsed_parts
        else:
            (
                self.health_nepali_year,
                self.health_nepali_month,
                self.health_nepali_day,
                self.health_nepali_quarter,
            ) = _fallback_submission_bs_parts(self)
            if self.health_date:
                logger.warning(
                    "Could not parse health_date '%s' for submission %s.",
                    self.health_date,
                    self.pk,
                )
        super().save(*args, **kwargs)


class DestructionSubmission(Submission):
    commodity_type = models.ForeignKey(
        CommodityType,
        on_delete=models.CASCADE,
        related_name="destruction_submissions",
        verbose_name=_("Commodity Type"),
    )

    target_content_type = models.ForeignKey(
        ContentType, on_delete=models.CASCADE, null=True, blank=True
    )
    target_object_id = models.UUIDField(null=True, blank=True)
    target_submission = GenericForeignKey("target_content_type", "target_object_id")

    quantity = models.DecimalField(_("Quantity"), max_digits=15, decimal_places=2)
    unit_of_measurement = models.ForeignKey(
        UnitOfMeasurement,
        on_delete=models.CASCADE,
        related_name="destruction_submissions",
        verbose_name=_("Unit of Measurement"),
    )

    destruction_method = models.CharField(_("Destruction Method"), max_length=200)
    destruction_date = models.CharField(_("Destruction Date"), max_length=20, null=True, blank=True)
    reason = models.TextField(_("Reason"), blank=True, null=True)

    destruction_nepali_year = models.PositiveIntegerField(null=True, blank=True, db_index=True)
    destruction_nepali_month = models.PositiveSmallIntegerField(null=True, blank=True, db_index=True)
    destruction_nepali_day = models.PositiveSmallIntegerField(null=True, blank=True)
    destruction_nepali_quarter = models.PositiveSmallIntegerField(null=True, blank=True, db_index=True)

    class Meta:
        db_table = "record_destruction_submission"
        verbose_name = _("Destruction Submission")
        verbose_name_plural = _("Destruction Submissions")

    def save(self, *args, **kwargs):
        parsed_parts = _parse_bs_date_parts(self.destruction_date)
        if parsed_parts:
            (
                self.destruction_nepali_year,
                self.destruction_nepali_month,
                self.destruction_nepali_day,
                self.destruction_nepali_quarter,
            ) = parsed_parts
        else:
            (
                self.destruction_nepali_year,
                self.destruction_nepali_month,
                self.destruction_nepali_day,
                self.destruction_nepali_quarter,
            ) = _fallback_submission_bs_parts(self)
            if self.destruction_date:
                logger.warning(
                    "Could not parse destruction_date '%s' for submission %s.",
                    self.destruction_date,
                    self.pk,
                )
        super().save(*args, **kwargs)


class Document(UUIDTimeStampedModel):
    submission = models.ForeignKey(
        Submission,
        on_delete=models.CASCADE,
        related_name="documents",
    )

    uploaded_by = models.ForeignKey(
        User,
        on_delete=models.CASCADE,
        related_name="documents_uploaded",
        verbose_name=_("Uploaded By"),
    )

    file = models.FileField(_("File"), upload_to=document_upload_path)

    is_verified = models.BooleanField(default=False)

    remarks = models.TextField(_("Remarks"), blank=True, null=True)

    class Meta:
        db_table = "record_document"
        verbose_name = _("Document")
        verbose_name_plural = _("Documents")

    def __str__(self):
        return self.file.name


class SubmissionStatusHistory(UUIDTimeStampedModel):
    submission = models.ForeignKey(
        Submission, on_delete=models.CASCADE, related_name="status_history"
    )

    updated_by = models.ForeignKey(
        User,
        on_delete=models.CASCADE,
        related_name="status_history_updated_by",
        verbose_name=_("Updated By"),
    )

    from_status = models.ForeignKey(
        SubmissionStatus,
        on_delete=models.CASCADE,
        related_name="status_history_from_status",
    )

    to_status = models.ForeignKey(
        SubmissionStatus,
        on_delete=models.CASCADE,
        related_name="status_history_to_status",
    )

    remarks = models.TextField(_("Remarks"), blank=True, null=True)

    class Meta:
        db_table = "record_submission_status_history"
