package com.scm.contract.service.print;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.scm.contract.dao.print.PrintCodeDao;
import com.scm.contract.model.apply.DictionaryTagType;
import com.scm.contract.model.apply.LabelApplication;
import com.scm.contract.model.apply.LabelApplicationDetail;
import com.scm.contract.model.cancel.TagCancel;
import com.scm.contract.model.management.Provider;
import com.scm.contract.model.management.TagData;
import com.scm.contract.model.print.*;
import com.scm.contract.service.apply.DictionaryTagTypeService;
import com.scm.contract.service.apply.LabelApplicationDetailService;
import com.scm.contract.service.apply.LabelApplicationService;
import com.scm.contract.service.cancel.TagCancelService;
import com.scm.contract.service.management.ProviderService;
import com.scm.contract.service.management.TagDataService;
import com.scm.contract.tools.MaterielSerialNumber;
import com.scm.contract.tools.RestTemplateUtils;
import com.scm.contract.tools.Tools;
import com.scm.contract.tools.sm2.SM2Utils;
import com.set.direwolf.core.entity.User;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.bouncycastle.crypto.InvalidCipherTextException;
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 org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author zuo
 * @date 2024/6/25 16:34
 */
@Service
@Transactional(readOnly = true, rollbackFor = Exception.class)
public class PrintCodeService {

    @Value("${direwolf.sm2.publicKey}")
    private String publicKey;

    @Value("${direwolf.sm2.publicKey}")
    private String privateKey;

    @Value("${direwolf.print.url}")
    private String sendUrl;

    @Autowired
    private PrintCodeDao printCodeDao;

    @Autowired
    private TagDataService tagDataService;

    @Autowired
    private LabelApplicationService labelApplicationService;

    @Autowired
    private ProviderService providerService;

    @Autowired
    private MaterielSerialNumber materielSerialNumber;

    @Autowired
    private LabelApplicationDetailService labelApplicationDetailService;

    @Autowired
    private DictionaryTagTypeService tagTypeService;

    @Autowired
    private ReprintCodeService reprintCodeService;

    @Autowired
    private ReprintCodeDetailService reprintCodeDetailService;

    @Autowired
    private TagCancelService tagCancelService;

    public List<PrintCode> getListByDetailIds(List<String> detailIds) {
        return printCodeDao.getListByDetailIds(detailIds);
    }

    public List<PrintCode> getListByDetailId(String detailId) {
        return printCodeDao.getListByDetailId(detailId);
    }

    @Transactional
    public synchronized Boolean saveList(List<PrintCode> printCodes) {
        return this.printCodeDao.saveList(printCodes);
    }

    public TagPreview getPreviewById(String id) {
        TagPreview tagPreview = new TagPreview();
        LabelApplication labelApplication = labelApplicationService.getById(id, true);
        if (!Objects.isNull(labelApplication)) {
            String tagApplyStateId = labelApplication.getTagApplyStateId();
            if (Integer.parseInt(tagApplyStateId) < 3) throw new RuntimeException("当前订单还没赋码，不能进行预览");
            List<LabelApplicationDetail> details = labelApplication.getDetails();
            LabelApplicationDetail detail = details.get(0);
            String detailId = detail.getId();
            String minimumPackingUnit = Objects.isNull(detail.getMinimumPackingUnit()) ? "" : detail.getMinimumPackingUnit();
            List<PrintCode> printCodes = getListByDetailIds(Collections.singletonList(detailId));
            if (CollectionUtils.isNotEmpty(printCodes)) {
                PrintCode printCode = printCodes.get(0);
                tagPreview.setGoodsName(printCode.getGoodsName());
                tagPreview.setPackageNumber(printCode.getMinimumPackingNumber() + minimumPackingUnit);
                tagPreview.setProviderName(printCode.getProviderName());
                try {
                    tagPreview.setQrCode(SM2Utils.encrypt(publicKey, printCode.getQrCode()));
                } catch (InvalidCipherTextException e) {
                    throw new RuntimeException("国密算法加密出错：" + e);
                }
            }
        }
        return tagPreview;
    }

    @Transactional
    public synchronized Boolean printSend(List<String> labelApplicationIds) {
        if (CollectionUtils.isEmpty(labelApplicationIds)) throw new RuntimeException("没有可以打印的订单");
        List<LabelApplication> labelApplications = labelApplicationService.getListByIds(labelApplicationIds);
        List<String> idList = new ArrayList<>();
        for (LabelApplication labelApplication : labelApplications) {
            String tagApplyStateId = labelApplication.getTagApplyStateId();
            if ("3".equals(tagApplyStateId)) {
                idList.add(labelApplication.getId());
            }
        }
        if (CollectionUtils.isNotEmpty(idList)) {
            List<LabelApplicationDetail> details = labelApplicationDetailService.getListByLabelApplicationIds(idList);
            details.sort(Comparator.comparing(LabelApplicationDetail::getGoodsCode)
                    .thenComparing(LabelApplicationDetail::getMinimumPackingNumber));
            List<String> detailIds = new ArrayList<>();
            List<LabelApplicationDetail> detailList = new ArrayList<>();
            Map<String, LabelApplicationDetail> detailMap = new HashMap<>();
            for (LabelApplicationDetail detail : details) {
                Integer isPrint = detail.getIsPrint();
                if (!Objects.isNull(isPrint) && 0 == isPrint) {
                    detailIds.add(detail.getId());
                    detailList.add(detail);
                    detailMap.put(detail.getId(), detail);
                }
            }

            if (CollectionUtils.isNotEmpty(detailIds)) {
                List<PrintCode> printCodes = this.printCodeDao.getListByDetailIds(detailIds);
                if (CollectionUtils.isNotEmpty(printCodes)) {
                    //按照detailIds的顺序排序printCodes
                    printCodes.sort(((o1, o2) -> {
                        int io1 = detailIds.indexOf(o1.getDetailId());
                        int io2 = detailIds.indexOf(o2.getDetailId());

                        if (io1 != -1) {
                            io1 = printCodes.size() - io1;
                        }
                        if (io2 != -1) {
                            io2 = printCodes.size() - io2;
                        }
                        return io2 - io1;
                    }));

                    Map<String, PrintTagSend> printTagSendMap = new LinkedHashMap<>();
                    for (PrintCode printCode : printCodes) {
                        String detailId = printCode.getDetailId();
                        if (!printTagSendMap.containsKey(detailId)) {
                            LabelApplicationDetail detail = detailMap.getOrDefault(detailId, new LabelApplicationDetail());
                            PrintTagSend printTagSend = new PrintTagSend();
                            printTagSend.setId(detailId);
                            printTagSend.setProviderName(printCode.getProviderName());
                            String unit = Objects.isNull(detail.getMinimumPackingUnit()) ? "" : detail.getMinimumPackingUnit();
                            printTagSend.setPackageNumber(printCode.getMinimumPackingNumber() + unit);
                            printTagSend.setMaterialName(printCode.getGoodsName());
                            printTagSend.setQrCode(new ArrayList<>());
                            printTagSendMap.put(detailId, printTagSend);
                        }
                        List<String> qrCodeList = printTagSendMap.get(detailId).getQrCode();
                        qrCodeList.add(printCode.getQrCode());
                    }

                    if (CollectionUtils.isNotEmpty(printTagSendMap)) {
                        List<PrintTagSend> printTagSends = new ArrayList<>(printTagSendMap.values());
                        Map map = RestTemplateUtils.postSend(sendUrl + "/api/print", printTagSends);
                        if (Objects.isNull(map)) throw new RuntimeException("打印机故障，请稍后再试");
                        Integer code = (Integer) map.get("code");
                        if (Objects.isNull(code) || 200 != code) {
                            throw new RuntimeException("打印机故障，请稍后再试");
                        }
                        Date date = new Date();
                        for (LabelApplicationDetail detail : detailList) {
                            detail.setIsPrint(1);
                            detail.setPrintStatus(1001);
                            detail.setPrintTime(date);
                        }
                        //修改狀態
                        labelApplicationService.updateStatus(idList, "4", true);
                        labelApplicationDetailService.updatePrintBash(detailList);
                    }
                }
            }
        }
        return true;
    }

    @Transactional
    public synchronized Boolean reprintSend(List<String> printCodeIds) {
        if (CollectionUtils.isEmpty(printCodeIds)) throw new RuntimeException("没有可以重新打印的EPC编码");
        /**
         * 校验重打信息
         */
        List<PrintCode> printCodes = getListByIds(printCodeIds);
        if (CollectionUtils.isEmpty(printCodes)) throw new RuntimeException("没有可以重新打印的EPC编码");

        /**
         * 校验重打的printCodeIds中有没有正在重打的
         */
        //正在打印的
        List<ReprintCode> reprinting = reprintCodeService.getReprinting(1);
        if (CollectionUtils.isNotEmpty(reprinting)) {
            List<String> reprintCodeIds = reprinting.stream().map(ReprintCode::getId).distinct().collect(Collectors.toList());
            List<ReprintCodeDetail> reprintCodeDetails = reprintCodeDetailService.getListByReprintCodeIds(reprintCodeIds);
            if (CollectionUtils.isNotEmpty(reprintCodeDetails)) {
                Map<String, ReprintCodeDetail> map = new HashMap<>();
                for (ReprintCodeDetail reprintCodeDetail : reprintCodeDetails) {
                    String printCodeId = reprintCodeDetail.getPrintCodeId();
                    map.put(printCodeId, reprintCodeDetail);
                }
                for (String printCodeId : printCodeIds) {
                    ReprintCodeDetail reprintCodeDetail = map.get(printCodeId);
                    if (!Objects.isNull(reprintCodeDetail)) {
                        throw new RuntimeException("重打任务中，有正在重打的EPC<br/>" + reprintCodeDetail.getNewQrCode());
                    }
                }
            }
        }


        /**
         * 按照detailId分重打任务,并校验是否已有发货的EPC
         */

        Map<String, List<PrintCode>> printCodeMap = new HashMap<>();
        Set<String> detailIds = new HashSet<>();
        for (PrintCode printCode : printCodes) {
            String detailId = printCode.getDetailId();
            Integer isDeliverGoods = printCode.getIsDeliverGoods();
            String qrCode = printCode.getQrCode();
            if (!Objects.isNull(isDeliverGoods) && 1 == isDeliverGoods) {
                throw new RuntimeException("重打编码中有已交付的EPC编码<br/>" + qrCode);
            }
            if (!printCodeMap.containsKey(detailId)) printCodeMap.put(detailId, new ArrayList<>());
            printCodeMap.get(detailId).add(printCode);
            detailIds.add(detailId);
        }

        /**
         * 订单是否在待发货状态校验
         */
        List<LabelApplicationDetail> details = labelApplicationDetailService.listByDetailIds(new ArrayList<>(detailIds));
        List<String> labelApplicationIds = new ArrayList<>();
        Map<String, LabelApplicationDetail> detailMap = new HashMap<>();
        for (LabelApplicationDetail detail : details) {
            String labelApplicationId = detail.getLabelApplicationId();
            String detailId = detail.getId();
            labelApplicationIds.add(labelApplicationId);
            detailMap.put(detailId, detail);
        }
        List<LabelApplication> labelApplications = labelApplicationService.getListByIds(labelApplicationIds);
        if (CollectionUtils.isNotEmpty(labelApplications)) {
            long count = labelApplications.stream().filter(labelApplication -> !labelApplication.getTagApplyStateId().equals("5")).count();
            if (count > 0) throw new RuntimeException("只能重打发货以状态的EPC");
        }

        /**
         * 按任务进行重打
         */
        for (Map.Entry<String, List<PrintCode>> entry : printCodeMap.entrySet()) {
            List<PrintCode> oldPrintCodes = entry.getValue();
            PrintCode printCode = oldPrintCodes.get(0);
            String tagType = printCode.getTagType();
            String detailId = printCode.getDetailId();
            LabelApplicationDetail detail = detailMap.get(detailId);
            if (Objects.isNull(detail)) continue;

            /**
             * 库存校验
             */
            //获取每种标签的剩余可使用数量的MAP
            Map<String, Long> surplusNumberMap = tagDataService.getSurplusNumber();
            Long tagSurplusNumber = surplusNumberMap.getOrDefault(tagType, 0L);
            if (tagSurplusNumber - oldPrintCodes.size() < 0) {
                List<DictionaryTagType> tagTypes = tagTypeService.listByIds(Collections.singletonList(tagType));
                if (CollectionUtils.isEmpty(tagTypes)) {
                    throw new RuntimeException("标签库存不足，不能进行重打");
                } else {
                    DictionaryTagType tagType1 = tagTypes.get(0);
                    throw new RuntimeException(tagType1.getName() + "库存不足，不能进行重打");
                }
            }

            int number = oldPrintCodes.size();
            Long numberLong = Long.valueOf(String.valueOf(number));
            //重打后的EPC编码信息List
            List<PrintCode> newPrintCodes = new ArrayList<>();
            List<ReprintCodeDetail> reprintCodeDetails = new ArrayList<>();
            ReprintCode reprintCode = new ReprintCode(detail, numberLong);

            /**
             * 对需要重打的编码进行重新获取流水号和赋码
             */
            for (int i = 0; i < number; i++) {
                PrintCode oldPrintCode = oldPrintCodes.get(i);
                PrintCode newPrintCode = new PrintCode(oldPrintCode);
                String goodsCode = newPrintCode.getGoodsCode();
                Integer minimumPackingNumber = newPrintCode.getMinimumPackingNumber();
                //获取新流水号
                String SerialNumberKey = goodsCode + "|" + minimumPackingNumber;
                String SerialNumber = materielSerialNumber.generateId(SerialNumberKey);
                newPrintCode.setSerialNumber(SerialNumber);


                //计算新的校验码
                String enterpriseCode = newPrintCode.getEnterpriseCode();
                String providerCode = newPrintCode.getProviderCode();
                String tagTypeId = newPrintCode.getTagType();
                StringBuilder qrCode = new StringBuilder("");
                qrCode.append(Tools.decimalToHex(Long.valueOf(enterpriseCode), 2));
                qrCode.append(Tools.decimalToHex(Long.valueOf(goodsCode), 8));
                qrCode.append(Tools.decimalToHex(Long.valueOf(providerCode), 9));
                qrCode.append(Tools.decimalToHex(Long.valueOf(tagTypeId), 1));
                qrCode.append(Tools.decimalToHex(Long.valueOf(minimumPackingNumber.toString()), 3));
                qrCode.append(Tools.decimalToHex(Long.valueOf(SerialNumber), 8));
                String verificationStr = enterpriseCode +
                        goodsCode +
                        providerCode +
                        tagTypeId +
                        minimumPackingNumber +
                        SerialNumber;
                String verificationCode = Tools.calculateCheckDigit(verificationStr);
                newPrintCode.setVerificationCode(verificationCode);

                //计算新的EPC编码
                qrCode.append(verificationCode);
                newPrintCode.setQrCode(qrCode.toString());
                newPrintCodes.add(newPrintCode);

                //reprint_code_detail信息
                reprintCodeDetails.add(new ReprintCodeDetail(reprintCode.getId(), newPrintCode, oldPrintCode));
            }

            if (CollectionUtils.isNotEmpty(newPrintCodes)) {
                //发送打印信息
                PrintTagSend printTagSend = new PrintTagSend(reprintCode, newPrintCodes);
                Map map = RestTemplateUtils.postSend(sendUrl + "/api/print", Collections.singletonList(printTagSend));
                if (Objects.isNull(map)) throw new RuntimeException("打印机故障，请稍后再试");
                Integer code = (Integer) map.get("code");
                if (Objects.isNull(code) || 200 != code) {
                    throw new RuntimeException("打印机故障，请稍后再试");
                }
                reprintCode.setIsPrint(1);
                reprintCode.setPrintStatus(1001);

                //修改newPrintCodes
                this.updateBash(newPrintCodes);
                //保存reprintCodeDetails
                reprintCodeDetailService.saveBash(reprintCodeDetails);
                //保存reprintCode
                reprintCodeService.save(reprintCode);
                //保存作废信息并修改作废库存（修改库存已在保存信息中写了）
                User user = Tools.getUser();
                TagCancel tagCancel = new TagCancel();
                tagCancel.setId(IdWorker.getIdStr());
                tagCancel.setNumber(numberLong);
                tagCancel.setTagTypeId(reprintCode.getTagTypeId());
                tagCancel.setStatus("1");
                tagCancel.setOperatorId(user.getId());
                tagCancel.setOperatorName(user.getNickName());
                tagCancel.setReprintCodeId(printTagSend.getId());
                tagCancelService.save(tagCancel);
            }
        }
        return true;
    }

    @Transactional
    public synchronized int updateBash(List<PrintCode> newPrintCodes) {
        return printCodeDao.updateBash(newPrintCodes);
    }

    /**
     * 定时任务专用（发现任务莫名消失补发，分重新打印的和第一次打印的）
     *
     * @param detailIds      第一次打印的ids
     * @param reprintCodeIds 重新打印的ids
     * @return
     */
    @Transactional
    public synchronized void printSendByJob(List<String> detailIds, List<String> reprintCodeIds) {
        List<PrintCode> printCodes = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(detailIds)) {
            printCodes = this.printCodeDao.getListByDetailIds(detailIds);
        }
        List<ReprintCodeDetail> reprintCodeDetails = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(reprintCodeIds)) {
            reprintCodeDetails = reprintCodeDetailService.getListByReprintCodeIds(reprintCodeIds);
        }
        Map<String, PrintTagSend> printTagSendMap = new HashMap<>();
        if (CollectionUtils.isNotEmpty(printCodes)) {
            for (PrintCode printCode : printCodes) {
                String detailId = printCode.getDetailId();
                if (!printTagSendMap.containsKey(detailId)) {
                    PrintTagSend printTagSend = new PrintTagSend();
                    printTagSend.setId(detailId);
                    printTagSend.setProviderName(printCode.getProviderName());
                    printTagSend.setPackageNumber(String.valueOf(printCode.getMinimumPackingNumber()));
                    printTagSend.setMaterialName(printCode.getGoodsName());
                    printTagSend.setQrCode(new ArrayList<>());
                    printTagSendMap.put(detailId, printTagSend);
                }
                List<String> qrCodeList = printTagSendMap.get(detailId).getQrCode();
                qrCodeList.add(printCode.getQrCode());
            }
        }
        if (CollectionUtils.isNotEmpty(reprintCodeDetails)) {
            List<ReprintCode> reprintCodes = this.reprintCodeService.getListByIds(reprintCodeIds);
            Map<String, ReprintCode> reprintCodeMap = new HashMap<>();
            Set<String> detailSet = new HashSet<>();
            for (ReprintCode reprintCode : reprintCodes) {
                reprintCodeMap.put(reprintCode.getId(), reprintCode);
                String detailId = reprintCode.getDetailId();
                detailSet.add(detailId);
            }
            List<PrintCode> printCodeList = printCodeDao.getListByDetailIds(new ArrayList<>(detailSet));
            Map<String, String> printCodeProviderNameMap = new HashMap<>();
            for (PrintCode printCode : printCodeList) {
                printCodeProviderNameMap.put(printCode.getId(), printCode.getProviderName());
            }

            for (ReprintCodeDetail reprintCodeDetail : reprintCodeDetails) {
                String reprintCodeId = reprintCodeDetail.getReprintCodeId();
                if (!printTagSendMap.containsKey(reprintCodeId)) {
                    ReprintCode reprintCode = reprintCodeMap.get(reprintCodeId);
                    String printCodeId = reprintCodeDetail.getPrintCodeId();
                    PrintTagSend printTagSend = new PrintTagSend();
                    printTagSend.setId(reprintCodeId);
                    printTagSend.setProviderName(printCodeProviderNameMap.get(printCodeId));
                    printTagSend.setPackageNumber(String.valueOf(reprintCode.getMinimumPackingNumber()));
                    printTagSend.setMaterialName(reprintCode.getGoodsName());
                    printTagSend.setQrCode(new ArrayList<>());
                    printTagSendMap.put(reprintCodeId, printTagSend);
                }
                List<String> qrCodeList = printTagSendMap.get(reprintCodeId).getQrCode();
                qrCodeList.add(reprintCodeDetail.getNewQrCode());
            }
        }

        if (CollectionUtils.isNotEmpty(printTagSendMap)) {
            List<PrintTagSend> printTagSends = new ArrayList<>(printTagSendMap.values());
            Map map = RestTemplateUtils.postSend(sendUrl + "/api/print", printTagSends);
            System.out.println(map);
        }

    }

    @Transactional
    public synchronized Integer coding(List<String> ids) {
        if (CollectionUtils.isEmpty(ids)) throw new RuntimeException("没有可以赋码的订单");
        List<LabelApplication> labelApplications = labelApplicationService.getListByIds(ids);
        labelApplicationService.setName(labelApplications, true);
        List<LabelApplication> labelApplicationList = new ArrayList<>();
        List<String> labelApplicationIds = new ArrayList<>();
        Set<String> providerIds = new HashSet<>();
        for (LabelApplication labelApplication : labelApplications) {
            String tagApplyStateId = labelApplication.getTagApplyStateId();
            if ("2".equals(tagApplyStateId)) {
                providerIds.add(labelApplication.getProviderId());
                labelApplicationList.add(labelApplication);
                labelApplicationIds.add(labelApplication.getId());
            }
        }

        Map<String, Provider> providerMap = new HashMap<>();
        if (CollectionUtils.isNotEmpty(providerIds)) {
            List<Provider> providers = providerService.listByIds(new ArrayList<>(providerIds));
            for (Provider provider : providers) {
                providerMap.put(provider.getId(), provider);
            }
        }

        List<PrintCode> printCodes = new ArrayList<>();
        for (LabelApplication labelApplication : labelApplicationList) {
            List<LabelApplicationDetail> details = labelApplication.getDetails();
            String providerId = labelApplication.getProviderId();
            Provider provider = providerMap.getOrDefault(providerId, new Provider());
            String providerName = provider.getName();
            String providerCode = provider.getCode();
            for (LabelApplicationDetail detail : details) {
                Long number = detail.getNumber();
                String detailId = detail.getId();
                String materielCode = detail.getMaterielCode();
                String materielDescribe = detail.getMaterielDescribe();
                String goodsCode = detail.getGoodsCode();
                String goodsName = detail.getGoodsName();
                String tagTypeId = detail.getTagTypeId();
                Integer minimumPackingNumber = detail.getMinimumPackingNumber();
                for (long i = 0L; i < number; i++) {
                    //code
                    StringBuilder qrCode = new StringBuilder("");
                    //获取流水号
                    String SerialNumberKey = goodsCode + "|" + minimumPackingNumber;
                    String SerialNumber = materielSerialNumber.generateId(SerialNumberKey);
                    PrintCode printCode = new PrintCode();
                    printCode.setId(IdWorker.getIdStr());
                    printCode.setDetailId(detailId);
                    qrCode.append(Tools.decimalToHex(10L, 2));
                    printCode.setEnterpriseCode("10");
                    printCode.setGoodsCode(goodsCode);
                    qrCode.append(Tools.decimalToHex(Long.valueOf(goodsCode), 8));
                    printCode.setGoodsName(goodsName);
                    qrCode.append(Tools.decimalToHex(Long.valueOf(providerCode), 9));
                    printCode.setProviderCode(providerCode);
                    printCode.setProviderName(providerName);
                    qrCode.append(Tools.decimalToHex(Long.valueOf(tagTypeId), 1));
                    printCode.setTagType(tagTypeId);
                    qrCode.append(Tools.decimalToHex(Long.valueOf(minimumPackingNumber.toString()), 3));
                    printCode.setMinimumPackingNumber(minimumPackingNumber);
                    qrCode.append(Tools.decimalToHex(Long.valueOf(SerialNumber), 8));
                    printCode.setSerialNumber(SerialNumber);
                    String verificationStr = 10L +
                            goodsCode +
                            providerCode +
                            tagTypeId +
                            minimumPackingNumber +
                            SerialNumber;
                    String verificationCode = Tools.calculateCheckDigit(verificationStr);
                    qrCode.append(verificationCode);
                    printCode.setVerificationCode(verificationCode);
                    printCode.setQrCode(qrCode.toString());
                    printCode.setIsDeliverGoods(0);
                    printCode.setMaterielCode(materielCode);
                    printCode.setMaterielDescribe(materielDescribe);
                    printCodes.add(printCode);
                }
            }
        }
        if (CollectionUtils.isNotEmpty(printCodes)) {
            printCodeDao.saveList(printCodes);
            labelApplicationService.updateStatus(labelApplicationIds, "3", true);
        }
        return printCodes.size();
    }

    public Map<String, Long> getMaxSerialNumber() {
        List<PrintCode> printCodes = printCodeDao.getMaxSerialNumber();
        Map<String, Long> returnMap = new HashMap<>();
        if (CollectionUtils.isNotEmpty(printCodes)) {
            for (PrintCode printCode : printCodes) {
                String goodsCode = printCode.getGoodsCode();
                Integer minimumPackingNumber = printCode.getMinimumPackingNumber();
                String serialNumber = printCode.getSerialNumber();
                if (serialNumber.length() != 10) {
                    throw new RuntimeException("流水号错误");
                }
                String year = serialNumber.substring(0, 4);
                String number = serialNumber.substring(4);
                String key = goodsCode + "|" + minimumPackingNumber + "|" + year;
                returnMap.put(key, Long.valueOf(number));
            }
        }
        return returnMap;
    }

    public List<SerialNumberRange> getSerialNumberRange(List<String> detailIds) {
        return printCodeDao.getSerialNumberRange(detailIds);
    }

    @Transactional
    public synchronized Boolean cancelPrint(String id) {
        List<LabelApplicationDetail> details = labelApplicationDetailService.listByDetailIds(Collections.singletonList(id));
        List<ReprintCode> reprintCodes = reprintCodeService.getListByIds(Collections.singletonList(id));

        if (CollectionUtils.isNotEmpty(details)) {
            LabelApplicationDetail detail = details.get(0);
            Integer isPrint = detail.getIsPrint();
            if (1 == isPrint) {
                Map map = RestTemplateUtils.postSend(sendUrl + "/api/print/cancel/" + id, new Object());
                if (Objects.isNull(map)) throw new RuntimeException("打印机故障，请稍后再试");
                Integer code = (Integer) map.get("code");
                if (Objects.isNull(code) || 200 != code) {
                    throw new RuntimeException("打印机故障，请稍后再试");
                }
                detail.setPrintStatus(null);
                detail.setIsPrint(0);
                labelApplicationDetailService.updatePrintBash(details);

                //修改订单状态为已赋码
                String labelApplicationId = detail.getLabelApplicationId();
                List<String> labelApplicationIds = Collections.singletonList(labelApplicationId);
                List<LabelApplication> labelApplications = labelApplicationService.getListByIds(labelApplicationIds);
                List<LabelApplication> labelApplicationList = new ArrayList<>();
                for (LabelApplication labelApplication : labelApplications) {
                    String tagApplyStateId = labelApplication.getTagApplyStateId();
                    if (!"3".equals(tagApplyStateId)) {
                        labelApplication.setTagApplyStateId("3");
                        labelApplicationList.add(labelApplication);
                    }
                }
                if (CollectionUtils.isNotEmpty(labelApplicationList)) {
                    labelApplicationService.updateListById(labelApplicationList);
                }

                //扣除库存
                Long number = detail.getNumber();
                String tagTypeId = detail.getTagTypeId();

                TagData tagData = new TagData();
                tagData.setDataType("2");
                tagData.setTagTypeId(tagTypeId);
                tagData.setNumber(-1 * number);
                tagData.setStatus("4");

                List<TagData> tagDataList = new ArrayList<>();
                tagDataList.add(tagData);
                tagDataService.saveOrUpdateBash(null, null, tagDataList, true);
            }
        } else if (CollectionUtils.isNotEmpty(reprintCodes)) {
            ReprintCode reprintCode = reprintCodes.get(0);
            Integer isPrint = reprintCode.getIsPrint();
            if (1 == isPrint) {
                Map map = RestTemplateUtils.postSend(sendUrl + "/api/print/cancel/" + id, new Object());
                if (Objects.isNull(map)) throw new RuntimeException("打印机故障，请稍后再试");
                Integer code = (Integer) map.get("code");
                if (Objects.isNull(code) || 200 != code) {
                    throw new RuntimeException("打印机故障，请稍后再试");
                }
                Object data = map.get("data");
                if (Objects.isNull(data)) {
                    throw new RuntimeException("打印机无应答");
                }
                List<PrintCodeStatus> printCodeStatuses = JSON.parseArray(data.toString(), PrintCodeStatus.class);
                Map<String, Boolean> printCodeStatusMap = new HashMap<>();
                boolean b = false;
                for (PrintCodeStatus printCodeStatus : printCodeStatuses) {
                    Boolean isPrintOver = printCodeStatus.getIsPrintOver();
                    if (Objects.isNull(isPrintOver)) isPrintOver = false;
                    printCodeStatusMap.put(printCodeStatus.getQrCode(), isPrintOver);
                    if (isPrintOver) b = true;
                }


                Long number = reprintCode.getNumber();
                String tagTypeId = reprintCode.getTagTypeId();

                List<ReprintCodeDetail> reprintCodeDetails = reprintCodeDetailService.getListByReprintCodeIds(Collections.singletonList(id));
                List<ReprintCodeDetail> delReprintCodeDetails = new ArrayList<>();
                long saveNumber = 0;
                for (ReprintCodeDetail reprintCodeDetail : reprintCodeDetails) {
                    String newQrCode = reprintCodeDetail.getNewQrCode();
                    if (printCodeStatusMap.getOrDefault(newQrCode, false)) {
                        saveNumber++;
                    } else {
                        delReprintCodeDetails.add(reprintCodeDetail);
                    }
                }
                //修改没有打印的reprintCodeDetails的OldQrCode到printCode中
                if (CollectionUtils.isNotEmpty(delReprintCodeDetails)) {
                    List<String> delReprintCodeDetailIds = new ArrayList<>();
                    Map<String, ReprintCodeDetail> delReprintCodeDetailMap = new HashMap<>();
                    for (ReprintCodeDetail delReprintCodeDetail : delReprintCodeDetails) {
                        delReprintCodeDetailIds.add(delReprintCodeDetail.getId());
                        delReprintCodeDetailMap.put(delReprintCodeDetail.getPrintCodeId(), delReprintCodeDetail);
                    }
                    //修改没有打印的printCode的OldQrCode到printCode中
                    List<PrintCode> printCodes = getListByIds(new ArrayList<>(delReprintCodeDetailMap.keySet()));
                    for (PrintCode printCode : printCodes) {
                        String printCodeId = printCode.getId();
                        ReprintCodeDetail reprintCodeDetail = delReprintCodeDetailMap.get(printCodeId);
                        if (Objects.isNull(reprintCodeDetail)) continue;
                        String oldQrCode = reprintCodeDetail.getOldQrCode();
                        String oldSerialNumber = reprintCodeDetail.getOldSerialNumber();
                        printCode.setQrCode(oldQrCode);
                        printCode.setSerialNumber(oldSerialNumber);
                        printCode.setVerificationCode(oldQrCode.substring(oldQrCode.length() - 1));
                    }
                    //修改printCode
                    this.updateBash(printCodes);

                    //删除reprintCodeDetails
                    reprintCodeDetailService.deleteByIds(delReprintCodeDetailIds);
                }
                if (b) {
                    //修改reprintCode
                    reprintCode.setNumber(saveNumber);
                    reprintCode.setIsPrint(2);
                    reprintCode.setPrintStatus(1000);
                    reprintCodeService.updatePrintBash(reprintCodes);
                    //修改重打记录数量
                    TagCancel tagCancel = new TagCancel();
                    tagCancel.setId(id);
                    tagCancel.setNumber(saveNumber);
                    tagCancelService.updateNumber(tagCancel);
                    //处理库存
                    TagData tagData = new TagData();
                    tagData.setDataType("3");
                    tagData.setTagTypeId(tagTypeId);
                    tagData.setNumber(-1 * (number - saveNumber));
                    tagData.setStatus("1");
                    List<TagData> tagDataList = new ArrayList<>();
                    tagDataList.add(tagData);
                    tagDataService.saveOrUpdateBash(null, null, tagDataList, true);
                } else {
                    //全删除
                    reprintCodeService.deleteById(id);
                    //删除重打记录
                    tagCancelService.deleteByReprintCodeId(id);
                    //处理库存
                    TagData tagData = new TagData();
                    tagData.setDataType("3");
                    tagData.setTagTypeId(tagTypeId);
                    tagData.setNumber(-1 * number);
                    tagData.setStatus("1");
                    List<TagData> tagDataList = new ArrayList<>();
                    tagDataList.add(tagData);
                    tagDataService.saveOrUpdateBash(null, null, tagDataList, true);
                }
            }
        }
        return true;
    }

    private List<PrintCode> getListByIds(List<String> ids) {
        return printCodeDao.getListByIds(ids);
    }

    @Transactional
    public synchronized Boolean changePrintStatus(String id, String path, Integer printStatus) {
        List<LabelApplicationDetail> details = labelApplicationDetailService.listByDetailIds(Collections.singletonList(id));
        List<ReprintCode> reprintCodes = reprintCodeService.getListByIds(Collections.singletonList(id));

        if (CollectionUtils.isNotEmpty(details)) {
            LabelApplicationDetail detail = details.get(0);
            Integer isPrint = detail.getIsPrint();
            if (1 == isPrint) {
                Map map = RestTemplateUtils.postSend(sendUrl + path + id, new Object());
                if (Objects.isNull(map)) throw new RuntimeException("打印机故障，请稍后再试");
                Integer code = (Integer) map.get("code");
                if (Objects.isNull(code) || 200 != code) {
                    throw new RuntimeException("打印机故障，请稍后再试");
                }
                detail.setPrintStatus(printStatus);
                labelApplicationDetailService.updatePrintBash(details);
            }
        } else if (CollectionUtils.isNotEmpty(reprintCodes)) {
            ReprintCode reprintCode = reprintCodes.get(0);
            Integer isPrint = reprintCode.getIsPrint();
            if (1 == isPrint) {
                Map map = RestTemplateUtils.postSend(sendUrl + path + id, new Object());
                if (Objects.isNull(map)) throw new RuntimeException("打印机故障，请稍后再试");
                Integer code = (Integer) map.get("code");
                if (Objects.isNull(code) || 200 != code) {
                    throw new RuntimeException("打印机故障，请稍后再试");
                }
                reprintCode.setPrintStatus(printStatus);
                reprintCodeService.updatePrintBash(reprintCodes);
            }
        }
        return true;
    }

    @Transactional
    public synchronized Boolean continueALLPrint() {
        List<Printing> printings = labelApplicationDetailService.getPrinting();
        if (CollectionUtils.isEmpty(printings)) {
            return true;
        }
        List<String> idList = new ArrayList<>();
        List<String> detailIds = new ArrayList<>();
        List<String> reprintCodeIds = new ArrayList<>();
        for (Printing printing : printings) {
            Integer printStatus = printing.getPrintStatus();
            if (Objects.isNull(printStatus) || 2 != printStatus) continue;
            Boolean isReprint = printing.getIsReprint();
            if (Objects.isNull(isReprint)) isReprint = false;
            if (isReprint) {
                reprintCodeIds.add(printing.getId());
            } else {
                detailIds.add(printing.getId());
            }
            idList.add(printing.getId());
        }
        if (CollectionUtils.isEmpty(idList)) {
            return true;
        }

        Map<String, Object> map1 = new HashMap<>();
        map1.put("ids", idList);
        Map map = RestTemplateUtils.postSend(sendUrl + "/api/print/continue", map1);
        if (Objects.isNull(map)) throw new RuntimeException("打印机故障，请稍后再试");
        Integer code = (Integer) map.get("code");
        if (Objects.isNull(code) || 200 != code) {
            throw new RuntimeException("打印机故障，请稍后再试");
        }

        if (CollectionUtils.isNotEmpty(detailIds)) {
            List<LabelApplicationDetail> details = labelApplicationDetailService.listByDetailIds(detailIds);
            for (LabelApplicationDetail detail : details) {
                detail.setPrintStatus(1001);
            }
            labelApplicationDetailService.updatePrintBash(details);
        }
        if (CollectionUtils.isNotEmpty(reprintCodeIds)) {
            List<ReprintCode> reprintCodes = reprintCodeService.getListByIds(reprintCodeIds);
            for (ReprintCode reprintCode : reprintCodes) {
                reprintCode.setPrintStatus(1001);
            }
            reprintCodeService.updatePrintBash(reprintCodes);
        }
        return true;
    }

    public int updateIsDeliverGoods(List<String> printCodeIds, Integer isDeliverGoods) {
        return this.printCodeDao.updateIsDeliverGoods(printCodeIds, isDeliverGoods);
    }

    public List<PrintCode> getCodesByDetailId(String detailId) {
        List<PrintCode> printCodes = getListByDetailId(detailId);
        List<ReprintCode> reprinting = reprintCodeService.getReprinting(1);
        List<String> reprintCodeIds = new ArrayList<>();
        for (ReprintCode reprintCode : reprinting) {
            if (reprintCode.getDetailId().equals(detailId)) {
                reprintCodeIds.add(reprintCode.getId());
            }
        }
        Map<String, Boolean> printCodePrintingMap = new HashMap<>();
        if (CollectionUtils.isNotEmpty(reprintCodeIds)) {
            List<ReprintCodeDetail> reprintCodeDetails = reprintCodeDetailService.getListByReprintCodeIds(reprintCodeIds);
            for (ReprintCodeDetail reprintCodeDetail : reprintCodeDetails) {
                String printCodeId = reprintCodeDetail.getPrintCodeId();
                printCodePrintingMap.put(printCodeId, true);
            }

        }
        for (PrintCode printCode : printCodes) {
            printCode.setIsReprinting(printCodePrintingMap.getOrDefault(printCode.getId(), false));
        }
        return printCodes;
    }

    @Transactional
    public synchronized Integer reprintSendImport(MultipartFile file) {
        List<String> epcs = new ArrayList<>();
        try {
            XSSFWorkbook workbook = new XSSFWorkbook(file.getInputStream());
            XSSFSheet sheet = workbook.getSheetAt(0);
            if (Objects.isNull(sheet)) throw new RuntimeException("空表,不能进行导入");
            int totalRowNum = sheet.getLastRowNum();
            XSSFRow headRow = sheet.getRow(0);
            if (Objects.isNull(headRow)) throw new RuntimeException("空表,不能进行导入");
            for (int i = 1; i <= totalRowNum; i++) {
                XSSFRow row = sheet.getRow(i);
                XSSFCell cell = row.getCell(0);
                if (Objects.isNull(cell)) throw new RuntimeException("错误（第" + (i + 1) + "行)");
                CellType cellTypeEnum = cell.getCellTypeEnum();
                if (cellTypeEnum != CellType.STRING) throw new RuntimeException("错误（第" + (i + 1) + "行)");
                String epc = cell.getStringCellValue();
                if (StringUtils.isBlank(epc)) throw new RuntimeException("错误（第" + (i + 1) + "行)");
                epcs.add(epc);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        int number = 0;
        if (CollectionUtils.isNotEmpty(epcs)) {
            List<PrintCode> printCodes = this.getListByEpcs(epcs);
            if (CollectionUtils.isNotEmpty(printCodes)) {
                List<String> printCodeIds = printCodes.stream().map(PrintCode::getId).collect(Collectors.toList());
                reprintSend(printCodeIds);
                number = printCodes.size();
            }
        }
        return number;
    }

    private List<PrintCode> getListByEpcs(List<String> epcs) {
        return printCodeDao.getListByEpcs(epcs);
    }
}
