import uuid

from django.db import models

from api.models.data.base import BaseModel
from api.models.data.products import Product
from api.models.data.raw_materials import RawMaterialStock
from api.models.data.stock import Stock
from api.models.data.storage_tank import StorageTank


class PackingRun(BaseModel):
    """A transaction that bottles bulk liquid from a tank into finished stock."""
    COMPONENT_BOTTLE = 'bottle'
    COMPONENT_CAP = 'cap'
    COMPONENT_LABEL = 'label'
    COMPONENT_PLASTIC = 'plastic'
    COMPONENT_CARTON = 'carton'
    COMPONENT_CHOICES = [
        (COMPONENT_BOTTLE, 'Bottle'),
        (COMPONENT_CAP, 'Cap'),
        (COMPONENT_LABEL, 'Label'),
        (COMPONENT_PLASTIC, 'Plastic'),
        (COMPONENT_CARTON, 'Carton'),
    ]

    packing_number = models.CharField(max_length=40, unique=True, blank=True)
    storage_tank = models.ForeignKey(
        StorageTank,
        on_delete=models.PROTECT,
        related_name='packing_runs',
    )
    product = models.ForeignKey(
        Product,
        on_delete=models.PROTECT,
        related_name='packing_runs',
        null=True,
        blank=True,
    )
    finished_stock = models.ForeignKey(
        Stock,
        on_delete=models.PROTECT,
        related_name='packing_runs',
        null=True,
        blank=True,
    )
    quantity_liters = models.DecimalField(max_digits=12, decimal_places=4)
    bottle_volume_liters = models.DecimalField(max_digits=8, decimal_places=4)
    bottle_count = models.PositiveIntegerField()
    liquid_cost_afn = models.DecimalField(max_digits=15, decimal_places=4)
    packaging_cost_afn = models.DecimalField(max_digits=15, decimal_places=4)
    total_cost_afn = models.DecimalField(max_digits=15, decimal_places=4)
    cost_per_bottle_afn = models.DecimalField(max_digits=15, decimal_places=6)
    notes = models.TextField(blank=True)

    class Meta:
        ordering = ['-created_at', '-id']

    def save(self, *args, **kwargs):
        if not self.packing_number:
            self.packing_number = f'PK-{uuid.uuid4().hex[:12].upper()}'
        super().save(*args, **kwargs)

    def __str__(self):
        return f'{self.packing_number} - {self.storage_tank.name}'


class PackingRunComponent(BaseModel):
    packing_run = models.ForeignKey(
        PackingRun,
        on_delete=models.CASCADE,
        related_name='components',
    )
    component_type = models.CharField(
        max_length=12,
        choices=PackingRun.COMPONENT_CHOICES,
    )
    stock = models.ForeignKey(
        Stock,
        on_delete=models.PROTECT,
        related_name='packing_consumptions',
        null=True,
        blank=True,
    )
    raw_material_stock = models.ForeignKey(
        RawMaterialStock,
        on_delete=models.PROTECT,
        related_name='packing_run_components',
        null=True,
        blank=True,
    )
    quantity_pieces = models.DecimalField(max_digits=12, decimal_places=4)
    unit_cost_afn = models.DecimalField(max_digits=15, decimal_places=6)
    total_cost_afn = models.DecimalField(max_digits=15, decimal_places=4)
    inventory_allocations = models.JSONField(default=list)

    class Meta:
        ordering = ['id']
        constraints = [
            models.UniqueConstraint(
                fields=['packing_run', 'component_type'],
                name='uniq_packing_run_component_type',
            ),
        ]

    def __str__(self):
        material = self.raw_material_stock or self.stock
        return f'{self.packing_run.packing_number} - {self.get_component_type_display()} ({material})'


class PackingBreakage(BaseModel):
    """
    Post-packing breakage of finished bottles.

    Fate of components for each broken bottle:
      - liquid  → returned to the source storage tank
      - bottle  → returned to the original raw-material vendor (AP credit)
      - label / cap / plastic (+ carton share) → scrap / waste expense
    """

    breakage_number = models.CharField(max_length=40, unique=True, blank=True)
    packing_run = models.ForeignKey(
        PackingRun,
        on_delete=models.PROTECT,
        related_name='breakages',
    )
    storage_tank = models.ForeignKey(
        StorageTank,
        on_delete=models.PROTECT,
        related_name='packing_breakages',
    )
    bottle_vendor = models.ForeignKey(
        'api.Vendor',
        on_delete=models.PROTECT,
        related_name='packing_breakage_returns',
        null=True,
        blank=True,
        help_text='Vendor who supplied the bottle raw material',
    )
    bottle_purchase_item = models.ForeignKey(
        'api.RawMaterialPurchaseItem',
        on_delete=models.PROTECT,
        related_name='packing_breakage_returns',
        null=True,
        blank=True,
    )
    broken_bottle_count = models.PositiveIntegerField()
    liquid_liters_returned = models.DecimalField(max_digits=12, decimal_places=4)
    liquid_cost_afn = models.DecimalField(max_digits=15, decimal_places=4, default=0)
    bottle_return_value_afn = models.DecimalField(
        max_digits=15, decimal_places=4, default=0,
        help_text='AP credit for bottles returned to vendor',
    )
    scrap_cost_afn = models.DecimalField(
        max_digits=15, decimal_places=4, default=0,
        help_text='Label + cap (+ plastic/carton share) written off as waste',
    )
    finished_stock_reduced = models.DecimalField(
        max_digits=12, decimal_places=4, default=0,
    )
    scrap_details = models.JSONField(
        default=dict,
        blank=True,
        help_text='Per-component scrap qty/cost breakdown',
    )
    notes = models.TextField(blank=True)

    class Meta:
        ordering = ['-created_at', '-id']

    def save(self, *args, **kwargs):
        if not self.breakage_number:
            self.breakage_number = f'PB-{uuid.uuid4().hex[:12].upper()}'
        super().save(*args, **kwargs)

    def __str__(self):
        return (
            f'{self.breakage_number} - {self.broken_bottle_count} bottles '
            f'from {self.packing_run.packing_number}'
        )
