# coding=utf-8

from django.db.models import Sum,F
from django.utils import timezone

from apps.exceptions import CustomError

from apps.goods.models import GoodsGodownEntry,GoodsGodownEntryDetail
from apps.purchase.models import GodownEntry,GodownEntryDetail
from apps.warehouse.models import WarehouseStock,WarehouseRecord,WarehouseStockRecord,WarehouseRecordDetail,WarehouseBackMap,Inventory,InventoryDetail
from apps.product.models import ProductBase
from apps.base import Formater

class BizWarehouse():
    # 更新冗余字段
    @staticmethod
    def updateRedundant(warehouse_record):
        type = warehouse_record.type

        if type in (WarehouseRecord.CK_ZJ,WarehouseRecord.CK_XS,WarehouseRecord.CK_PK,WarehouseRecord.TL):
            BizWarehouse._updateDeliveredRedundant(warehouse_record)
        elif type == WarehouseRecord.TH:
            BizWarehouse._updateEntryBackRedundant(warehouse_record)

    @staticmethod
    def _updateDeliveredRedundant(warehouse_record):
        goods_entry_ids = []
        inventory_ids = []
        entry_ids = []
        rows = WarehouseRecordDetail.objects.filter(warehouse_record=warehouse_record)
        for row in rows:
            stock_record = row.warehouse_stock_record
            deliver_count = BizWarehouse._getDeliveredCount(stock_record)
            deliver_amount = deliver_count * stock_record.entry_price

            goods_entry_detail = GoodsGodownEntryDetail.objects.filter(stock_record=stock_record).first()
            inventory_detail = InventoryDetail.objects.filter(warehouse_stock_record=stock_record).first()
            entry_detail = GodownEntryDetail.objects.filter(stock_record=stock_record).first()
            if goods_entry_detail:
                goods_entry_detail.deliver_count = deliver_count
                goods_entry_detail.deliver_amount = deliver_amount
                goods_entry_detail.save()
                goods_entry_ids.append(goods_entry_detail.main_id)
            if inventory_detail:
                inventory_detail.deliver_count = deliver_count
                inventory_detail.deliver_amount = deliver_amount
                inventory_detail.save()
                inventory_ids.append(inventory_detail.main_id)
            if entry_detail:
                entry_detail.deliver_count = deliver_count
                entry_detail.deliver_amount = deliver_amount
                entry_detail.save()
                entry_ids.append(entry_detail.main_id)
        BizWarehouse._updateMainRedundant(goods_entry_ids,inventory_ids,entry_ids)

    @staticmethod
    def _updateEntryBackRedundant(warehouse_record):
        goods_entry_ids = []
        inventory_ids = []
        entry_ids = []
        rows = WarehouseRecordDetail.objects.filter(warehouse_record=warehouse_record)
        for row in rows:
            stock_record = row.warehouse_stock_record
            return_count = BizWarehouse._getEntryBackCount(stock_record)
            return_amount = return_count * stock_record.entry_price

            inventory_detail = InventoryDetail.objects.filter(warehouse_stock_record=stock_record).first()
            entry_detail = GodownEntryDetail.objects.filter(stock_record=stock_record).first()
            if inventory_detail:
                inventory_detail.return_count = return_count
                inventory_detail.return_amount = return_amount
                inventory_detail.save()
                inventory_ids.append(inventory_detail.main_id)
            if entry_detail:
                entry_detail.return_count = return_count
                entry_detail.return_amount = return_amount
                entry_detail.save()
                entry_ids.append(entry_detail.main_id)
        BizWarehouse._updateMainRedundant(goods_entry_ids, inventory_ids, entry_ids)

    @staticmethod
    def _getDeliveredCount(stock_record):
        count = 0
        deliver_types = (WarehouseRecord.CK_ZJ,WarehouseRecord.CK_XS,WarehouseRecord.CK_PK,WarehouseRecord.TL)

        sum_row = WarehouseRecordDetail.objects.filter(
            warehouse_stock_record=stock_record,
            warehouse_record__type__in=deliver_types
        ).aggregate(total_count=Sum('count'))
        if sum_row:
            count = sum_row['total_count'] or 0
        return -count

    @staticmethod
    def _getEntryBackCount(stock_record):
        count = 0
        sum_row = WarehouseRecordDetail.objects.filter(
            warehouse_stock_record=stock_record,
            warehouse_record__type=WarehouseRecord.TH
        ).aggregate(total_count=Sum('count'))
        if sum_row:
            count = sum_row['total_count'] or 0
        return -count

    @staticmethod
    def _updateMainRedundant(goods_entry_ids,inventory_ids,entry_ids):
        goods_entry_ids = list(set(goods_entry_ids))
        inventory_ids = list(set(inventory_ids))
        entry_ids = list(set(entry_ids))

        for id in goods_entry_ids:
            row = GoodsGodownEntry.objects.filter(id=id).first()
            if row:
                row.update_redundant()
        for id in inventory_ids:
            row = Inventory.objects.filter(id=id).first()
            if row:
                row.update_redundant()
        for id in entry_ids:
            row = GodownEntry.objects.filter(id=id).first()
            if row:
                row.update_redundant()

    # 获取金额
    @staticmethod
    def tryDelivered(product,warehouse,count):
        amount = 0
        amount2 = 0
        max_entry_price = 0
        max_entry_price2 = 0

        stock_records = WarehouseStockRecord.objects.filter(
            product=product,
            warehouse=warehouse,
            surplus_count__gt=0
        ).order_by('entry_time')

        for row in stock_records:
            if row.surplus_count >= count:
                cur_count = count
            else:
                cur_count = row.surplus_count

            amount += cur_count * row.entry_price
            amount2 += cur_count * row.entry_price2

            if row.entry_price > max_entry_price:
                max_entry_price = row.entry_price
                max_entry_price2 = row.entry_price2

            count -= cur_count
            if not count:
                break

        if count:
            warehouse_stock = WarehouseStock.getByWarehouseAndProduct(warehouse,product)
            amount += count * warehouse_stock.avg_cost_price
            amount2 += count * warehouse_stock.avg_cost_price2

            if warehouse_stock.avg_cost_price > max_entry_price:
                max_entry_price = warehouse_stock.avg_cost_price
                max_entry_price2 = warehouse_stock.avg_cost_price2

        return amount, amount2, max_entry_price, max_entry_price2

    # 入库
    @staticmethod
    def entry(type,product,warehouse,supplier,entry_count,entry_price,entry_price2):
        rk_types = (WarehouseRecord.RK_ZJ,WarehouseRecord.RK_CG,WarehouseRecord.RK_PY)

        if entry_count <= 0:
            raise CustomError(u'[%s]入库数量不能小于等于0' % product.name)
        if entry_price < 0 or entry_price2 < 0:
            raise CustomError(u'[%s]入库价不能小于0' % product.name)
        if type not in rk_types:
            raise CustomError(u'[%s]入库类型错误' % product.name)

        warehouse_record = WarehouseRecord.objects.create(
            type=type,
            product=product,
            warehouse=warehouse,
            count=entry_count,
            amount=entry_count * entry_price,
            amount2=entry_count * entry_price2
        )
        warehouse_stock_record = WarehouseStockRecord.objects.create(
            product=product,
            warehouse=warehouse,
            supplier=supplier,
            entry_count=entry_count,
            entry_price=entry_price,
            entry_price2=entry_price2,
            surplus_count=entry_count
        )
        WarehouseRecordDetail.objects.create(
            warehouse_record=warehouse_record,
            warehouse_stock_record=warehouse_stock_record,
            count=entry_count
        )

        warehouse_stock = WarehouseStock.getByWarehouseAndProduct(warehouse,product)

        if type != WarehouseRecord.RK_PY:
            warehouse_stock.last_entry_price = entry_price
            product.last_entry_price = entry_price
        warehouse_stock.last_entry_time = timezone.now()
        product.last_entry_time = timezone.now()

        BizWarehouse._updateStock(warehouse_stock, warehouse_record)
        BizWarehouse._updateProductStock(product)

        return warehouse_stock_record

    # 出库
    @staticmethod
    def delivered(type,product,warehouse,count):
        ck_types = (WarehouseRecord.CK_ZJ,WarehouseRecord.CK_XS,WarehouseRecord.CK_PK)

        if count <= 0:
            raise CustomError(u'[%s]出库数量不能小于等于0' % product.name)
        if type not in ck_types:
            raise CustomError(u'[%s]出库类型错误' % product.name)

        stock_records = WarehouseStockRecord.objects.filter(
            product=product,
            warehouse=warehouse,
            surplus_count__gt=0
        ).order_by('entry_time')

        warehouse_record = WarehouseRecord.objects.create(
            type=type,
            product=product,
            warehouse=warehouse,
            count=-count,
            amount=0,
            amount2=0
        )
        BizWarehouse._deliveredForStockRecords(warehouse_record,stock_records)

        warehouse_stock = WarehouseStock.getByWarehouseAndProduct(warehouse,product)
        warehouse_stock.last_deliverd_time = timezone.now()
        product.last_deliverd_time = timezone.now()

        BizWarehouse._updateStock(warehouse_stock,warehouse_record)
        BizWarehouse._updateProductStock(product)

        BizWarehouse.updateRedundant(warehouse_record)
        return warehouse_record

    # 根据指定库存出库
    @staticmethod
    def deliveredByStockRecord(type,stock_record,count):
        product = stock_record.product
        warehouse = stock_record.warehouse

        if count <= 0:
            raise CustomError(u'[%s]出库数量不能小于等于0' % product.name)
        if count > stock_record.surplus_count:
            raise CustomError(u'[%s]出库数量超出对应入库记录的剩余数量' % product.name)

        warehouse_record = WarehouseRecord.objects.create(
            type=type,
            product=product,
            warehouse=warehouse,
            count=-count,
            amount=0,
            amount2=0
        )

        stock_records = (stock_record,)
        BizWarehouse._deliveredForStockRecords(warehouse_record, stock_records)

        warehouse_stock = WarehouseStock.getByWarehouseAndProduct(warehouse,product)
        warehouse_stock.last_deliverd_time = timezone.now()
        product.last_deliverd_time = timezone.now()

        BizWarehouse._updateStock(warehouse_stock, warehouse_record)
        BizWarehouse._updateProductStock(product)

        BizWarehouse.updateRedundant(warehouse_record)
        return warehouse_record

    # 退料
    @staticmethod
    def deliveredBack(warehouse_record, count):
        if count <= 0:
            raise CustomError(u'[%s]退料数量不能小于等于0' % warehouse_record.product.name)

        data = BizWarehouse._getDeliveredBackDetail(warehouse_record, count)
        res = BizWarehouse._deliveredBackByDetails(WarehouseRecord.TL,warehouse_record,data)

        BizWarehouse.updateRedundant(res)
        return res

    # 退货
    @staticmethod
    def entryBack(stock_record,count):
        product = stock_record.product
        warehouse = stock_record.warehouse

        if count <= 0:
            raise CustomError(u'[%s]退货数量不能小于等于0' % product.name)
        if count > stock_record.surplus_count:
            raise CustomError(u'[%s]退货数量超出对应入库记录的剩余数量' % product.name)

        warehouse_record = WarehouseRecord.objects.create(
            type=WarehouseRecord.TH,
            product=product,
            warehouse=warehouse,
            count=-count,
            amount=0,
            amount2=0
        )

        stock_records = (stock_record,)
        BizWarehouse._deliveredForStockRecords(warehouse_record, stock_records)

        warehouse_stock = WarehouseStock.getByWarehouseAndProduct(warehouse,product)
        BizWarehouse._updateStock(warehouse_stock, warehouse_record)
        BizWarehouse._updateProductStock(product)

        BizWarehouse.updateRedundant(warehouse_record)
        return warehouse_record

    # 批量退货
    @staticmethod
    def entryBatchBack(product,warehouse,count,supplier=None):
        if count <= 0:
            raise CustomError(u'[%s]退货数量不能小于等于0' % product.name)

        stock_records = WarehouseStockRecord.objects.filter(
            product=product,
            warehouse=warehouse,
            surplus_count__gt=0
        )
        if supplier:
            stock_records = stock_records.filter(
                supplier=supplier
            )
        stock_records = stock_records.order_by('entry_time')

        warehouse_record = WarehouseRecord.objects.create(
            type=WarehouseRecord.TH,
            product=product,
            warehouse=warehouse,
            count=-count,
            amount=0,
            amount2=0
        )
        BizWarehouse._deliveredForStockRecords(warehouse_record, stock_records)

        warehouse_stock = WarehouseStock.getByWarehouseAndProduct(warehouse,product)
        BizWarehouse._updateStock(warehouse_stock, warehouse_record)
        BizWarehouse._updateProductStock(product)

        BizWarehouse.updateRedundant(warehouse_record)
        return warehouse_record

    @staticmethod
    def _getDeliveredBackDetail(warehouse_record, count):
        details = WarehouseRecordDetail.objects.filter(
            warehouse_record=warehouse_record
        ).order_by('-warehouse_stock_record__entry_time')

        data = []
        for detail in details:
            back_count = (WarehouseBackMap.objects.filter(delivered_detail=detail).aggregate(count_sum=Sum('back_count')))['count_sum'] or 0
            if back_count >= -detail.count:
                continue
            temp = -(detail.count + back_count)
            if temp >= count:
                cur_count = count
            else:
                cur_count = temp
            data.append({'delivered_detail': detail, 'back_count': cur_count})

            count -= cur_count
            if not count:
                break
        if count:
            raise CustomError(u'[%s]退料数量超出出库剩余数量' % warehouse_record.product.name)
        return data

    @staticmethod
    def _deliveredBackByDetails(type, warehouse_record, details):
        product = warehouse_record.product
        warehouse = warehouse_record.warehouse

        item = WarehouseRecord.objects.create(
            type=type,
            product=warehouse_record.product,
            warehouse=warehouse_record.warehouse,
            count=0,
            amount=0,
            amount2=0
        )

        for detail in details:
            warehouse_stock_record = detail['delivered_detail'].warehouse_stock_record
            back_detail = WarehouseRecordDetail.objects.create(
                warehouse_record=item,
                warehouse_stock_record=warehouse_stock_record,
                count=detail['back_count']
            )
            warehouse_stock_record.surplus_count += detail['back_count']
            warehouse_stock_record.save()

            WarehouseBackMap.objects.create(
                back_detail=back_detail,
                delivered_detail=detail['delivered_detail'],
                back_count=detail['back_count']
            )
            item.count += detail['back_count']

        sum_row = WarehouseRecordDetail.objects.filter(
            warehouse_record=item
        ).aggregate(
            sum_amount=Sum(F('count') * F('warehouse_stock_record__entry_price')),
            sum_amount2=Sum(F('count') * F('warehouse_stock_record__entry_price2'))
        )
        item.amount = sum_row['sum_amount']
        item.amount2 = sum_row['sum_amount2']
        item.save()

        warehouse_stock = WarehouseStock.getByWarehouseAndProduct(warehouse,product)
        BizWarehouse._updateStock(warehouse_stock, item)
        BizWarehouse._updateProductStock(product)
        return item

    @staticmethod
    def _deliveredForStockRecords(warehouse_record,stock_records):
        product = warehouse_record.product

        temp_count = -warehouse_record.count
        for row in stock_records:
            if row.surplus_count >= temp_count:
                cur_count = temp_count
            else:
                cur_count = row.surplus_count
            row.surplus_count -= cur_count
            row.save()

            WarehouseRecordDetail.objects.create(
                warehouse_record=warehouse_record,
                warehouse_stock_record=row,
                count=-cur_count
            )

            temp_count -= cur_count
            if not temp_count:
                break

        if temp_count:
            raise CustomError(u'[%s]库存不足' % product.name)

        sum_row = WarehouseRecordDetail.objects.filter(
            warehouse_record=warehouse_record
        ).aggregate(
            sum_amount=Sum(F('count') * F('warehouse_stock_record__entry_price')),
            sum_amount2=Sum(F('count') * F('warehouse_stock_record__entry_price2'))
        )
        warehouse_record.amount = sum_row['sum_amount']
        warehouse_record.amount2 = sum_row['sum_amount2']
        warehouse_record.save()

    @staticmethod
    def _updateStock(warehouse_stock,warehouse_record):
        warehouse_stock.count += warehouse_record.count
        warehouse_stock.amount += warehouse_record.amount
        warehouse_stock.amount2 += warehouse_record.amount2
        if warehouse_stock.count != 0:
            warehouse_stock.avg_cost_price = warehouse_stock.amount / warehouse_stock.count
            warehouse_stock.avg_cost_price2 = warehouse_stock.amount2 / warehouse_stock.count

        warehouse_record.cur_count = warehouse_stock.count
        warehouse_record.cur_amount = warehouse_stock.amount
        warehouse_record.cur_amount2 = warehouse_stock.amount2
        warehouse_record.save()
        warehouse_stock.save()

    @staticmethod
    def _updateProductStock(product):
        sum_row = WarehouseStock.objects.filter(
            product=product
        ).aggregate(
            count=Sum('count'),
            amount=Sum('amount'),
            amount2=Sum('amount2')
        )
        product.stock_count = sum_row['count']
        product.stock_amount = sum_row['amount']
        product.stock_amount2 = sum_row['amount2']
        if product.stock_count != 0:
            product.avg_cost_price = product.stock_amount / product.stock_count
            product.avg_cost_price2 = product.stock_amount2 / product.stock_count
        product.save()


class GetWarehouseSrockRecord():
    @staticmethod
    def getRecord(product, warehouse, supplier=None):
        record_data = []
        record_rows = WarehouseStockRecord.objects.filter(warehouse_id=warehouse, product_id=product,
                                                          surplus_count__gt=0)
        if supplier:
            record_rows = record_rows.filter(supplier_id=supplier)
        for record_row in record_rows:
            product_base = record_row.product
            if product_base.type == ProductBase.GOODS:
                g_row = GoodsGodownEntryDetail.objects.filter(stock_record_id=record_row.id).first()
            else:
                g_row = GodownEntryDetail.objects.filter(stock_record_id=record_row.id).first()

            if not g_row:
                g_row = InventoryDetail.objects.filter(warehouse_stock_record_id=record_row.id).first()

            record_item = {
                'stock_id': record_row.id,
                'no': g_row.main.no,
                'entry_price': Formater.formatPriceShow(record_row.entry_price),
                'surplus_count': Formater.formatCountShow(record_row.surplus_count),
                'detail_id': g_row.id,
                'notes': g_row.main.notes or ''
            }

            record_data.append(record_item)

        return record_data