package com.fjhx.service.apply.impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fjhx.constants.StockJournalTypeConstant;
import com.fjhx.entity.apply.ApplyPurchase;
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.apply.ApplyPurchaseStatusEnum;
import com.fjhx.mapper.apply.ApplyPurchaseMapper;
import com.fjhx.params.apply.ApplyPurchaseVo;
import com.fjhx.service.apply.ApplyPurchaseService;
import com.fjhx.service.product.ProductInfoService;
import com.fjhx.service.stock.StockJournalService;
import com.fjhx.service.stock.StockService;
import com.fjhx.uitl.code.CodeEnum;
import com.fjhx.utils.UserClientUtil;
import com.fjhx.utils.wrapperUtil.IWrapper;
import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.redis.lock.RedisLockClient;
import org.springblade.core.secure.utils.AuthUtil;
import org.springblade.core.tool.utils.DateUtil;
import org.springblade.core.tool.utils.Func;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.Serializable;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * <p>
 * 申购单 服务实现类
 * </p>
 *
 * @author ${author}
 * @since 2022-11-28
 */
@Service
public class ApplyPurchaseServiceImpl extends ServiceImpl<ApplyPurchaseMapper, ApplyPurchase> implements ApplyPurchaseService {

    @Autowired
    private RedisLockClient redisLockClient;

    @Autowired
    private StockService stockService;

    @Autowired
    private ProductInfoService productInfoService;

    @Autowired
    private StockJournalService stockJournalService;

    private final String REDIS_LOCK_CACHE_KEY = "seq:lock:" + AuthUtil.getTenantId() + ":apply_purchase:";

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

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

        wrapper.eq("t1", ApplyPurchase::getReceiptWarehouseId, condition.get("warehouseId"))
                .eq("t1", ApplyPurchase::getStatus)
                .apply(Func.isNotEmpty(condition.get("code")), "instr(t1.`code`, '" + condition.get("code") + "') > 0")
                .apply(Func.isNotEmpty(condition.get("goodsCode")), "instr(t2.`code`, '" + condition.get("goodsCode") + "') > 0")
                .apply(Func.isNotEmpty(condition.get("goodsName")), "instr(t2.`name`, '" + condition.get("goodsName") + "') > 0")
                .eq("t1", ApplyPurchase::getCreateUser, condition.get("applyUserId"))
                .ge("t1", ApplyPurchase::getCreateTime, condition.get("startTime"))
                .le("t1", ApplyPurchase::getCreateTime, condition.get("endTime"))
                .and(Func.isNotEmpty(condition.get("keyword")),
                        o -> o.apply("instr(t1.`code`, '" + condition.get("keyword") + "') > 0").or()
                                .apply("instr(t2.`code`, '" + condition.get("keyword") + "') > 0").or()
                                .apply("instr(t2.`name`, '" + condition.get("keyword") + "') > 0"))
                .orderByDesc("t1", ApplyPurchase::getId);

        Page<ApplyPurchase> page = baseMapper.getPage(createPage(condition), wrapper);
        if (Func.isNotEmpty(page.getRecords())) {
            Map<Long, String> userNameMap = UserClientUtil.getUserNameMap(page.getRecords(), ApplyPurchase::getCreateUser);
            for (ApplyPurchase record : page.getRecords()) {
                if (Func.isNotEmpty(userNameMap) && Func.isNotEmpty(userNameMap.get(record.getCreateUser()))) {
                    record.setCreateName(userNameMap.get(record.getCreateUser()));
                }
            }
        }

        return page;
    }

    @Override
    public void add(List<ApplyPurchase> list) {
        if (!redisLockClient.lockFair(REDIS_LOCK_CACHE_KEY, () -> {
            // 获取编码
            String code = CodeEnum.APPLY_PURCHASE.getCode();
            // 查询编码是否已存在
            // if (!checkCodeIsExist(null, code)) {
            //     throw new ServiceException("后台自增编码存在重复，请重试或联系管理员！编码：" + code);
            // }
            list.forEach(item -> {
                item.setCode(code);
                item.setStatus(ApplyPurchaseStatusEnum.STATUS_30.getKey());
            });
            saveBatch(list);
            return true;
        })) {
            throw new ServiceException("当前系统繁忙，请稍后重试！");
        }
    }

    private Boolean checkCodeIsExist(Long id, String code) {
        List<ApplyPurchase> list = lambdaQuery().ne(Func.isNotEmpty(id), ApplyPurchase::getId, id).eq(ApplyPurchase::getCode, code).list();
        return !Func.isNotEmpty(list);
    }

    @Override
    public void edit(ApplyPurchaseVo applyPurchaseVo) {
        updateById(applyPurchaseVo);
    }

    @Override
    public void delete(ApplyPurchaseVo applyPurchaseVo) {
        removeById(applyPurchaseVo.getId());
    }

    @Override
    public ApplyPurchase getById(Serializable id) {
        ApplyPurchase purchase = super.getById(id);
        if (Func.isNotEmpty(purchase)) {
            //查询物品
            ProductInfo info = productInfoService.getById(purchase.getGoodsId());
            if (Func.isNotEmpty(info)) {
                purchase.setGoodsCode(info.getCode());
                purchase.setGoodsName(info.getName());
                purchase.setGoodsType(info.getType());
                purchase.setGoodsUnit(info.getUnit());
            }

            //物品出库信息map
            Map<Long, BigDecimal> outStockMap = getOutStockMap(Arrays.asList(purchase.getGoodsId()));
            purchase.setOutStockQuantity(Optional.ofNullable(outStockMap.get(purchase.getGoodsId())).orElse(new BigDecimal(BigDecimal.ZERO.intValue())));

            //查询库存
            Stock stock = stockService.lambdaQuery().eq(Stock::getWarehouseId, purchase.getReceiptWarehouseId()).eq(Stock::getGoodsId, purchase.getGoodsId()).one();
            if (Func.isNotEmpty(stock)) {
                purchase.setStockQuantity(stock.getQuantity());
            }
        }
        return purchase;
    }

    /**
     * 待采购列表
     *
     * @param condition 查询条件
     * @return
     */
    @Override
    public Page<ApplyPurchase> stayPurchasePage(Map<String, Object> condition) {
        IWrapper<ApplyPurchase> wrapper = IWrapper.getWrapper(condition);

        wrapper.between("t1", ApplyPurchase::getStatus, ApplyPurchaseStatusEnum.STATUS_30.getKey(), ApplyPurchaseStatusEnum.STATUS_50.getKey())
                .eq("t1", ApplyPurchase::getStatus)
                .eq("t2", ProductInfo::getType, condition.get("goodsType"))
                .eq("t3", Warehouse::getId, condition.get("warehouseId"))
                .apply(Func.isNotEmpty(condition.get("code")), "instr(t1.`code`, '" + condition.get("code") + "') > 0")
                .apply(Func.isNotEmpty(condition.get("goodsCode")), "instr(t2.`code`, '" + condition.get("goodsCode") + "') > 0")
                .apply(Func.isNotEmpty(condition.get("goodsName")), "instr(t2.`name`, '" + condition.get("goodsName") + "') > 0")
                .eq("t1", ApplyPurchase::getCreateUser, condition.get("applyUserId"))
                .ge("t1", ApplyPurchase::getCreateTime, condition.get("startTime"))
                .le("t1", ApplyPurchase::getCreateTime, condition.get("endTime"))
                .and(Func.isNotEmpty(condition.get("keyword")), o -> o.apply("instr(t1.`code`, '" + condition.get("keyword") + "') > 0").or().apply("instr(t2.`code`, '" + condition.get("keyword") + "') > 0").or().apply("instr(t2.`name`, '" + condition.get("keyword") + "') > 0"))
                .orderByDesc("t1", ApplyPurchase::getId);

        Page<ApplyPurchase> page = baseMapper.getPage(createPage(condition), wrapper);
        if (Func.isNotEmpty(page.getRecords())) {
            //物品ID集合
            List<Long> goodsIds = page.getRecords().stream().map(ApplyPurchase::getGoodsId).distinct().collect(Collectors.toList());
            //物品出库信息map
            Map<Long, BigDecimal> outStockMap = getOutStockMap(goodsIds);

            //用户信息map
            Map<Long, String> userNameMap = UserClientUtil.getUserNameMap(page.getRecords(), ApplyPurchase::getCreateUser);

            for (ApplyPurchase record : page.getRecords()) {
                if (Func.isNotEmpty(userNameMap) && Func.isNotEmpty(userNameMap.get(record.getCreateUser()))) {
                    record.setCreateName(userNameMap.get(record.getCreateUser()));
                }
                if (Func.isNotEmpty(outStockMap) && Func.isNotEmpty(outStockMap.get(record.getGoodsId()))) {
                    record.setOutStockQuantity(outStockMap.get(record.getGoodsId()));
                }
            }
        }

        return page;
    }

    /**
     * 获取物品出库信息
     *
     * @param goodsIds 物品ID集合
     * @return
     */
    private Map<Long, BigDecimal> getOutStockMap(List<Long> goodsIds) {
        //物品出库信息map
        Map<Long, BigDecimal> outStockMap = new HashMap<>();
        //结束时间
        Date endTime = new Date();
        //开始时间：往前推移30天
        Date startTime = DateUtil.plusDays(endTime, -30l);

        //查询出库信息
        List<StockJournal> stockJournals = stockJournalService.query()
                .select("goods_id, SUM(change_quantity) change_quantity")
                .eq("`type`", StockJournalTypeConstant.OUT)
                .in("goods_id", goodsIds)
                .apply("DATE_FORMAT(create_time, '%Y-%m-%d') >= '" + new SimpleDateFormat("yyyy-MM-dd").format(startTime) + "'")
                .apply("DATE_FORMAT(create_time, '%Y-%m-%d') <= '" + new SimpleDateFormat("yyyy-MM-dd").format(endTime) + "'")
                .groupBy("goods_id")
                .list();
        if (Func.isNotEmpty(stockJournals)) {
            outStockMap = stockJournals.stream().collect(Collectors.toMap(StockJournal::getGoodsId, StockJournal::getChangeQuantity, (key1, key2) -> key2));
        }

        return outStockMap;
    }

    /**
     * 根据采购ID查询
     *
     * @param purchaseId 采购ID
     * @return
     */
    @Override
    public List<ApplyPurchase> getByPurchaseId(Long purchaseId) {

        List<ApplyPurchase> purchases = lambdaQuery().eq(ApplyPurchase::getPurchaseId, purchaseId).list();
        if (Func.isNotEmpty(purchases)) {
            //物品ID集合
            List<Long> goodsIds = purchases.stream().map(ApplyPurchase::getGoodsId).distinct().collect(Collectors.toList());

            //物品信息map
            Map<Long, ProductInfo> productInfoMap = new HashMap<>();
            //查询物品信息
            List<ProductInfo> productInfos = productInfoService.lambdaQuery().in(ProductInfo::getId, goodsIds).list();
            if (Func.isNotEmpty(productInfos)) {
                productInfoMap = productInfos.stream().collect(Collectors.toMap(ProductInfo::getId, Function.identity(), (key1, key2) -> key2));
            }

            //物品出库信息map
            Map<Long, BigDecimal> outStockMap = getOutStockMap(goodsIds);

            for (ApplyPurchase record : purchases) {
                if (Func.isNotEmpty(productInfoMap) && Func.isNotEmpty(productInfoMap.get(record.getGoodsId()))) {
                    ProductInfo info = productInfoMap.get(record.getGoodsId());
                    record.setGoodsType(info.getType());
                    record.setGoodsCode(info.getCode());
                    record.setGoodsName(info.getName());
                    record.setGoodsUnit(info.getUnit());
                }
                if (Func.isNotEmpty(outStockMap) && Func.isNotEmpty(outStockMap.get(record.getGoodsId()))) {
                    record.setOutStockQuantity(outStockMap.get(record.getGoodsId()));
                }
            }
        }
        return purchases;
    }
}
