package com.prolog.cs.biz.outbound.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import com.google.common.collect.Lists;
import com.prolog.cs.basic.chain.model.cargo.BaseCargoOwnerMst;
import com.prolog.cs.basic.common.model.BaseCommonDict;
import com.prolog.cs.basic.item.model.vo.WaveItemInfoVO;
import com.prolog.cs.biz.outbound.dao.PickTaskHMapper;
import com.prolog.cs.biz.outbound.dao.SaleRedRecordMapper;
import com.prolog.cs.biz.outbound.dao.WaveBillDMapper;
import com.prolog.cs.biz.outbound.dao.WaveBillMapper;
import com.prolog.cs.biz.outbound.model.BILL_STATUS;
import com.prolog.cs.biz.outbound.model.PickTaskH;
import com.prolog.cs.biz.outbound.model.SaleRedRecord;
import com.prolog.cs.biz.outbound.model.dto.BillDTO;
import com.prolog.cs.biz.outbound.model.dto.BillMergeDTO;
import com.prolog.cs.biz.outbound.model.dto.WmsOutPriorityReqDTO;
import com.prolog.cs.biz.outbound.model.entity.WaveBillDDO;
import com.prolog.cs.biz.outbound.model.entity.WaveBillDO;
import com.prolog.cs.biz.outbound.model.vo.BillGroupByVO;
import com.prolog.cs.biz.outbound.resource.*;
import com.prolog.cs.biz.outbound.service.BillService;
import com.prolog.entity.vo.UpdatePriorityVO;
import com.prolog.enums.UpgradePriorityEnum;
import com.prolog.framework.authority.user.UserTemplate;
import com.prolog.framework.common.message.RestMessage;
import com.prolog.framework.core.exception.BizException;
import com.prolog.framework.core.exception.PrologException;
import com.prolog.framework.core.pojo.Page;
import com.prolog.framework.core.restriction.Criteria;
import com.prolog.framework.core.restriction.RestrictionBuilder;
import com.prolog.framework.dao.util.PageUtils;
import com.prolog.framework.utils.MapUtils;
import com.prolog.framework.utils.ObjectUtils;
import com.prolog.framework.utils.SnowFlakeUtils;
import com.prolog.inte.EisHandler;
import com.prolog.upcloud.base.strategy.model.BusinessControl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class BillServiceImpl implements BillService {
    @Autowired
    private WaveBillMapper billMapper;

    @Autowired
    private StrategyResource strategyResource;

    @Autowired
    private WaveBillDMapper billDMapper;
    
    @Autowired
    private UserTemplate userTemplate;

    @Autowired
    private ItemClient itemClient;

    @Autowired
    private SaleRedRecordMapper saleRedRecordMapper;

    @Value("${prolog.interfaceId.updateBillPriority}")
    private String updateBillPriority;

    @Autowired
    private ControlResource controlResource;

    @Autowired
    private ChainResource chainResource;

    @Autowired
    private PickTaskHMapper pickTaskHMapper;

    @Value("${prolog.dao.keyGenerator.snowFlake.dataCenterId:15}")
    private long dataCenterId;

    @Value("${prolog.dao.keyGenerator.snowFlake.machineId:2}")
    private long machineId;

    @Autowired
    private EisOutBoundResource eisOutboundClient;

    @Autowired
    private CommonClient commonClient;

    @Value("${eis.billPriorityUrl}")
    private String billPriorityUrl;

    @Resource
    private EisHandler eisHandler;

    @Override
    @Transactional
    public RestMessage<Boolean> updateBill(List<BillDTO> billDTOs) {
        RestMessage<List<BaseCommonDict>> maxBillNum =  commonClient.getDictBasicListByCodes("WAVE_BILL_MAX_NUM", null);
        List<String> maxBillNums = maxBillNum.getData().stream().map(BaseCommonDict::getDictName).collect(Collectors.toList());
        int max = 2;
        if (CollUtil.isNotEmpty(maxBillNums)){
            max = Integer.parseInt(maxBillNums.get(0));
        }
        if(billDTOs.size() > max){
            throw new PrologException(" 单次最多提升【"+max+"】个订单的优先级");
        }
        List<UpdatePriorityVO> list = Lists.newArrayList();
        for (BillDTO billDTO : billDTOs) {
            Date now = new Date();
            billDTO.setReplenishmentTime(now);
            Integer priority = billDTO.getPriority();

            // 增加补货确认时间字段
            if (null == priority || priority <= 0) {
                billDTO.setPriority(9999);
            }
            billMapper.updateBillInfo(billDTO);
            double eisPriority = 10000 - billDTO.getPriority();
            WaveBillDO bill = billMapper.findById(billDTO.getId(),WaveBillDO.class);
            if(bill!=null){
                Integer count = billMapper.getAgvPickCount(bill.getBillGroupId(),bill.getWarehouseId());
                if(count>0){

//                    String url = billPriorityUrl;
//                    List<WmsOutPriorityReqDTO> wmsOutPriorityReqList = getWmsOutPriorityReqList(bill);
//                    log.info("同步订单优先级 url:{} ;入参：{}", JSONUtil.toJsonStr(url),JSON.toJSONString(wmsOutPriorityReqList));
//                    if (CollUtil.isNotEmpty(wmsOutPriorityReqList)) {
//                        String result = HttpUtil.post(url, JSONObject.toJSONString(wmsOutPriorityReqList));
//                        JSONObject json = JSONObject.parseObject(result);
//                        if (json.containsKey("success") && !json.getBoolean("success")) {
//                            log.info("提升优先级任务到EIS失败:{}", result);
//                        }
//                    }
                    List<UpdatePriorityVO> tasks = getUpgradePriorityList(bill, eisPriority);
                    if (CollUtil.isNotEmpty(tasks)) {
                        list.addAll(tasks);
                    }
                }else{
                    Criteria crt = Criteria.forClass(PickTaskH.class);
                    crt.restrictionBuilder().eq("billGroupId",bill.getSrcCode()).ne("status", "0");
                    crt.buildRestriction();
                    long num = pickTaskHMapper.findCountByCriteria(crt);
                    if(num>0)
                        throw new PrologException("订单已开始拣货,无法修改订单优先级!");
                    //更新订单对应补货任务的优先级
                    billMapper.updateBillPriority(billDTO.getPriority(),bill.getBillGroupId(),billDTO.getWarehouseId());
                }
            }
        }
        if (CollUtil.isNotEmpty(list)) {
            eisHandler.upgradePriority(list);
        }
        return RestMessage.doSuccess(Boolean.TRUE);
    }

    public List<WmsOutPriorityReqDTO> getWmsOutPriorityReqList(WaveBillDO bill) {
        List<PickTaskH> pickTaskHS = pickTaskHMapper.findByMap(MapUtils.put("billGroupId", bill.getBillGroupId()).getMap(), PickTaskH.class);
        List<WmsOutPriorityReqDTO> wmsOutPriorityReqList = new ArrayList<>();
        if (CollUtil.isNotEmpty(pickTaskHS)) {
            for (PickTaskH pickTaskH : pickTaskHS) {
                WmsOutPriorityReqDTO wmsOutPriorityReqDTO = new WmsOutPriorityReqDTO();
                wmsOutPriorityReqDTO.setMsgId(bill.getId());
                wmsOutPriorityReqDTO.setWarehouseId(bill.getWarehouseId());
                wmsOutPriorityReqDTO.setOwnerId(bill.getConsignorId());
                wmsOutPriorityReqDTO.setWorkType("10");
                wmsOutPriorityReqDTO.setBusinessType(bill.getType());
                wmsOutPriorityReqDTO.setBillNo(pickTaskH.getPickTaskId()); //
                wmsOutPriorityReqDTO.setTaskId(pickTaskH.getPickTaskId()); //
                wmsOutPriorityReqDTO.setPriority(bill.getPriority());
                wmsOutPriorityReqList.add(wmsOutPriorityReqDTO);
            }
        }
        return wmsOutPriorityReqList;
    }

    public List<UpdatePriorityVO> getUpgradePriorityList(WaveBillDO bill, double priority) {
        List<PickTaskH> pickTaskHS = pickTaskHMapper.findByMap(MapUtils.put("billGroupId", bill.getBillGroupId()).getMap(), PickTaskH.class);
        List<UpdatePriorityVO> list = new ArrayList<>();
        if (CollUtil.isNotEmpty(pickTaskHS)) {
            UpgradePriorityEnum priorityEnum = UpgradePriorityEnum.get(bill.getType());
            for (PickTaskH pickTaskH : pickTaskHS) {
                UpdatePriorityVO vo = new UpdatePriorityVO();
                vo.setMsgId(IdUtil.fastSimpleUUID());
                vo.setEnterpriseId(pickTaskH.getEnterpriseId());
                vo.setWarehouseId(pickTaskH.getWarehouseId());
                vo.setWorkType(priorityEnum.getWorkType());
                vo.setBusinessType(priorityEnum.getBusinessType());
                vo.setTaskId(pickTaskH.getPickTaskId());
                vo.setBillNo(pickTaskH.getPickTaskId());
                vo.setPriority(priority);
                list.add(vo);
            }
        }
        return list;
    }

    @Override
    public RestMessage<Boolean> delete(BillDTO billDTO) {
        String billId = billDTO.getId();
        billMapper.deleteById(billId, WaveBillDO.class);
        return RestMessage.doSuccess(true);
    }

    @Override
    public Page<WaveBillDO> query(BillDTO billDTO) {
        Integer pageNum = billDTO.getPageNum();
        pageNum = pageNum == null ? 0 : pageNum;
        Integer pageSize = billDTO.getPageSize();
        pageSize = pageSize == null ? 10 : pageSize;
        PageUtils.startPage(pageNum, pageSize);
        List<WaveBillDO> query = billMapper.query(billDTO);
        RestMessage<List<BaseCommonDict>> billtypeList =  commonClient.getDictBasicListByCodes("do_bill_type", null);
//        List<String> billTypes = billtypeList.getData().stream().map(BaseCommonDict::getDictCode).collect(Collectors.toList());
        if(!billtypeList.getData().isEmpty()){
            billtypeList.getData().forEach(data->{
                query.forEach(row->{
                     if(data.getDictCode().equals(row.getType())){
                         row.setType(data.getDictName());
                     }
                });
            });
        }
        Page<WaveBillDO> page = PageUtils.getPage(query);
        return page;
    }

    @Override
    public List<WaveBillDO> queryForMerge(BillMergeDTO billMergeDTO) {
        List<WaveBillDO> bills = billMapper.queryForMerge(billMergeDTO);
        return bills;

    }

    private BillGroupByVO getBillGroupByVO(BillMergeDTO billMergeDTO, WaveBillDO bill) {
        BillGroupByVO billGroupByVO = new BillGroupByVO();
        billGroupByVO.setReceiveAddress(bill.getReceiveAddress());
        billGroupByVO.setContactTel(bill.getContactTel());
        billGroupByVO.setWaveId(bill.getWaveId());
        billGroupByVO.setConsignorId(bill.getConsignorId());
        billGroupByVO.setPaymentDayType(bill.getPaymentDayType());
        billGroupByVO.setSaleBranchName(bill.getSaleBranchName());
        billGroupByVO.setSameDeptNo(billMergeDTO.isSameDeptNo());
        billGroupByVO.setSamePaymentDayTye(billMergeDTO.isSamePaymentDayTye());
        return billGroupByVO;
    }


    @Override
    @Transactional
    public boolean merge(BillMergeDTO billMergeDTO) {
        // 根据货主设置是否允许合单，0-不允许，1-允许(默认) - by Huanglh 2022-02-08
        List<WaveBillDO> bills = billMapper.queryForMerge(billMergeDTO);
        // 获取货主 id 的 Set
        Set<String> cIds = bills.stream().map(WaveBillDO::getConsignorId).collect(Collectors.toSet());
        if (ObjectUtils.isNotNull(cIds)) {
            RestMessage<Map<String, BaseCargoOwnerMst>> restMessage = chainResource.getCargoOwnerByIds(cIds);
            if (!restMessage.isSuccess()) {
                throw new BizException("货主信息获取失败");
            }
            Map<String, BaseCargoOwnerMst> cargoOwnerMap = restMessage.getData();
            if (ObjectUtils.isNotNull(cargoOwnerMap)) {
                // 因为存在多个货主的订单合并的情况，所以循环判断，如果有任意货主设置了不允许合单，则合单操作不允许
                for (Map.Entry<String, BaseCargoOwnerMst> mstEntry : cargoOwnerMap.entrySet()) {
                    BaseCargoOwnerMst mst = mstEntry.getValue();
                    String result = String.valueOf(mst.getIsOutstockMerge());
                    if ("0".equals(result)) {
                        throw new PrologException("货主 " + mst.getCargoOwnerName() + "(" + mst.getCargoOwnerCode() + ") 不允许合单");
                    }
                }
            }
        }

        RestMessage<BusinessControl> rest = strategyResource.getControlByCode("PICK_TASK_ASK_WAY",
                billMergeDTO.getWarehouseId(), null, userTemplate.getUserDTO().getEnterpriseId(), null);
        if (rest.isSuccess() && rest.getData() != null && "1".equals(rest.getData().getRegValue())) {
            Criteria crt = Criteria.forClass(WaveBillDDO.class);
            RestrictionBuilder builder = crt.restrictionBuilder();
            builder.in("billId", billMergeDTO.getBillIds().toArray());
            crt.buildRestriction();
            List<WaveBillDDO> billDDOS = billDMapper.findByCriteria(crt);
            List<WaveBillDDO> tempBillDDOS=new ArrayList<>();
            for (int i = 0; i < billDDOS.size(); i++) {
                WaveBillDDO waveBillDDO = billDDOS.get(i);
                List<SaleRedRecord> redRecords = saleRedRecordMapper.findByMap(
                        MapUtils.put("billDId", waveBillDDO.getId()).getMap(), SaleRedRecord.class);
                if(redRecords!=null && redRecords.size()>0) {
                    BigDecimal lineRedQty = redRecords.stream().map(SaleRedRecord::getQty).reduce(BigDecimal.ZERO, BigDecimal::add);
                    if(waveBillDDO.getPieceQty().compareTo(lineRedQty)<=0){
                        continue;
                    }
                }
                tempBillDDOS.add(waveBillDDO);
            }
            List<String> itemIds = tempBillDDOS.stream().map(WaveBillDDO::getItemId).distinct().collect(Collectors.toList());
            RestMessage<List<WaveItemInfoVO>> waveItemInfos = itemClient.getWaveItemInfo(itemIds);
            List<WaveItemInfoVO> itemVOs = waveItemInfos.getData();
            Integer caseSum = itemVOs.stream().mapToInt(WaveItemInfoVO::getIsCaseOrPiece).sum();
            if(caseSum!=0 && itemIds.size()!=caseSum){
                throw new BizException("整单拣选模式，商品必须为全部整散分离或全部整散合一");
            }
        }
        // 移动到方法最前面 - by Huanglh 2022-02-08
        // List<WaveBillDO> bills = billMapper.queryForMerge(billMergeDTO);
        Map<BillGroupByVO, List<WaveBillDO>> collect = bills.stream().
                collect(Collectors.groupingBy(bill -> getBillGroupByVO(billMergeDTO, bill)));
        for (Map.Entry<BillGroupByVO, List<WaveBillDO>> entry : collect.entrySet()) {
            List<WaveBillDO> v = entry.getValue();
            if (v.size() <= 1) {
                continue;
            }

            String id = String.valueOf(SnowFlakeUtils.getId(dataCenterId,machineId));
            WaveBillDO billDO = v.get(0);
            BILL_STATUS status = billDO.getStatus();
            if (status != BILL_STATUS.CREATED) {
                throw new PrologException("订单已经下发，不能合单");
            }
/*            WaveBillDO mergedBill = new WaveBillDO();
            BeanUtils.copyProperties(billDO, mergedBill);
            mergedBill.setId(id);
            //合并后的订单
            mergedBill.setIsMerged(2);*/
/*            StringBuilder srcCodeBUilder = new StringBuilder();
            BigDecimal totalQty = BigDecimal.ZERO;
            BigDecimal totalVolume = BigDecimal.ZERO;
            BigDecimal totalPcs = BigDecimal.ZERO;
            BigDecimal totalWeight = BigDecimal.ZERO;*/
            for (WaveBillDO bill : v) {
                bill.setIsMerged(1);
                 /*
                srcCodeBUilder.append(bill.getSrcCode()).append(',');
                totalQty = totalQty.add(bill.getTotalQty());
                totalVolume = totalVolume.add(bill.getTotalVolume());
                totalPcs = totalPcs.add(bill.getTotalPcs());
                totalWeight = totalWeight.add(bill.getTotalWeight());
                 */

                bill.setBillGroupId(id);
                billMapper.update(bill);
            }
/*            mergedBill.setSrcCode(srcCodeBUilder.toString());
            mergedBill.setTotalPcs(totalPcs);
            mergedBill.setTotalWeight(totalWeight);
            mergedBill.setTotalVolume(totalVolume);
            mergedBill.setTotalQty(totalQty);*/
//            billMapper.save(mergedBill);

        }
        return true;
    }

    /**
     * 更新波次操作时的订单
     *
     * @param billDTOs
     */
    @Override
    public void updateWaveBill(List<BillDTO> billDTOs) {
        for (BillDTO billDTO : billDTOs) {
            // 增加补货确认时间字段
            billMapper.updateBillInfo(billDTO);
        }
    }

}
