package com.fjhx.service.stock.impl;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fjhx.constants.StatusConstant;
import com.fjhx.constants.StockJournalTypeConstant;
import com.fjhx.entity.stock.Stock;
import com.fjhx.entity.stock.StockJournal;
import com.fjhx.enums.stock.InTypeEnum;
import com.fjhx.enums.stock.OutTypeEnum;
import com.fjhx.mapper.stock.StockMapper;
import com.fjhx.params.stock.StockChangeVo;
import com.fjhx.params.stock.StockVo;
import com.fjhx.service.stock.StockService;
import com.fjhx.utils.Assert;
import com.fjhx.utils.WrapperUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;

/**
 * <p>
 * 库存 服务实现类
 * </p>
 *
 * @author ${author}
 * @since 2022-11-24
 */
@Service
public class StockServiceImpl extends ServiceImpl<StockMapper, Stock> implements StockService {

    @Autowired
    private StockJournalServiceImpl stockJournalService;

    @Override
    public Page<Stock> getPage(Map<String, Object> condition) {

        QueryWrapper<Stock> wrapper = Wrappers.query();

        WrapperUtil.init(condition, wrapper)
                .createTimeDesc();

        return page(condition, wrapper);
    }

    @Override
    public void add(StockVo stockVo) {
        save(stockVo);
    }

    @Override
    public void edit(StockVo stockVo) {
        updateById(stockVo);
    }

    @Override
    public void delete(StockVo stockVo) {
        removeById(stockVo.getId());
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void changeQuantity(StockChangeVo stockChangeVo) {
        Assert.notEmpty(stockChangeVo.getChangeDetailsList(), "变更数量数量列表为空");
        // 合并变更数量
        Map<String, Stock> map = mergeChangeQuantity(stockChangeVo);
        // 变更数量
        ChangeQuantity(map, stockChangeVo.getTypeEnum());
        // 添加流水记录
        addJournal(stockChangeVo);
    }

    /**
     * 合并变更数量(如果同一种物品在出入库一个仓库，则合并数量)
     */
    private Map<String, Stock> mergeChangeQuantity(StockChangeVo stockChangeVo) {

        List<? extends StockChangeVo.ChangeDetails> changeDetailsList = stockChangeVo.getChangeDetailsList();
        Enum<?> typeEnum = stockChangeVo.getTypeEnum();

        Map<String, Stock> map = new HashMap<>();
        for (StockChangeVo.ChangeDetails changeDetails : changeDetailsList) {
            // 变更数量
            BigDecimal changeQuantity = changeDetails.getChangeQuantity();
            // 变更数量小于等于0，则跳过
            if (changeQuantity.compareTo(BigDecimal.ZERO) <= 0) {
                continue;
            }
            // 仓库id
            Long warehouseId = ObjectUtil.defaultIfNull(changeDetails.getWarehouseId(), stockChangeVo.getDefaultWarehouseId());
            // 物品id
            Long goodsId = changeDetails.getGoodsId();
            // 生成mapKey
            String key = goodsId.toString() + warehouseId.toString();
            Stock stock = map.get(key);

            if (null == stock) {
                stock = new Stock();
                stock.setWarehouseId(warehouseId);
                stock.setGoodsId(goodsId);
                if (typeEnum instanceof InTypeEnum) {
                    stock.setFrozenQuantity(changeQuantity);
                } else {
                    stock.setQuantity(changeQuantity);
                }
                map.put(key, stock);
            } else {
                if (typeEnum instanceof InTypeEnum) {
                    stock.setFrozenQuantity(stock.getFrozenQuantity().add(changeQuantity));
                } else {
                    stock.setQuantity(stock.getQuantity().add(changeQuantity));
                }
            }
        }
        return map;
    }

    /**
     * 变更库存数量
     */
    private synchronized void ChangeQuantity(Map<String, Stock> map, Enum<?> typeEnum) {
        Set<String> keySet = map.keySet();

        List<Stock> list = list(Wrappers.<Stock>query().in("CONCAT(goods_id, warehouse_id)", keySet));
        for (Stock stock : list) {
            String key = stock.getGoodsId().toString() + stock.getWarehouseId().toString();
            Stock changeStock = map.get(key);

            changeStock.setId(stock.getId());
            if (typeEnum instanceof InTypeEnum) {
                changeStock.setFrozenQuantity(stock.getFrozenQuantity().add(changeStock.getFrozenQuantity()));
            } else {
                BigDecimal quantity = stock.getQuantity().subtract(changeStock.getQuantity());
                Assert.geZero(quantity, "产品库存不足，出库失败");
                changeStock.setQuantity(quantity);
            }
        }
        saveOrUpdateBatch(new ArrayList<>(map.values()));
    }

    /**
     * 添加流水记录
     */
    private void addJournal(StockChangeVo stockChangeVo) {
        List<? extends StockChangeVo.ChangeDetails> changeDetailsList = stockChangeVo.getChangeDetailsList();
        Enum<?> typeEnum = stockChangeVo.getTypeEnum();

        // 添加入库明细
        List<StockJournal> stockJournalList = new ArrayList<>();

        for (StockChangeVo.ChangeDetails changeDetails : changeDetailsList) {

            // 变更数量
            BigDecimal changeQuantity = changeDetails.getChangeQuantity();
            // 变更数量小于等于0，则跳过
            if (changeQuantity.compareTo(BigDecimal.ZERO) <= 0) {
                continue;
            }

            // 业务id
            Long businessId = ObjectUtil.defaultIfNull(changeDetails.getBusinessId(), stockChangeVo.getDefaultBusinessId());
            // 产品id
            Long goodsId = changeDetails.getGoodsId();
            // 仓库id
            Long warehouseId = ObjectUtil.defaultIfNull(changeDetails.getWarehouseId(), stockChangeVo.getDefaultWarehouseId());
            // 备注
            String remarks = ObjectUtil.defaultIfNull(changeDetails.getRemarks(), stockChangeVo.getDefaultRemarks());

            StockJournal stockJournal = new StockJournal();

            if (typeEnum instanceof InTypeEnum) {
                stockJournal.setType(StockJournalTypeConstant.IN);
                stockJournal.setDetailsType(((InTypeEnum) typeEnum).getKey());
                stockJournal.setActualQuantity(BigDecimal.ZERO);
                stockJournal.setIsInStock(StatusConstant.NO);
            } else {
                stockJournal.setType(StockJournalTypeConstant.OUT);
                stockJournal.setDetailsType(((OutTypeEnum) typeEnum).getKey());
            }

            stockJournal.setBusinessId(businessId);
            stockJournal.setGoodsId(goodsId);
            stockJournal.setWarehouseId(warehouseId);
            stockJournal.setChangeQuantity(changeQuantity);
            stockJournal.setRemarks(remarks);
            stockJournalList.add(stockJournal);
        }

        stockJournalService.saveBatch(stockJournalList);
    }

}
