from django.db import models
from django.core.exceptions import ValidationError
from flower.common import ModelBase
from basic_info import models as basic_info_models
from functools import reduce
import decimal


# ==================== 枚举类定义 ====================

class StockFreezeStatusEnum(models.IntegerChoices):
    """库存冻结状态类型"""

    FROZEN = 1, '冻结中'
    CANCELLED = 2, '已取消'
    COMPLETED = 3, '已成单'

class StockChangeSourceEnum(models.IntegerChoices):
    """库存变动来源类型"""

    PURCHASE = 1, '采购'
    SALES_RETURN = 2, '销退'
    TRANSPORT_IN = 3, '调入'
    RECHECK_ADD = 4, '盘盈'
    COMBINE = 5, '合并'

    SALES = 6, '销售'
    PURCHASE_RETURN = 7, '采购退货'
    TRANSPORT_OUT = 8, '调出'
    RECHECK_REMOVE = 9, '盘亏'
    EXPLODE = 10, '拆卷'
    OFFSET = 11, '红冲'

    @classmethod
    def incoming_values(cls) -> set[int]:
        """返回被视为入库的来源类型"""
        return {
            cls.PURCHASE,
            cls.SALES_RETURN,
            cls.TRANSPORT_IN,
            cls.RECHECK_ADD,
            cls.COMBINE,
            cls.OFFSET,
        }

    @classmethod
    def outgoing_values(cls) -> set[int]:
        """返回被视为出库的来源类型"""
        return {
            cls.SALES,
            cls.PURCHASE_RETURN,
            cls.TRANSPORT_OUT,
            cls.RECHECK_REMOVE,
            cls.EXPLODE,
            cls.OFFSET,
        }


class StockChangeTypeEnum(models.IntegerChoices):
    """库存变动类型"""

    ADD = 1, '入库'
    REMOVE = 2, '出库'


# ==================== 模型定义 ====================

class StockChangeRecord(ModelBase):
    """库存变动记录模型"""

    id = models.BigAutoField(primary_key=True)
    merchant = models.ForeignKey(
        basic_info_models.Merchant, 
        on_delete=models.PROTECT, 
        related_name='stock_change_records', 
        verbose_name='所属商户'
    )
    type = models.IntegerField(choices=StockChangeTypeEnum.choices, verbose_name='变动类型')
    warehouse = models.ForeignKey(
        basic_info_models.WareHouse,
        on_delete=models.PROTECT,
        related_name='stock_change_records',
        verbose_name='仓库',
    )
    source_type = models.IntegerField(
        choices=StockChangeSourceEnum.choices, 
        verbose_name='变动来源',
    )
    created_by = models.ForeignKey(
        'auth.User',
        on_delete=models.PROTECT,
        null=True,
        blank=True,
        verbose_name='操作人',
    )
    source_id = models.BigIntegerField(null=True, blank=True, verbose_name='来源单据ID')
    is_finished = models.BooleanField(default=False, verbose_name='完成出入库')
    finished_at = models.DateTimeField(null=True, blank=True, verbose_name='完成时间')
    red_flush_id = models.UUIDField(null=True, blank=True, db_index=True, verbose_name='红冲批次ID')
    remarks = models.TextField(blank=True, null=True, verbose_name='备注')

    def __str__(self):
        return f'出入库记录 {self.id} - 仓库：{self.warehouse.name}'

    class Meta:
        verbose_name = '出入库记录'
        verbose_name_plural = '出入库记录'

    def clean(self):
        if self.type == StockChangeTypeEnum.ADD:
            if self.source_type not in StockChangeSourceEnum.incoming_values():
                raise ValidationError('入库记录的来源类型无效')
        elif self.type == StockChangeTypeEnum.REMOVE:
            if self.source_type not in StockChangeSourceEnum.outgoing_values():
                raise ValidationError('出库记录的来源类型无效')

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

    @property
    def is_incoming(self) -> bool:
        """判断是否为入库记录"""
        return self.type == StockChangeTypeEnum.ADD

    @property
    def is_outgoing(self) -> bool:
        """判断是否为出库记录"""
        return self.type == StockChangeTypeEnum.REMOVE

    @property
    def num_of_rolls(self) -> int:
        """获取匹数"""
        self.details.count()
        return self.details.count()

    @property
    def is_offset(self) -> bool:
        """是否为红冲记录（用于冲抵其他记录）"""
        return self.stock_snapshots.filter(offset_to__isnull=False).exists()

    @property
    def is_reversed(self) -> bool:
        """是否已被红冲"""
        return self.stock_snapshots.filter(cancelled=True).exists()

    @property
    def cancelled(self) -> bool:
        """兼容字段：是否已被红冲"""
        return self.is_reversed

    @property
    def delta_num_of_rolls(self) -> int:
        """计算库存变动的匹数增减"""

        # 入库为正，出库为负
        if self.type == StockChangeTypeEnum.ADD:
            return self.num_of_rolls
        elif self.type == StockChangeTypeEnum.REMOVE:
            return self.num_of_rolls * -1
        else:
            raise ValueError("未知的库存变动类型")

    
class StockChangeDetail(ModelBase):
    """库存变动明细模型"""

    id = models.BigAutoField(primary_key=True)
    merchant = models.ForeignKey(
        basic_info_models.Merchant, 
        on_delete=models.PROTECT, 
        related_name='stock_change_details', 
        verbose_name='所属商户'
    )
    product = models.ForeignKey(
        basic_info_models.Product,
        on_delete=models.PROTECT,
        related_name='stock_change_details',
        verbose_name='产品',
    )
    unit = models.IntegerField(
        verbose_name='单位', 
        choices=basic_info_models.ProductUnitEnum.choices,
    )
    stock_change_record = models.ForeignKey(
        StockChangeRecord,
        on_delete=models.CASCADE,
        related_name='details',
        verbose_name='库存变动记录',
    )
    quantity = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='数量')
    is_consumed = models.BooleanField(default=False, verbose_name='是否已消耗')
    consume_with = models.OneToOneField(
        'self',
        on_delete=models.PROTECT,
        null=True,
        blank=True,
        related_name='consumed_by_detail',
        verbose_name='所消耗的入库明细',
    )

    def __str__(self):
        return f'库存变动明细 {self.id} - 记录ID: {self.stock_change_record.id}'

    class Meta:
        verbose_name = '库存变动明细'
        verbose_name_plural = '库存变动明细'


class Inventory(ModelBase):
    """库存模型"""

    id = models.BigAutoField(primary_key=True)
    merchant = models.ForeignKey(
        basic_info_models.Merchant, 
        on_delete=models.PROTECT, 
        related_name='inventories', 
        verbose_name='所属商户'
    )
    product = models.ForeignKey(
        basic_info_models.Product,
        on_delete=models.PROTECT,
        related_name='inventories',
        verbose_name='产品',
    )
    warehouse = models.ForeignKey(
        basic_info_models.WareHouse,
        on_delete=models.PROTECT,
        related_name='inventories',
        verbose_name='仓库',
    )
    quantity = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='库存数量')
    num_of_rolls = models.IntegerField(default=1, verbose_name='匹数')
    # minimum_quantity = models.IntegerField(null=True, blank=True, verbose_name='最低库存量')
    spec = models.CharField(max_length=100, blank=True, null=True, verbose_name='规格')
    description = models.TextField(blank=True, null=True, verbose_name='备注描述')

    def __str__(self):
        return f'库存 {self.product.name} - 仓库: {self.warehouse.name}'

    class Meta:
        verbose_name = '库存'
        verbose_name_plural = '库存'
        unique_together = ('product', 'warehouse')


class StockSnapshot(ModelBase):
    """库存变动快照模型"""

    id = models.BigAutoField(primary_key=True)
    merchant = models.ForeignKey(
        basic_info_models.Merchant, 
        on_delete=models.PROTECT, 
        related_name='stock_snapshots', 
        verbose_name='所属商户'
    )
    product = models.ForeignKey(
        basic_info_models.Product,
        on_delete=models.PROTECT,
        related_name='stock_snapshots',
        verbose_name='产品',
    )
    warehouse = models.ForeignKey(
        basic_info_models.WareHouse,
        on_delete=models.PROTECT,
        related_name='stock_snapshots',
        verbose_name='仓库',
    )
    delta = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='库存变动数量')
    quantity_before = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='变动前库存数量')
    quantity_after = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='变动后库存数量')
    stock_change_record = models.ForeignKey(
        StockChangeRecord,
        on_delete=models.PROTECT,
        related_name='stock_snapshots',
        verbose_name='关联的库存变动记录',
    )
    unit = models.IntegerField(
        verbose_name='单位', 
        choices=basic_info_models.ProductUnitEnum.choices,
    )
    num_of_rolls = models.IntegerField(default=1, verbose_name='匹数')

    # offset_to表示该快照被冲抵到的目标快照ID
    # 当offset_to不为空时，cancelled不可为True 及 offset_id只能为空
    # 因为该记录是冲抵记录无法再取消，也不可能冲抵的同时被冲抵
    offset_to = models.BigIntegerField(
        null=True,
        blank=True,
        verbose_name='冲抵目标ID',
    )
    offset_at = models.DateTimeField(
        null=True,
        blank=True,
        verbose_name='冲抵时间',
    )

    # 当cancelled为True时，offset_id不可为空，表示该快照已被冲抵
    cancelled = models.BooleanField(default=False, verbose_name='已取消')
    cancelled_at = models.DateTimeField(
        null=True,
        blank=True,
        verbose_name='取消时间',
    )
    offset_id = models.BigIntegerField(
        null=True, 
        blank=True, 
        verbose_name='冲抵记录ID',
    )

    def __str__(self):
        return f'库存快照 {self.id} - 产品: {self.product.name}'

    class Meta:
        verbose_name = '库存快照'
        verbose_name_plural = '库存快照'


class StockFreeze(ModelBase):
    """库存冻结模型"""

    id = models.BigAutoField(primary_key=True)
    merchant = models.ForeignKey(
        basic_info_models.Merchant, 
        on_delete=models.PROTECT, 
        related_name='stock_freezes', 
        verbose_name='所属商户'
    )
    product = models.ForeignKey(
        basic_info_models.Product,
        on_delete=models.PROTECT,
        related_name='stock_freezes',
        verbose_name='产品',
    )
    warehouse = models.ForeignKey(
        basic_info_models.WareHouse,
        on_delete=models.PROTECT,
        related_name='stock_freezes',
        verbose_name='仓库',
    )
    stock_detail = models.ForeignKey(
        StockChangeDetail,
        on_delete=models.PROTECT,
        related_name='stock_freezes',
        verbose_name='关联的库存变动明细',
    )
    quantity = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='冻结数量')
    unit = models.IntegerField(
        verbose_name='单位', 
        choices=basic_info_models.ProductUnitEnum.choices,
    )
    status = models.IntegerField(
        choices=StockFreezeStatusEnum.choices,
        default=StockFreezeStatusEnum.FROZEN,
        verbose_name='冻结状态',
    )
    frozen_by = models.ForeignKey(
        'auth.User',
        on_delete=models.PROTECT,
        verbose_name='冻结操作人',
    )
    frozen_with = models.BigIntegerField(null=True, blank=True, verbose_name='冻结关联ID')
    cancelled_by = models.ForeignKey(
        'auth.User',
        on_delete=models.PROTECT,
        null=True,
        blank=True,
        related_name='cancelled_stock_freezes',
        verbose_name='取消操作人',
    )
    completed_by = models.ForeignKey(
        'auth.User',
        on_delete=models.PROTECT,
        null=True,
        blank=True,
        related_name='completed_stock_freezes',
        verbose_name='成单操作人',
    )
    completed_at = models.DateTimeField(null=True, blank=True, verbose_name='成单时间')
    completed_with = models.BigIntegerField(null=True, blank=True, verbose_name='成单关联ID')
    cancelled_at = models.DateTimeField(null=True, blank=True, verbose_name='取消时间')
    reason = models.TextField(blank=True, null=True, verbose_name='冻结原因')

    def __str__(self):
        return f'库存冻结 {self.id} - 产品: {self.product.name}'

    class Meta:
        verbose_name = '库存冻结'
        verbose_name_plural = '库存冻结'


class TransferOrderStatusEnum(models.IntegerChoices):
    DRAFT = 1, '待完成'
    COMPLETED = 2, '已完成'
    CANCELLED = 3, '已取消'


class TransferOrder(ModelBase):
    """跨仓调拨单"""

    id = models.BigAutoField(primary_key=True)
    merchant = models.ForeignKey(
        basic_info_models.Merchant,
        on_delete=models.PROTECT,
        related_name='transfer_orders',
        verbose_name='所属商户',
    )
    from_warehouse = models.ForeignKey(
        basic_info_models.WareHouse,
        on_delete=models.PROTECT,
        related_name='outgoing_transfer_orders',
        verbose_name='调出仓',
    )
    to_warehouse = models.ForeignKey(
        basic_info_models.WareHouse,
        on_delete=models.PROTECT,
        related_name='incoming_transfer_orders',
        verbose_name='调入仓',
    )
    mode = models.IntegerField(
        choices=basic_info_models.WareHouseModeEnum.choices,
        verbose_name='调拨模式',
    )
    operator = models.ForeignKey(
        basic_info_models.Employee,
        on_delete=models.PROTECT,
        related_name='transfer_orders',
        verbose_name='经办人',
    )
    created_by = models.ForeignKey(
        'auth.User',
        on_delete=models.PROTECT,
        null=True,
        blank=True,
        related_name='created_transfer_orders',
        verbose_name='创建人',
    )
    transfer_date = models.DateField(verbose_name='调拨日期')
    status = models.IntegerField(
        choices=TransferOrderStatusEnum.choices,
        default=TransferOrderStatusEnum.DRAFT,
        verbose_name='状态',
    )
    remarks = models.TextField(blank=True, null=True, verbose_name='备注')
    request_id = models.CharField(max_length=64, blank=True, null=True, verbose_name='请求ID')
    outgoing_record = models.ForeignKey(
        'stock.StockChangeRecord',
        on_delete=models.PROTECT,
        related_name='transfer_out_orders',
        null=True,
        blank=True,
        verbose_name='调出出入库记录',
    )
    incoming_record = models.ForeignKey(
        'stock.StockChangeRecord',
        on_delete=models.PROTECT,
        related_name='transfer_in_orders',
        null=True,
        blank=True,
        verbose_name='调入出入库记录',
    )

    class Meta:
        verbose_name = '调拨单'
        verbose_name_plural = '调拨单'
        constraints = [
            models.UniqueConstraint(
                fields=['merchant', 'request_id'],
                condition=models.Q(request_id__isnull=False),
                name='unique_transfer_request_per_merchant',
            ),
            models.CheckConstraint(
                check=~models.Q(from_warehouse=models.F('to_warehouse')),
                name='transfer_different_warehouses',
            ),
        ]

    def clean(self):
        super().clean()
        if self.from_warehouse_id and self.to_warehouse_id:
            if self.from_warehouse_id == self.to_warehouse_id:
                raise ValidationError('调拨的出入库仓库不能相同')
            if self.from_warehouse.merchant_id != self.merchant_id or self.to_warehouse.merchant_id != self.merchant_id:
                raise ValidationError('调拨仓库必须属于当前商户')
            if self.from_warehouse.mode != self.to_warehouse.mode:
                raise ValidationError('调拨要求出入库仓库的模式一致')
            if self.mode and self.mode != self.from_warehouse.mode:
                raise ValidationError('调拨模式与仓库模式不一致')

    def __str__(self):
        return f'调拨单 {self.id} - {self.from_warehouse.name} -> {self.to_warehouse.name}'


class TransferOrderItem(ModelBase):
    id = models.BigAutoField(primary_key=True)
    transfer_order = models.ForeignKey(
        TransferOrder,
        on_delete=models.CASCADE,
        related_name='items',
        verbose_name='调拨单',
    )
    product = models.ForeignKey(
        basic_info_models.Product,
        on_delete=models.PROTECT,
        related_name='transfer_order_items',
        verbose_name='产品',
    )
    unit = models.IntegerField(
        choices=basic_info_models.ProductUnitEnum.choices,
        verbose_name='单位',
    )
    total_quantity = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='数量合计')
    num_of_rolls = models.PositiveIntegerField(default=1, verbose_name='匹数')

    class Meta:
        verbose_name = '调拨单明细'
        verbose_name_plural = '调拨单明细'

    def __str__(self):
        return f'调拨明细 {self.id} - 产品: {self.product.name}'
