package com.shkj.wms.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.shkj.common.core.domain.Result;
import com.shkj.common.utils.DateUtils;
import com.shkj.common.utils.SecurityUtils;
import com.shkj.wms.bo.*;
import com.shkj.wms.domain.*;
import com.shkj.wms.enums.InvStatusEnum;
import com.shkj.wms.enums.InvWayEnum;
import com.shkj.wms.enums.PlanStatusEnum;
import com.shkj.wms.enums.PlanTypeEnum;
import com.shkj.wms.mapper.BusinStockInvMapper;
import com.shkj.wms.service.*;
import com.shkj.wms.utils.IntIdUtil;
import com.shkj.wms.utils.SnowflakeIdWorker;
import com.shkj.wms.utils.WmsCallWcsUtil;
import com.shkj.wms.vo.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 盘点计划Service业务层处理
 *
 * @author lch
 * @date 2022-06-10
 */
@Slf4j
@Service
@Transactional(rollbackFor = Exception.class)
public class BusinStockInvServiceImpl extends ServiceImpl<BusinStockInvMapper, BusinStockInv> implements IBusinStockInvService {

    @Autowired
    private ISysStockDetailService sysStockDetailService;

    @Autowired
    private ISysSeqManaService sysSeqManaService;

    @Autowired
    private IBaseContainerService baseContainerService;

    @Autowired
    @Lazy
    private IBusinOutInPlanService businOutInPlanService;

    @Autowired
    private IBaseLocationService baseLocationService;


    @Autowired
    private WmsCallWcsUtil wmsCallWcsUtil;

    /**
     * 查询盘点模具
     *
     * @param bo 薄
     * @return {@link List}<{@link StockLocationGoodsAreaStorageVo}>
     */
    @Override
    public List<StockLocationGoodsAreaStorageVo> queryInvMold(BusinStockInvGoodsQueryBo bo) {
        return this.baseMapper.queryInvMold(bo);
    }

    /**
     *
     * @param bo
     * @return
     */
    @Override
    public List<StockLocationInvVo> queryInvLocation(BusinStockInvGoodsQueryBo bo){
        return this.baseMapper.queryInvLocation(bo);
    }

    /**
     * pda查询盘点信息由托盘代码
     *
     * @param bo 薄
     * @return {@link List}<{@link BusinStockInv}>
     */
    @Override
    public List<BusinStockInvVo> pdaInvByContainerCode(PdaInvByContainerCodeBo bo) {
        List<BusinStockInvVo> businStockInvVos = this.baseMapper.pdaInvByContainerCode(bo);
        //这里是为了实时表示当前库存的数量
        businStockInvVos.forEach(x -> x.setQty(x.getActualQty()));
        return businStockInvVos;
    }

    @Override
    public List<Long> queryDoingLocationByInv() {
        LambdaQueryWrapper<BusinStockInv> wrapper = new LambdaQueryWrapper<BusinStockInv>()
                .ne(BusinStockInv::getInvStatus, InvStatusEnum.end.getValue())
                .ne(BusinStockInv::getInvStatus, InvStatusEnum.cancel.getValue());

        List<BusinStockInv> list = list(wrapper);
        return list.stream().map(x -> x.getLocationId()).distinct().collect(Collectors.toList());


    }

    @Override
    public List<BusinStockInvExeVo> getBusinStockInvList(BusinStockInvQueryBo bo) {
        return this.baseMapper.getBusinStockInvList(bo);
    }

    @Override
    public List<BusinStockInvVo> queryList(BusinStockInvQueryBo bo) {
        List<BusinStockInvVo> list = this.baseMapper.queryList(bo);
        return list;
    }

    @Override
    public List<BusinStockInv> queryStockInvList(String orderNo) {

        LambdaQueryWrapper<BusinStockInv> wrapper = new LambdaQueryWrapper<BusinStockInv>()
                .eq(BusinStockInv::getInvOrderNo, orderNo);
        return this.list(wrapper);
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result<Boolean> insertByAddBo(BusinStockInvAddBo bo) {

        Long branchId = SecurityUtils.getCurrentBranchId();
//        Result<String> result = sysSeqManaService.generatorCode(NumberPreEnum.PD.getValue(), NumberPreEnum.PD.getType(), branchId);
//        if (!Result.isOk(result)) {
//            return Result.err().msg(result.getMsg());
//        }
//        String orderNo = result.getData();
        Long orderNo = IntIdUtil.generateIntId();
        List<BusinStockInv> addList = new ArrayList<>();
        for (Long stockId : bo.getIds()) {
            SysStockDetail stockLot = sysStockDetailService.getById(stockId);
            if (stockLot == null) {
                return Result.err().msg("库存不存在");
            }
            //判断是否有任务未完成
            List<BusinOutInPlan> businOutInPlans = businOutInPlanService.queryNoFinishByLocationId(stockLot.getLocationId());
            if (CollectionUtils.isNotEmpty(businOutInPlans)) {
                log.info("当前货位存在出入库任务没有完成 = {}" , businOutInPlans);
                return Result.err().msg("当前货位存在出入库任务没有完成");
            }

            BusinStockInv inv = new BusinStockInv();
            inv.setInvOrderNo(orderNo.toString());
            inv.setLocationId(stockLot.getLocationId());
            inv.setContainerId(stockLot.getContainerId());
            inv.setStorageId(stockLot.getStorageId());
            inv.setAreaId(stockLot.getAreaId());
            inv.setMoldId(stockLot.getMoldId());
            inv.setMoldBarcode(stockLot.getBarcode());
            inv.setMoldNo(stockLot.getMoldNo());
            inv.setQty(stockLot.getQty());
            inv.setDifferenceQty(BigDecimal.ZERO);
            inv.setInvQty(stockLot.getQty());
            inv.setInvStatus(InvStatusEnum.init.getValue());
            inv.setInvType(bo.getInvType());
            //inv.setInvWay(bo.getInvWay());
            inv.setInvWay(InvWayEnum.open.getValue());
            inv.setBranchId(stockLot.getBranchId());
            addList.add(inv);
        }

        this.saveBatch(addList);
        return Result.ok();
    }


    public BusinStockInv queryNoFinishByLocationId(Long containerId) {
        LambdaQueryWrapper<BusinStockInv> wrapper = new LambdaQueryWrapper<BusinStockInv>()
                .eq(BusinStockInv::getContainerId, containerId)
                .ne(BusinStockInv::getInvStatus, InvStatusEnum.end.getValue())
                .ne(BusinStockInv::getInvStatus, InvStatusEnum.cancel.getValue())
                .last("limit 1");
        return getOne(wrapper);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result<Boolean> insertByAddNewBo(BusinStockInvAddBo bo) {

        List<BusinStockInv> addList = new ArrayList<>();
        for (Long contaierId : bo.getIds()) {

            BusinStockInv businStockInv = queryNoFinishByLocationId(contaierId);
            if (businStockInv !=null){
                BaseContainer baseContainer = baseContainerService.getById(contaierId);
                log.info("此托盘有未完成的盘点任务"+baseContainer.getContainerName());
                return Result.err().msg("此托盘有未完成的盘点任务:"+baseContainer.getContainerName());
            }
            Long orderNo = IntIdUtil.generateIntId();

            List<SysStockDetail> sysStockDetails = sysStockDetailService.getByContainerId(contaierId);
            if (sysStockDetails.size()==0) {
                return Result.err().msg("库存不存在");
            }
            //判断是否有任务未完成
            List<BusinOutInPlan> businOutInPlans = businOutInPlanService.queryNoFinishByContainerId(contaierId);
            if (CollectionUtils.isNotEmpty(businOutInPlans)) {
                BaseContainer baseContainer = baseContainerService.getById(contaierId);
                log.info("当前货位存在出入库任务没有完成 = {}" , businOutInPlans);
                return Result.err().msg("当前货位存在出入库任务没有完成"+baseContainer.getContainerName());
            }

            for (SysStockDetail stockLot : sysStockDetails) {
                BusinStockInv inv = new BusinStockInv();
                inv.setInvOrderNo(orderNo.toString());
                inv.setLocationId(stockLot.getLocationId());
                inv.setContainerId(stockLot.getContainerId());
                inv.setStorageId(stockLot.getStorageId());
                inv.setAreaId(stockLot.getAreaId());
                inv.setMoldId(stockLot.getMoldId());
                inv.setMoldBarcode(stockLot.getBarcode());
                inv.setMoldNo(stockLot.getMoldNo());
                inv.setQty(stockLot.getQty());
                inv.setDifferenceQty(BigDecimal.ZERO);
                inv.setInvQty(stockLot.getQty());
                inv.setInvStatus(InvStatusEnum.init.getValue());
                inv.setInvType(bo.getInvType());
                //inv.setInvWay(bo.getInvWay());
                inv.setInvWay(InvWayEnum.open.getValue());
                inv.setBranchId(stockLot.getBranchId());
                addList.add(inv);
            }
        }

        this.saveBatch(addList);
        return Result.ok();
    }

    /**
     * 盘点添加出库任务
     *
     * @param orderNo 订单没有
     * @param addList 添加列表
     */
    private void addOutStockByInv(Long orderNo, List<BusinStockInv> addList) {
        // 调用堆垛机下发任务
        Map<Long, List<BusinStockInv>> listMap = addList.stream().collect(Collectors.groupingBy(BusinStockInv::getLocationId));
        Integer index = 0;
        for (Map.Entry<Long, List<BusinStockInv>> entry : listMap.entrySet()) {
            BaseLocation vo = baseLocationService.getById(entry.getKey());
            OutStockDetailVo detailVo = new OutStockDetailVo();
            detailVo.setLocationId(vo.getId());
            detailVo.setLocationCode(vo.getLocationCode());
            detailVo.setLocationRow(vo.getLocationRow());
            detailVo.setLayer(vo.getLayer());
            detailVo.setLocationColumn(vo.getLocationColumn());
            detailVo.setOrderNo(orderNo);
            detailVo.setBranchId(vo.getBranchId());
            log.info("开始调用wcs下发任务 = {}" , detailVo);
            List<BusinStockInv> businStockInvs = entry.getValue();
            //由于堆垛机只能执行一个任务、所以这里只能第一个任务下发、其他的任务进行轮询
            //全部进入任务管理，进行调度执行
            if (index.equals(0)) {
                wmsCallWcsUtil.outStock(detailVo, false,businStockInvs.get(0).getLayer());
            } else {
                log.info("不用调用、直接进入轮询 = {}" , detailVo);
                wmsCallWcsUtil.outStock(detailVo, false,businStockInvs.get(0).getLayer());
            }
            index++;
        }
    }

    /**
     * 取消盘点计划
     *
     * @param ids
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result<Boolean> cancel(List<Long> ids) {
        List<BusinStockInv> businStockInvs = listByIds(ids);
        if (businStockInvs.size() != ids.stream().distinct().collect(Collectors.toList()).size()) {
            return Result.err().msg("盘点数据不存在");
        }
        for (BusinStockInv stockInv : businStockInvs) {
            if (!stockInv.getInvStatus().equals(InvStatusEnum.difference.getValue())) {
                return Result.err().msg("盘点数据状态不能取消");
            }
            stockInv.setInvStatus(InvStatusEnum.cancel.getValue());
        }
        this.updateBatchById(businStockInvs);
        //返回原始位置
        returnOriginalLocation(businStockInvs);
        return Result.ok();
    }

    /**
     * 返回原始位置
     *
     * @param list 列表
     * @return {@link Result}
     */
    public Result returnOriginalLocation(List<BusinStockInv> list) {
        //判断当前盘点的任务是否全部完成
        Map<Long, List<BusinStockInv>> map = list.stream().collect(Collectors.groupingBy(x -> x.getLocationId()));
        for (Map.Entry<Long, List<BusinStockInv>> entry : map.entrySet()) {
         /*   List<BusinStockInv> noFinishList = this.list(new LambdaQueryWrapper<BusinStockInv>()
                    .eq(BusinStockInv::getLocationId, entry.getKey())
                    .ne(BusinStockInv::getInvStatus, InvStatusEnum.end.getValue())
                    .ne(BusinStockInv::getInvStatus, InvStatusEnum.cancel.getValue())
            );*/
           // if (CollectionUtils.isEmpty(noFinishList)) {
                //新增回库任务
                BusinStockInv inv = entry.getValue().get(0);
                BusinOutInPlan inPlan = new BusinOutInPlan();
                inPlan.setOrderNo(Long.parseLong(inv.getInvOrderNo()));
                inPlan.setContainerId(inv.getContainerId());
                BaseContainer container = baseContainerService.getById(inv.getContainerId());
                if (container == null) {
                    continue;
                }
                //随机定义一个磨具即可
                inPlan.setContainerCode(container.getContainerCode());
                inPlan.setType(PlanTypeEnum.INV_BACK.getValue());
                inPlan.setPlanStatus(PlanStatusEnum.WAIT.getValue());
                inPlan.setQty(BigDecimal.ZERO);
                inPlan.setBranchId(3L);
                inPlan.setMoldBarcode(inv.getMoldBarcode())
                        .setMoldId(inv.getMoldId())
                        .setMoldNo(inv.getMoldNo());
                inPlan.setLocationId(entry.getKey());
                businOutInPlanService.save(inPlan);
           // }
            continue;
        }
        return Result.ok();

    }

    /**
     * 盘点录入保存
     *
     * @param bo
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result<Boolean> entrySave(List<BusinStockInvEditBo> bo) {

        List<BusinStockInv> updList = new ArrayList<>();
        List<BusinStockInv> addList = new ArrayList<>();
        for (BusinStockInvEditBo editBo : bo) {
            String invOrderNo = editBo.getInvOrderNo();
            //有ID则是生成盘点的时候增加的；没有id是托盘新增的模具信息
            if (editBo.getId() !=null){
                //是否是空托盘
                if (editBo.getMoldId() != 0){
                    BusinStockInvVo stockInv = this.baseMapper.getInvById(editBo.getId());
                    if (stockInv == null) {
                        return Result.err().msg("盘点数据不存在");
                    }
                    if (!stockInv.getInvStatus().equals(InvStatusEnum.exec.getValue())) {
                        return Result.err().msg("盘点数据已经执行了");
                    }
                    if(stockInv.getOutQty() !=null){
                        if (stockInv.getOutQty().compareTo(editBo.getInvQty()) > 0) {
                            return Result.err().msg("实际库存数量不能小于出库预占数量");
                        }
                    }

                    if (stockInv.getActualQty().compareTo(editBo.getInvQty()) == 0) {
                        stockInv.setInvStatus(InvStatusEnum.end.getValue());
                    } else {
                        stockInv.setInvStatus(InvStatusEnum.difference.getValue());
                        stockInv.setDifferenceQty(editBo.getInvQty().subtract(stockInv.getQty()).abs());
                    }
                    stockInv.setQty(stockInv.getActualQty());
                    stockInv.setInvQty(editBo.getInvQty());
                    stockInv.setEndTime(DateUtils.getNowDate());
                    updList.add(stockInv);
                }else {
                    BusinStockInv businStockInv = this.getById(editBo.getId());
                    businStockInv.setEndTime(DateUtils.getNowDate());
                    businStockInv.setInvStatus(InvStatusEnum.end.getValue());
                    BusinStockInvVo businStockInvVo = BeanUtil.toBean(businStockInv, BusinStockInvVo.class);
                    updList.add(businStockInvVo);
                }
            }else {
                BaseLocation baseLocation = baseLocationService.getById(editBo.getLocationId());
                BusinStockInvVo addStockInv = new BusinStockInvVo();
                addStockInv.setInvOrderNo(invOrderNo);
                addStockInv.setInvStatus(InvStatusEnum.difference.getValue());
                addStockInv.setInvQty(editBo.getInvQty());
                addStockInv.setQty(BigDecimal.ZERO);
                addStockInv.setContainerId(editBo.getContainerId());
                addStockInv.setLocationId(editBo.getLocationId());
                addStockInv.setMoldBarcode(editBo.getMoldBarcode());
                addStockInv.setMoldNo(editBo.getMoldNo());
                addStockInv.setMoldId(editBo.getMoldId());
                addStockInv.setDifferenceQty(BigDecimal.ONE);
                addStockInv.setEndTime(DateUtils.getNowDate());
                addStockInv.setAreaId(baseLocation.getAreaId());
                addStockInv.setBranchId(baseLocation.getBranchId());
                addStockInv.setCreateBy(SecurityUtils.getUsername());
                addStockInv.setCreateTime(DateUtils.getNowDate());
                addStockInv.setExecTime(DateUtils.getNowDate());
                addList.add(addStockInv);
            }
        }

        BusinStockInvEditBo businStockInvEditBo = bo.get(0);
        //判断生成判断的时候，是否是空托盘，如果是则更新下状态；
        LambdaQueryWrapper<BusinStockInv> wrapperCon = new LambdaQueryWrapper<BusinStockInv>()
                .eq(BusinStockInv::getLocationId, businStockInvEditBo.getLocationId())
                .eq(BusinStockInv::getInvStatus,InvStatusEnum.exec)
                .isNull(BusinStockInv::getMoldBarcode)
                .last("limit 1");
        BusinStockInv emptyCon = getOne(wrapperCon);
        if (emptyCon !=null){
            emptyCon.setEndTime(DateUtils.getNowDate());
            emptyCon.setInvStatus(InvStatusEnum.end.getValue());
            updList.add(emptyCon);
        }

        this.updateBatchById(updList);

        this.saveBatch(addList);

        //生成可执行的入库计划
        if(!CollectionUtils.isEmpty(updList) && !CollectionUtils.isEmpty(addList)){
            returnOriginalLocation(addList);
        }else {
            returnOriginalLocation(updList);
        }

        return Result.ok();
    }


    /**
     * 差异处理列表中盘点确认
     *
     * @param
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result<Boolean> confirm(List<Long> ids) {
        if(ids.size() ==0){
            return Result.err().msg("请选择盘点数据");
        }
        List<BusinStockInv> updList = new ArrayList<>();
        for (Long id : ids) {
            BusinStockInv stockInv = getById(id);
            if (stockInv == null) {
                return Result.err().msg("盘点数据不存在");
            }
            if (!stockInv.getInvStatus().equals(InvStatusEnum.difference.getValue())) {
                return Result.err().msg("盘点数据不是有差异的数据");
            }
            stockInv.setInvStatus(InvStatusEnum.end.getValue());
            updList.add(stockInv);
        }

        //实际更新库存数量

        Result result = sysStockDetailService.updStockByInv(updList);
        if (!Result.isOk(result)) {
            log.info("盘点库存失败 进行回滚= {}" , result);
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return result;
        }
        log.info("盘点更新库存信息 = {}" , updList);
        this.updateBatchById(updList);

        return Result.ok();
    }



    /**
     * 执行盘点计划
     *
     * @param
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result<Boolean> exec(String invOrderNo,Integer layer) {
        LambdaQueryWrapper<BusinStockInv> wrapper = new LambdaQueryWrapper<BusinStockInv>()
                .eq(BusinStockInv::getInvOrderNo, invOrderNo)
                .eq(BusinStockInv::getInvStatus, InvStatusEnum.init.getValue());
        List<BusinStockInv> list = this.list(wrapper);
        if (list.size()==0){
            return Result.err().msg("盘点数据已执行");
        }
        for (BusinStockInv businStockInv : list) {
            businStockInv.setInvStatus(InvStatusEnum.exec.getValue());
            businStockInv.setExecTime(DateUtils.getNowDate());
            businStockInv.setLayer(layer);
        }

        //更新盘点的状态为确认执行
        this.updateBatchById(list);
        addOutStockByInv(Long.valueOf(invOrderNo), list);
        return Result.ok();
    }



    /**
     * 取消盘点计划（执行初始化的时候，可取消盘点，其他状态不可取消）
     *
     * @param
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result<Boolean> execCancel(String invOrderNo) {
        LambdaQueryWrapper<BusinStockInv> wrapper = new LambdaQueryWrapper<BusinStockInv>()
                .eq(BusinStockInv::getInvOrderNo, invOrderNo)
                .eq(BusinStockInv::getInvStatus, InvStatusEnum.init.getValue());
        List<BusinStockInv> list = this.list(wrapper);
        if (list.size()==0){
            return Result.err().msg("盘点数据已执行");
        }
        for (BusinStockInv businStockInv : list) {
            businStockInv.setInvStatus(InvStatusEnum.cancel.getValue());
            businStockInv.setExecTime(DateUtils.getNowDate());
        }
        //更新盘点的状态为确认执行
        this.updateBatchById(list);
        return Result.ok();
    }


}
