"""Controlled compensating adjustments and position-scoped physical counts."""
from django.conf import settings
from django.db import models
from .models import InventoryRecord


class StockCount(InventoryRecord):
    @property
    def variance(self):
        if self.expected_quantity is None or self.counted_quantity is None:
            return None
        return self.counted_quantity - self.expected_quantity

    company = models.ForeignKey("organization.Company", on_delete=models.PROTECT)
    location = models.ForeignKey("inventory.InventoryLocation", on_delete=models.PROTECT)
    spare_part = models.ForeignKey("parts.SparePart", on_delete=models.PROTECT)
    created_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT, related_name="+")
    status = models.CharField(max_length=16, default="DRAFT", choices=[(x, x.title()) for x in ("DRAFT", "COUNTING", "RECONCILED", "CANCELLED")])
    expected_quantity = models.PositiveIntegerField(null=True, blank=True)
    counted_quantity = models.PositiveIntegerField(null=True, blank=True)
    started_at = models.DateTimeField(null=True, blank=True)
    started_by = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, on_delete=models.PROTECT, related_name="+")
    finished_at = models.DateTimeField(null=True, blank=True)
    finished_by = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, on_delete=models.PROTECT, related_name="+")
    note = models.TextField(max_length=2000, blank=True)
    reason = models.CharField(max_length=500, blank=True)

    class Meta:
        permissions = [("count_stock", "Start and record physical stock counts")]
        constraints = [
            models.UniqueConstraint(fields=["location", "spare_part"], condition=models.Q(status="COUNTING"), name="inventory_one_count_per_position"),
            models.CheckConstraint(condition=models.Q(status__in=["DRAFT", "COUNTING", "RECONCILED", "CANCELLED"]), name="inventory_count_status"),
            models.CheckConstraint(condition=(models.Q(status="DRAFT", started_at=None, started_by=None, expected_quantity=None, counted_quantity=None, finished_at=None, finished_by=None)
                | models.Q(status="COUNTING", started_at__isnull=False, started_by__isnull=False, expected_quantity__isnull=False, finished_at=None, finished_by=None)
                | models.Q(status="RECONCILED", started_at__isnull=False, started_by__isnull=False, expected_quantity__isnull=False, counted_quantity__isnull=False, finished_at__isnull=False, finished_by__isnull=False)
                | models.Q(status="CANCELLED", finished_at__isnull=False, finished_by__isnull=False)), name="inventory_count_evidence"),
            models.CheckConstraint(condition=models.Q(expected_quantity=None) | models.Q(expected_quantity__lte=1000000000), name="inventory_count_expected_bound"),
            models.CheckConstraint(condition=models.Q(counted_quantity=None) | models.Q(counted_quantity__lte=1000000000), name="inventory_count_observed_bound"),
            models.CheckConstraint(condition=models.Q(started_at=None) | models.Q(finished_at=None) | models.Q(finished_at__gte=models.F("started_at")), name="inventory_count_time"),
        ]


class StockCountUnit(InventoryRecord):
    count = models.ForeignKey(StockCount, on_delete=models.PROTECT, related_name="units")
    unit = models.ForeignKey("inventory.SerializedStockUnit", on_delete=models.PROTECT)
    expected = models.BooleanField(default=False)
    counted = models.BooleanField(default=False)

    class Meta:
        constraints = [models.UniqueConstraint(fields=["count", "unit"], name="inventory_count_unit_unique")]


class StockAdjustment(InventoryRecord):
    class Reason(models.TextChoices):
        LOSS = "LOSS", "Physical loss"
        FOUND = "FOUND", "Found stock"
        CORRECTION = "CORRECTION", "Documented stock correction"
        COUNT_VARIANCE = "COUNT_VARIANCE", "Approved physical count variance"

    company = models.ForeignKey("organization.Company", on_delete=models.PROTECT)
    location = models.ForeignKey("inventory.InventoryLocation", on_delete=models.PROTECT)
    spare_part = models.ForeignKey("parts.SparePart", on_delete=models.PROTECT)
    quantity_delta = models.IntegerField()
    reason = models.CharField(max_length=20, choices=Reason.choices)
    actor = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT)
    movement = models.OneToOneField("inventory.StockMovement", on_delete=models.PROTECT)
    count = models.ForeignKey(StockCount, null=True, blank=True, on_delete=models.PROTECT, related_name="adjustments")

    class Meta:
        permissions = [("adjust_stock", "Post controlled stock adjustments")]
        constraints = [
            models.CheckConstraint(condition=~models.Q(quantity_delta=0) & models.Q(quantity_delta__gte=-1000000000, quantity_delta__lte=1000000000), name="inventory_adjustment_delta"),
            models.CheckConstraint(condition=models.Q(reason__in=["LOSS", "FOUND", "CORRECTION", "COUNT_VARIANCE"]), name="inventory_adjustment_reason"),
            models.CheckConstraint(condition=(models.Q(reason="COUNT_VARIANCE", count__isnull=False) | (~models.Q(reason="COUNT_VARIANCE") & models.Q(count=None))), name="inventory_adjustment_count"),
            models.CheckConstraint(condition=~models.Q(reason="LOSS") | models.Q(quantity_delta__lt=0), name="inventory_loss_direction"),
            models.CheckConstraint(condition=~models.Q(reason="FOUND") | models.Q(quantity_delta__gt=0), name="inventory_found_direction"),
        ]
