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.base.Condition;
import com.fjhx.constants.StatusConstant;
import com.fjhx.entity.product.ProductCombination;
import com.fjhx.entity.product.ProductInfo;
import com.fjhx.entity.stock.Stock;
import com.fjhx.entity.stock.StockJournal;
import com.fjhx.entity.warehouse.Warehouse;
import com.fjhx.enums.stock.InTypeEnum;
import com.fjhx.enums.stock.OutTypeEnum;
import com.fjhx.mapper.stock.StockMapper;
import com.fjhx.params.stock.*;
import com.fjhx.service.product.ProductCombinationService;
import com.fjhx.service.stock.StockService;
import com.fjhx.utils.Assert;
import com.fjhx.utils.WrapperUtil;
import com.fjhx.utils.wrapperUtil.IWrapper;
import com.fjhx.utils.wrapperUtil.KeywordData;
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.*;
import java.util.stream.Collectors;

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

    @Autowired
    private StockJournalServiceImpl stockJournalService;

    @Autowired
    private ProductCombinationService productCombinationService;

    @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 List<StockJournal> changeQuantity(StockChangeVo stockChangeVo) {
        Assert.notEmpty(stockChangeVo.getChangeDetailsList(), "变更数量列表为空");
        // 合并变更数量
        Map<String, Stock> map = mergeChangeQuantity(stockChangeVo);
        if (map.size() == 0) {
            return new ArrayList<>();
        }
        // 变更数量
        ChangeQuantity(map, stockChangeVo.getTypeEnum());
        // 添加流水记录
        return stockJournalService.addJournal(stockChangeVo);
    }

    @Override
    public Page<Map<String, Object>> pageByWarehouseId(Condition condition) {
        Long warehouseId = condition.getLong("warehouseId");
        Assert.notEmpty(warehouseId, "请传入仓库id");
        return baseMapper.pageByWarehouseId(condition.getPage(), warehouseId);
    }

    @Override
    public Page<PageByWarehouseVo> pageByWarehouse(Condition condition) {

        IWrapper<Object> wrapper = IWrapper.getWrapper(condition);

        wrapper.keyword(new KeywordData("w", Warehouse::getName),
                new KeywordData("pi", ProductInfo::getCode),
                new KeywordData("pi", ProductInfo::getName)
        );

        return baseMapper.pageByWarehouse(condition.getPage(), wrapper);
    }

    @Override
    public Page<PageByProductVo> pageByProduct(Condition condition) {

        IWrapper<Object> wrapper = IWrapper.getWrapper(condition);

        wrapper.keyword(
                        new KeywordData("p", ProductInfo::getCode),
                        new KeywordData("p", ProductInfo::getName))
                .groupBy("pi.id");

        return baseMapper.pageByProduct(condition.getPage(), wrapper);
    }

    @Override
    public Page<PageByCombinationVo> pageByCombination(Condition condition) {

        // TODO 临时接到通知，优先级低，后面补

        // 获取产品组合
        IWrapper<Object> wrapper = IWrapper.getWrapper(condition)
                .keyword(
                        new KeywordData("p", ProductInfo::getCode),
                        new KeywordData("p", ProductInfo::getName))
                .eq("pi", ProductInfo::getCombination, StatusConstant.YES)
                .groupBy("pi.id");
        Page<PageByCombinationVo> page = baseMapper.pageByCombination(condition.getPage(), wrapper);

        List<PageByCombinationVo> records = page.getRecords();
        if (records.size() == 0) {
            return page;
        }

        List<Long> productIdList = records.stream().map(PageByCombinationVo::getProductId).collect(Collectors.toList());
        // 组合产品关联表
        List<ProductCombination> combinationList = productCombinationService.list(
                q -> q.select(
                                ProductCombination::getProductId,
                                ProductCombination::getLinkProductId,
                                ProductCombination::getLinkQuantity
                        )
                        .in(ProductCombination::getProductId, productIdList));

        // 获取组合关联的产品idList
        List<Long> linkProductIdList = combinationList.stream().map(ProductCombination::getLinkProductId).collect(Collectors.toList());

        // 查询关联产品信息

        wrapper.clear();


        return page;
    }

    /**
     * 合并变更数量(如果同一种物品在出入库一个仓库，则合并数量)
     */
    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.equals(InTypeEnum.PURCHASE)) {
                    stock.setFrozenQuantity(changeQuantity);
                } else if (typeEnum.equals(OutTypeEnum.UNQUALIFIED_QUALITY_INSPECTION)) {
                    stock.setFrozenQuantity(BigDecimal.ZERO);
                } else {
                    stock.setQuantity(changeQuantity);
                }
                map.put(key, stock);
            } else {
                if (typeEnum.equals(InTypeEnum.PURCHASE)) {
                    stock.setFrozenQuantity(stock.getFrozenQuantity().add(changeQuantity));
                } else if (typeEnum.equals(OutTypeEnum.UNQUALIFIED_QUALITY_INSPECTION)) {
                    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) {
                if (typeEnum.equals(InTypeEnum.PURCHASE)) {
                    changeStock.setFrozenQuantity(stock.getFrozenQuantity().add(changeStock.getFrozenQuantity()));
                } else {
                    changeStock.setQuantity(stock.getQuantity().add(changeStock.getQuantity()));
                }
            } else {
                // 质检不合格出库,扣减锁定库存
                if (typeEnum.equals(OutTypeEnum.UNQUALIFIED_QUALITY_INSPECTION)) {
                    changeStock.setFrozenQuantity(changeStock.getFrozenQuantity().subtract(stock.getFrozenQuantity()));
                }
                // 其他出库扣减库存
                else {
                    BigDecimal subtract = changeStock.getQuantity().subtract(stock.getQuantity());
                    Assert.eqTrue(subtract.compareTo(BigDecimal.ZERO) < 0, "产品库存不足，出库失败");
                    changeStock.setQuantity(subtract);
                }
            }
        }
        saveOrUpdateBatch(new ArrayList<>(map.values()));
    }

}
