package com.jwipc.order.logic.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.google.common.collect.Lists;
import com.jwipc.aggregation.ModelAggregationService;
import com.jwipc.api.constant.RestCode;
import com.jwipc.api.dto.SapResposeBody;
import com.jwipc.constant.FlagConst;
import com.jwipc.dto.ExportDataDTO;
import com.jwipc.order.constant.PoSapClient;
import com.jwipc.order.convert.ShortageDataConvert;
import com.jwipc.order.convert.ShortageDetailConvert;
import com.jwipc.order.dto.*;
import com.jwipc.order.entity.FileLog;
import com.jwipc.order.entity.ShortageData;
import com.jwipc.order.entity.ShortageDetail;
import com.jwipc.order.logic.ShortagePlanLogicAsn;
import com.jwipc.order.model.po.ShortagePlanReleasePO;
import com.jwipc.order.model.po.ShortagePlanReplyPO;
import com.jwipc.order.model.query.BasicForeignQuotaQuery;
import com.jwipc.order.model.query.BasicPlanQuery;
import com.jwipc.order.service.IFileLogService;
import com.jwipc.order.service.IShortageDataService;
import com.jwipc.order.service.IShortageDetailService;
import com.jwipc.order.service.IShortagePlanService;
import com.jwipc.sys.client.*;
import com.jwipc.sys.client.dto.DictDTO;
import com.jwipc.util.EasyExcelUtilsAyn;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.File;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @Author: 邓京术
 * @Date: 2025/05/29 14:26
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class ShortagePlanLogicAsnImpl implements ShortagePlanLogicAsn {
    private final IShortagePlanService shortagePlanService;
    private final IShortageDetailService shortageDetailService;
    private final IShortageDataService shortageDataService;
    private final IFileLogService fileLogService;
    private final ShortageDataConvert shortageDataConvert;
    private final ShortageDetailConvert shortageDetailConvert;
    private final FileClient fileClient;
    private final ModelAggregationService modelAggregationService;
    private final PoSapClient sapClient;
    private final DictClient dictClient;
    private final RedisTemplate redisTemplate;
    private final StringRedisTemplate stringRedisTemplate;


    @Override
    @Async
    public void exportToPublishedData(BasicPlanQuery basicPlanQuery,
                                      List<ExportDataDTO> exportDataDTOS, Long userId, Long id) {
//        LocalTime nowTime = LocalTime.now();
//        long secondsUntilMidnight = 86400 - nowTime.toSecondOfDay();
//        redisTemplate.opsForValue().set(String.valueOf(LoginUtil.getUserId()), com.alibaba.fastjson2.JSON.toJSONString(result), secondsUntilMidnight, TimeUnit.SECONDS);
        List<ShortagePlanReleasePO> list = shortagePlanService.findWaitReleaseData(null, basicPlanQuery);
        List<ShortagePlanReleaseDTO> result = shortageDetailConvert.pos2Dtos(list);
        modelAggregationService.processRelationAggregation(result);
        File file = fileClient.createFile(
                "发布_欠料明细数据-" + DateUtil.format(new Date(), DatePattern.NORM_DATE_PATTERN) + "-" + IdWorker.getIdStr() + ".xlsx");
//        File file1 = new File(StrUtil.concat(false, fileLogic.genDatePath().getKey(), File.separator, file.getName()));
//        String fileName = "欠料明细数据-" + DateUtil.format(new Date(), DatePattern.NORM_DATE_PATTERN);
        try {
            EasyExcelUtilsAyn.trendsModelWriteStock(file.getAbsolutePath()
                    , "sheet", result,
                    exportDataDTOS);
            log.info(file.getAbsolutePath() + "===" +file.getPath() + "=="+file.getName());
            FileLog fileLog = fileLogService.getById(id);
            fileLog.setFileName(file.getName());
            fileLog.setFileSize(file.length());
            fileLog.setFilePath(file.getPath());
            fileLog.setStatus("2");
            fileLog.setMsg("数据导出成功");
            fileLog.setPlanId(result.get(0).getPlanId());
            fileLogService.updateById(fileLog);
            //保存文件导出记录
//            log.info((respoDTO==null) +"");
//            log.info((respoDTO!=null)?respoDTO.getFilePath() + "==" + respoDTO.getId(): "上传为空");
            stringRedisTemplate.opsForValue().set("export_published_" + userId, "N", 30, TimeUnit.SECONDS);
        } catch (Exception e) {
            stringRedisTemplate.opsForValue().set("export_published_" + userId, "N", 30, TimeUnit.SECONDS);
            log.error("导出欠料明细数据文件失败.",e.getMessage());
            FileLog fileLog = fileLogService.getById(id);
            fileLog.setStatus("4");
            fileLog.setMsg("数据导出失败 : " + e.getMessage().substring(1,e.getMessage().length() > 1000?800:e.getMessage().length()));
            fileLogService.updateById(fileLog);
        }
        stringRedisTemplate.opsForValue().set("export_published_" + userId, "N", 30, TimeUnit.SECONDS);
    }

    /**
     * 发布数据保存时，超过1万条数据，使用异步保存
     * @param list
     * @param userId
     * @param id
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    @Async
    public void importForPublished(List<ShortagePlanReleaseDTO> list, Long userId, Long id) {
        stringRedisTemplate.opsForValue().set("import_published_" + userId, "Y", 2, TimeUnit.HOURS);
        try {
            //上传 更新 上线日期， 备注（上线日期）， 供应商 邓京术 2025-03-12
            List<ShortageDetail> entities = new ArrayList<>();
            List<ShortageData> updateLists = new ArrayList<>();
            final Set<Long> ids = list.stream().map(ShortagePlanReleaseDTO::getId).collect(Collectors.toSet());
            final List<ShortageData> shortageData1 = shortageDataService.listByIds(ids);
            final Map<Long, Long> idCache = shortageData1.stream()
                    .collect(Collectors.toMap(ShortageData::getId, ShortageData::getDetailId));
            list.forEach(item -> {
                ShortageDetail shortageDetail = new ShortageDetail();
                shortageDetail.setId(idCache.get(item.getId()));
                //取消更新此字段 邓京术 2025-03-12
    //            shortageDetail.setDemandDate(item.getDemandDate());
                //上线日期也可以修改 邓京术 2024-07-26
                shortageDetail.setLaunchDate(item.getLaunchDate()==null?null:item.getLaunchDate());
                shortageDetail.setLaunchDateRemark(item.getLaunchDateRemark()==null?"":item.getLaunchDateRemark());
                entities.add(shortageDetail);
                ShortageData shortageData = new ShortageData();
                shortageData.setSupplierCode(item.getSupplierCode());
                //取消更新此字段 邓京术 2025-03-12
    //            shortageData.setShortageQuantity(item.getShortageQuantity());
                shortageData.setId(item.getId());
                updateLists.add(shortageData);
            });
            if (CollUtil.isNotEmpty(entities)) {
                for (ShortageDetail shortageDetail : entities) {
                    shortageDetailService.update(shortageDetail, new UpdateWrapper<ShortageDetail>().lambda()
                            .set(ShortageDetail::getLaunchDate,shortageDetail.getLaunchDate()==null?null:shortageDetail.getLaunchDate())
                            .set(ShortageDetail::getLaunchDateRemark,shortageDetail.getLaunchDateRemark()).eq(ShortageDetail::getId, shortageDetail.getId())
                    );
                }
            }
            //重新上传的发布欠料数据，状态改为未回复状态 邓京术 2025-07-07
            if (CollUtil.isNotEmpty(updateLists)) {
                for (ShortageData shortageData : updateLists) {
                    shortageDataService.update(shortageData, new UpdateWrapper<ShortageData>().lambda()
                            .set(ShortageData::getSupplierCode,shortageData.getSupplierCode()==null?null:shortageData.getSupplierCode())
                            .set(ShortageData::getStatus,null)
                            .eq(ShortageData::getId, shortageData.getId())
                    );
                }
            }
            FileLog fileLog = fileLogService.getById(id);
            fileLog.setMsg("数据导入成功");
            fileLog.setStatus("3");
            fileLogService.updateById(fileLog);
            stringRedisTemplate.opsForValue().set("import_published_" + userId, "N", 30, TimeUnit.SECONDS);
        } catch (Exception e) {
            log.error("数据导入失败:" + e.getMessage());
            FileLog fileLog = fileLogService.getById(id);
            fileLog.setStatus("4");
            fileLog.setMsg("数据导入失败:" + e.getMessage().substring(1,e.getMessage().length()>1000?800:e.getMessage().length()));
            fileLogService.updateById(fileLog);
            stringRedisTemplate.opsForValue().set("import_published_" + userId, "N", 30, TimeUnit.SECONDS);
        }
    }

    @Override
    @Async
    public void exportToReplyData(BasicForeignQuotaQuery basicPlanQuery,
                                      List<ExportDataDTO> exportDataDTOS, Long userId, Long id) {
        List<ShortagePlanReplyPO> replyPOS = shortageDataService.getSupplierReply(null, basicPlanQuery);
        final List<ShortagePlanReplyDTO> result = shortageDataConvert.pos2Dtos(replyPOS);
        modelAggregationService.processRelationAggregation(result);
        File file = fileClient.createFile(
                "回复_欠料明细数据-" + DateUtil.format(new Date(), DatePattern.NORM_DATE_PATTERN) + "-" + IdWorker.getIdStr() + ".xlsx");
        try {
            EasyExcelUtilsAyn.trendsModelWriteStock(file.getAbsolutePath()
                    , "sheet", result,
                    exportDataDTOS);
            log.info(file.getAbsolutePath() + "===" +file.getPath() + "=="+file.getName());
            FileLog fileLog = fileLogService.getById(id);
            fileLog.setFileName(file.getName());
            fileLog.setFileSize(file.length());
            fileLog.setFilePath(file.getPath());
            fileLog.setStatus("2");
            fileLog.setMsg("数据导出成功");
            fileLog.setPlanId(result.get(0).getPlanId());
            fileLogService.updateById(fileLog);
            stringRedisTemplate.opsForValue().set("export_reply_data_" + userId, "N", 30, TimeUnit.SECONDS);
        } catch (Exception e) {
            stringRedisTemplate.opsForValue().set("export_reply_data_" + userId, "N", 30, TimeUnit.SECONDS);
            log.error("导出欠料明细数据文件失败.",e.getMessage());
            FileLog fileLog = fileLogService.getById(id);
            fileLog.setStatus("4");
            fileLog.setMsg("数据导出失败 : " + e.getMessage().substring(1,e.getMessage().length() > 1000?800:e.getMessage().length()));
            fileLogService.updateById(fileLog);
        }
        stringRedisTemplate.opsForValue().set("export_reply_data_" + userId, "N", 30, TimeUnit.SECONDS);
    }

    /**
     * 回复页面，欠料数据导入1万条以上，5万条以下数据成功后，推送给sap
     * @param shortageData
     */
    private String pushToSap(List<ShortageData> shortageData) {
        try {
            //需要查询明细数据
            final Set<Long> detailIds = shortageData.stream().map(ShortageData::getDetailId).collect(Collectors.toSet());
            final List<ShortageDetail> shortageDetails = shortageDetailService.listByIds(detailIds);
            final Map<Long, ShortageDetail> detailMap = shortageDetails.stream()
                    .collect(Collectors.toMap(ShortageDetail::getId, a -> a));
            UpdateShortagePlanSapDTO.ITData data = new UpdateShortagePlanSapDTO.ITData();
            List<UpdateShortagePlanSapDTO.ITData.Item> items = Lists.newArrayList();
            for (ShortageData shortageDatum : shortageData) {
                final Long detailId = shortageDatum.getDetailId();
                final ShortageDetail shortageDetail = detailMap.get(detailId);
                UpdateShortagePlanSapDTO.ITData.Item item = new UpdateShortagePlanSapDTO.ITData.Item();
                item.MATNR = shortageDetail.getMaterialCode();
                item.WERKS = shortageDetail.getFactoryCode();
                item.MATNR1 = shortageDetail.getSubstituteMaterial1();
                item.MATNR2 = shortageDetail.getSubstituteMaterial2();
                item.MATNR3 = shortageDetail.getSubstituteMaterial3();
                item.AUFNR = shortageDetail.getWorkOrderNumber();
                item.Z_VER = shortageDetail.getShortageVersion();
                item.Z_XQRQ = shortageDetail.getDemandDate();
                item.Z_QLSL = shortageDetail.getShortageQuantity();
                shortageDataConvert.fillItem(item, shortageDatum);
                items.add(item);
            }
            data.setItem(items);
            UpdateShortagePlanSapDTO shortagePlanSapDTO = new UpdateShortagePlanSapDTO();
            shortagePlanSapDTO.setIT_DATA(data);
            final SapResposeBody sapResposeBody = sapClient.updateShortagePlan(shortagePlanSapDTO);
            if (!sapResposeBody.getES_RET().getCODE().equals(RestCode.SAP_SUCCESS.getCode())) {
                return sapResposeBody.getES_RET().getMSG();
            } else {
                return RestCode.SAP_SUCCESS.getCode();
            }
        } catch (Exception e) {
            e.printStackTrace();
            return e.getMessage();
        }
    }

    /**
     * 回复页面（采购回复页面），导入超过1万条少于5万条数据成功后，保存至数据库异步保存。
     * @param list
     * @param userId
     * @param id
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    @Async
    public void importForReply(List<ShortagePlanReplyDTO> list,Long userId,Long id) {
        try {
            stringRedisTemplate.opsForValue().set("import_reply_data_" + userId, "Y", 2,TimeUnit.HOURS);
            //保存时，所有的数据都要覆盖, 陈敏提出的问题， 2025-03-26 邓京术
            if (null != list) {
                List<ShortageData> dataList = shortageDataConvert.dtos2entities1(list);
                for (ShortageData shortageData : dataList) {
                    shortageDataService.update(shortageData, new UpdateWrapper<ShortageData>().lambda().set(
                                    ShortageData::getMainMaterialReplyDate, shortageData.getMainMaterialReplyDate() == null ? null : shortageData.getMainMaterialReplyDate())
                            .set(ShortageData::getMainMaterialReply, shortageData.getMainMaterialReply() == null ? null : shortageData.getMainMaterialReply())
                            .set(ShortageData::getRemark, shortageData.getRemark() == null ? null : shortageData.getRemark())
                            .set(ShortageData::getSuccessorMaterialReplyQuantity, shortageData.getSuccessorMaterialReplyQuantity() == null ? null : shortageData.getSuccessorMaterialReplyQuantity())
                            .set(ShortageData::getSuccessorMaterialDeliveryDateReply, shortageData.getSuccessorMaterialDeliveryDateReply() == null ? null : shortageData.getSuccessorMaterialDeliveryDateReply())
                            .set(ShortageData::getSubstituteMaterial1ReplyQuantity, shortageData.getSubstituteMaterial1ReplyQuantity() == null ? null : shortageData.getSubstituteMaterial1ReplyQuantity())
                            .set(ShortageData::getSubstituteMaterial1DeliveryDate, shortageData.getSubstituteMaterial1DeliveryDate() == null ? null : shortageData.getSubstituteMaterial1DeliveryDate())
                            .set(ShortageData::getSubstituteMaterial2ReplyQuantity, shortageData.getSubstituteMaterial2ReplyQuantity() == null ? null : shortageData.getSubstituteMaterial2ReplyQuantity())
                            .set(ShortageData::getSubstituteMaterial2DeliveryDate, shortageData.getSubstituteMaterial2DeliveryDate() == null ? null : shortageData.getSubstituteMaterial2DeliveryDate())
                            .set(ShortageData::getSubstituteMaterial3ReplyQuantity, shortageData.getSubstituteMaterial3ReplyQuantity() == null ? null : shortageData.getSubstituteMaterial3ReplyQuantity())
                            .set(ShortageData::getSubstituteMaterial3DeliveryDate, shortageData.getSubstituteMaterial3DeliveryDate() == null ? null : shortageData.getSubstituteMaterial3DeliveryDate())
                            .set(ShortageData::getCreateUser, userId)
                            .set(ShortageData::getUpdateTime, LocalDateTime.now())
                            .eq(ShortageData::getId, shortageData.getId())
                    );
                }
            }
            FileLog fileLog = fileLogService.getById(id);
            fileLog.setStatus("6");
            fileLog.setMsg("数据导入成功，待提交ERP(SAP).");
            fileLogService.updateById(fileLog);
            //异步推送Sap
//            confirmReply(list != null, userId, id);
            stringRedisTemplate.opsForValue().set("import_reply_data_" + userId, "N", 30,TimeUnit.SECONDS);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("数据导入失败:" + e.getMessage());
            FileLog fileLog = fileLogService.getById(id);
            fileLog.setStatus("4");
            fileLog.setMsg("数据导入失败:" + e.getMessage().substring(0,e.getMessage().length()>1000?800:e.getMessage().length()));
            fileLogService.updateById(fileLog);
            stringRedisTemplate.opsForValue().set("import_reply_data_" + userId, "N", 30, TimeUnit.SECONDS);
        }
        stringRedisTemplate.opsForValue().set("import_reply_data_" + userId, "N", 30,TimeUnit.SECONDS);
    }

    /**
     * 超过1万条以上的欠料数据回复直接推送到sap
     * @param userId
     */
    @Override
    @Async
    public void confirmReply(boolean pushAgain, Long userId, Long logId) {
        Object dataobj = redisTemplate.opsForValue().get("import_reply_" + userId);
        if (null == dataobj) {
            log.info("没有要推送ERP(SAP)的数据");
            FileLog fileLog = fileLogService.getById(logId);
            fileLog.setStatus("7");
            fileLog.setMsg("没有要推送ERP(SAP)的欠料数据");
            fileLogService.updateById(fileLog);
            stringRedisTemplate.opsForValue().set("confirm_reply_data_" + userId, "N", 30,TimeUnit.SECONDS);
            return;
        }
        stringRedisTemplate.opsForValue().set("confirm_reply_data_" + userId, "Y", 2,TimeUnit.HOURS);
        List<ShortagePlanReplyDTO> shortagePlanReplyDTOS = com.alibaba.fastjson2.JSON.parseArray(dataobj.toString(), ShortagePlanReplyDTO.class);
        final Set<Long> ids = shortagePlanReplyDTOS.stream().map(ShortagePlanReleaseDTO::getId).collect(Collectors.toSet());
        FileLog fileLog1 = fileLogService.getById(logId);
        fileLog1.setStatus("8");
        fileLog1.setMsg("数据导入成功"+shortagePlanReplyDTOS.size()+"条，正在提交ERP(SAP)，请耐心等待...");
        fileLogService.updateById(fileLog1);
        final List<ShortageData> dataList = shortageDataService.listByIds(ids);
        // 推送SAP时，黄色栏位（11个栏位）都为空时不推送SAP，不更新状态； 2025-03-12 邓京术
        // 使用 Stream API 筛选数据
        List<ShortageData> filteredDataList = dataList.stream()
                .filter(data -> data.getMainMaterialReplyDate() != null ||
                                StringUtils.isNotEmpty(data.getMainMaterialReply()) ||
                                StringUtils.isNotEmpty(data.getRemark())
                                || null != data.getSuccessorMaterialReplyQuantity() || null != data.getSuccessorMaterialDeliveryDateReply()
                                || null != data.getSubstituteMaterial1ReplyQuantity() || null != data.getSubstituteMaterial1DeliveryDate()
                                || null != data.getSubstituteMaterial2ReplyQuantity() || null != data.getSubstituteMaterial2DeliveryDate()
                                || null != data.getSubstituteMaterial3ReplyQuantity() || null != data.getSubstituteMaterial3DeliveryDate()
                )
                .collect(Collectors.toList());
        if(CollUtil.isNotEmpty(filteredDataList)) {
            if (pushAgain) {
                for (ShortageData shortageData : filteredDataList) {
                    shortageDataService.update(shortageData, new UpdateWrapper<ShortageData>().lambda().set(
                            ShortageData::getStatus,FlagConst.Y.getCode())
                            .set(ShortageData::getCreateUser,userId)
                            .set(ShortageData::getUpdateTime, LocalDateTime.now())
                            .eq(ShortageData::getId, shortageData.getId())
                    );
                }
            }
            //每次向sap推送的数量，动态配置，邓京术 2025-05-28
            List<DictDTO> dictDTOS = dictClient.getDicByType("push_to_sap_data_count");
            int count = Integer.parseInt(dictDTOS.get(0).getCode());
            //异步执行导出
            int times = filteredDataList.size() / count;
            int remainder = filteredDataList.size() % count;
            for (int i = 0; i < times; i ++) {
                int pushTimes = 3;
                String resCode = "E";
                while (pushTimes != 0 && resCode.equals("E")) {
                    resCode = pushToSap(filteredDataList.subList(i * count, (i + 1) * count));
                    log.info("推送次数==" + pushTimes  + "/" + resCode);
                    pushTimes -- ;
                }
                if (!resCode.equals("S")) {
                    FileLog fileLog = fileLogService.getById(logId);
                    fileLog.setStatus("9");
                    fileLog.setMsg("数据导入成功，提交ERP(SAP)失败：" + resCode);
                    fileLogService.updateById(fileLog);
                    stringRedisTemplate.opsForValue().set("confirm_reply_data_" + userId, "N", 30,TimeUnit.SECONDS);
                    return;
                }
            }
            int pushTimes = 3;
            String resCode = "E";
            while (remainder > 0 && pushTimes != 0 && resCode.equals("E")) {
                resCode = pushToSap(filteredDataList.subList(times * count, filteredDataList.size()));
                log.info("推送次数==" + pushTimes  + "/" + resCode);
                pushTimes -- ;
            }
            if (remainder > 0 && !resCode.equals("S")) {
                FileLog fileLog = fileLogService.getById(logId);
                fileLog.setStatus("9");
                fileLog.setMsg("数据导入成功"+dataList.size()+"，提交ERP(SAP)失败：" + resCode);
                fileLogService.updateById(fileLog);
                stringRedisTemplate.opsForValue().set("confirm_reply_data_" + userId, "N", 30,TimeUnit.SECONDS);
                return;
            }
            FileLog fileLog = fileLogService.getById(logId);
            fileLog.setStatus("8");
            fileLog.setMsg("数据导入成功"+dataList.size()+"条，提交ERP(SAP)成功" + dataList.size() + "条.");
            fileLogService.updateById(fileLog);
        } else {
            //记录file log日志，没有要推送的欠料数据。
            FileLog fileLog = fileLogService.getById(logId);
            fileLog.setStatus("7");
            fileLog.setMsg("没有要推送ERP(SAP)的欠料数据");
            fileLogService.updateById(fileLog);
        }
        stringRedisTemplate.opsForValue().set("confirm_reply_data_" + userId, "N", 30,TimeUnit.SECONDS);
    }
}
