package nckd.jxccl.swc.hsas.business.outdata;

import com.google.common.collect.Lists;
import nckd.jxccl.base.common.utils.DateTimeUtils;
import kd.bos.context.RequestContext;
import kd.bos.dataentity.entity.DynamicObject;
import kd.bos.dataentity.entity.DynamicObjectCollection;
import kd.bos.dataentity.resource.ResManager;
import kd.bos.dataentity.utils.StringUtils;
import kd.bos.db.tx.TX;
import kd.bos.db.tx.TXHandle;
import kd.bos.entity.EntityMetadataCache;
import kd.bos.entity.EntityType;
import kd.bos.entity.MainEntityType;
import kd.bos.exception.KDBizException;
import kd.bos.fileservice.FileService;
import kd.bos.fileservice.FileServiceFactory;
import kd.bos.impt.ExcelReader;
import kd.bos.impt.SheetHandler;
import kd.bos.logging.Log;
import kd.bos.logging.LogFactory;
import kd.bos.orm.query.QFilter;
import kd.bos.threads.ThreadPools;
import kd.hr.hbp.business.bgtask.HRBackgroundTaskHelper;
import kd.hr.hbp.business.servicehelper.HRBaseServiceHelper;
import kd.hr.hbp.common.cache.HRAppCache;
import kd.hr.hbp.common.cache.IHRAppCache;
import nckd.jxccl.swc.hsas.business.utils.OutImportTaskUtils;
import nckd.jxccl.swc.hsas.common.OutImpPresetItem;
import nckd.jxccl.swc.hsas.formplugin.web.outdata.helper.OutImpTemplateHelper;
import org.apache.commons.lang.exception.ExceptionUtils;

import java.io.InputStream;
import java.math.BigDecimal;
import java.text.MessageFormat;
import java.util.*;

public class OutImportTaskGuideImportService {

    private static final Log logger = LogFactory.getLog(OutImportTaskGuideImportService.class);
    private Long importTaskId;
    private Long tplId;
    private Long welfarepayerId;
    private Long periodId;
    private String periodNumber;
    private String url;
    private static final long DATE_TYPE_ID = 1050L;
    private static final long NUMBER_TYPE_ID = 1010L;
    private static final long DECIMAL_TYPE_ID = 1020L;
    private static final String DATE_FORMAT = "yyyy-MM-dd";

    public OutImportTaskGuideImportService(String url, Long importTaskId, Long tplId, Long periodId, String periodNumber) {
        this.url = url;
        this.importTaskId = importTaskId;
        this.tplId = tplId;
        this.periodId = periodId;
        this.periodNumber = periodNumber;
    }

    public Map<String, String> parseAndCheckExcelData(Long orgId, String pageId, String importTaskName) {
        Map<String, String> resultMap = new HashMap(5);
        List<Map<Integer, String>> dataHeadList = Lists.newArrayListWithCapacity(10);
        List<Map<Integer, String>> dataRowList = Lists.newArrayListWithCapacity(10);
        DynamicObject sinsurData = OutImportTaskUtils.getTplData(tplId, "id,number,nckd_startline,nckd_endline", (QFilter)null);
        logger.info("readExcelData begin");
        this.readExcelData(dataHeadList, dataRowList, sinsurData.getInt("nckd_startline") - 2, sinsurData.getInt("nckd_endline") - 1);
        logger.info("readExcelData end");
        if (dataHeadList.size() == 0) {
            throw new KDBizException(ResManager.loadKDString("没有找到对应的列标题。", "ImportTaskGuideImportService_0", "swc-hsas-business", new Object[0]));
        } else if (dataRowList.size() == 0) {
            resultMap.put("errorMsg", ResManager.loadKDString("未解析到有效数据，请录入数据后再试。", "ImportTaskGuideImportService_17", "swc-hsas-business", new Object[0]));
            return resultMap;
        } else {
            Set<String> errDataList = new HashSet(16);
            List<Map<String, String>> columnList = OutImportTaskUtils.getColumnHeadList(this.tplId, this.importTaskId);
            Map<Integer, OutImpPresetItem> tplFixItemMap = this.getTplFixItemMap(columnList);
            this.checkExcelTemplate(dataHeadList, errDataList, tplFixItemMap);
            String cacheKey;
            if (errDataList.size() > 0) {
                cacheKey = ResManager.loadKDString("模板列名“{0}”不能删除。", "ImportTaskGuideImportService_1", "swc-hsas-business", new Object[0]);
                String errorMsg = OutImportTaskUtils.join(errDataList, "、");
                throw new KDBizException(MessageFormat.format(cacheKey, errorMsg));
            } else {
                cacheKey = UUID.randomUUID().toString();
                resultMap.put("totalCount", String.valueOf(dataRowList.size()));
                resultMap.put("cacheKey", cacheKey);
                Map<String, Object> params = new HashMap(16);
                String name = ResManager.loadKDString("外部系统数据导入-%s", "ImportTaskGuideImportService_10", "swc-hsas-business", new Object[0]);
                name = String.format(Locale.ROOT, name, importTaskName);
                params.put("importTaskId", this.importTaskId);
                params.put("verifyId", cacheKey);
                params.put("totalCount", dataRowList.size());
                params.put("startTime", DateTimeUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss"));
                params.put("progressType", "22");
                String bgTaskId = HRBackgroundTaskHelper.getInstance().createBaskgroundTask("swc_hsas_importtaskguide_import", name, true, pageId, params);
                IHRAppCache appCache = HRAppCache.get(String.format(Locale.ROOT, "bggroud_taskid_%s", cacheKey));
                appCache.put("bgTaskId", bgTaskId);
                appCache.put("totalCount", dataRowList.size());
                RequestContext ctx = RequestContext.get();
                ThreadPools.executeOnce("IMPORTTASK_IMPORT_IN_POOL_ASYNC", () -> {
                    RequestContext.copyAndSet(ctx);
                    logger.info("parseAndCheckExcelData ,tranceId={}", ctx.getTraceId());
                    this.importData(dataHeadList, dataRowList, sinsurData, cacheKey, null, orgId, tplFixItemMap);
                });
                return resultMap;
            }
        }
    }

    private void importData(List<Map<Integer, String>> dataHeadList, List<Map<Integer, String>> dataRowList, DynamicObject migrationTplData, String cacheKey, String writeTaskType, Long orgId, Map<Integer, OutImpPresetItem> tplFixItemMap) {
        logger.info("importData begin,importTaskId={}", this.importTaskId);
        List<Map<String, String>> errDataList = new ArrayList(10);
        OutImportTaskGuideExportService service = new OutImportTaskGuideExportService();
        if (!this.isCancel(cacheKey)) {
            boolean hasWorkStartDate = tplFixItemMap.containsKey(11);
            Map<Integer, List<Map<String, String>>> columnIndexMap = this.getColumnIndexMap();
            Map<Integer, List<Map<String, String>>> commonColumnIndexMap = this.getCommonColumnIndexMap();
            int defaultSize = 500;
            List<List<Map<Integer, String>>> splitDataList = OutImportTaskUtils.split(dataRowList, defaultSize);
            int lineIndex = 0;
            List<Map<Integer, String>> tempList = null;
            int index = 0;

            for(int size = splitDataList.size(); index < size; ++index) {
                tempList = (List)splitDataList.get(index);
                lineIndex = index * defaultSize;
                this.checkImportData(tempList, errDataList, commonColumnIndexMap, columnIndexMap, lineIndex, cacheKey, writeTaskType, orgId, hasWorkStartDate, false);
            }

            if (!this.isCancel(cacheKey)) {
                if (errDataList.size() > 0) {
                    String fileUrl = service.getImportErrorExcelFile(dataHeadList, dataRowList, errDataList, migrationTplData.getInt("nckd_startline") - 2, writeTaskType, columnIndexMap, hasWorkStartDate);
                    OutImportTaskUtils.updateImportDataProgress(0, 0, cacheKey, fileUrl);
                }

                logger.info("importData end,importTaskId={}", this.importTaskId);
            }
        }
    }

    private Boolean isCancel(String cacheKey) {
        String key = String.format(Locale.ROOT, "import_cache_%s", cacheKey);
        IHRAppCache appCache = HRAppCache.get(key);
        Boolean isCancel = (Boolean)appCache.get(String.format(Locale.ROOT, "isCancel_%s", cacheKey), Boolean.class);
        return isCancel != null && isCancel ? Boolean.TRUE : Boolean.FALSE;
    }

    // TODO 校验导入数据
    private void checkImportData(List<Map<Integer, String>> dataRowList, List<Map<String, String>> allErrDataList, Map<Integer, List<Map<String, String>>> commonColumnIndexMap, Map<Integer, List<Map<String, String>>> columnIndexMap, int lineIndex, String cacheKey, String writeTaskType, Long orgId, boolean hasWorkStartDate, boolean isMulTimeOnePeriod) {
        Map<Integer, Map<Integer, Object>> passDataRowMap = new HashMap(16);
        int lineIndexVer = lineIndex;
        List<Map<String, String>> errDataList = new ArrayList(10);
        int successCount = 0;
        int failCount = 0;

        try {
            boolean isSuccess = true;
            Iterator<Map<Integer, String>> it = dataRowList.iterator();
            while(it.hasNext()) {
                Map<Integer, Object> passRowMap = new HashMap(16);
                Map<Integer, String> rowMap = (Map)it.next();
                isSuccess = this.checkFieldRequriedForCommon(commonColumnIndexMap, rowMap, errDataList, lineIndex, passRowMap);
                if(!isSuccess) {
                    ++lineIndex;
                } else if (!this.checkItemValue(columnIndexMap, passRowMap, errDataList, lineIndex, rowMap)) {
                    ++lineIndex;
                } else {
                    if(isSuccess) {
                        passDataRowMap.put(lineIndex, passRowMap);
                    }
                    ++lineIndex;
                }
            }

            if (!this.isCancel(cacheKey)) {
                this.saveData(passDataRowMap, commonColumnIndexMap, columnIndexMap);
                successCount = passDataRowMap.size();
                failCount = dataRowList.size() - successCount;
                return;
            }

        } catch (Exception e) {
            logger.error(e);
            successCount = 0;
            failCount = dataRowList.size();
            errDataList.clear();
            this.setErrorData(dataRowList.size(), lineIndexVer, errDataList, e.getMessage());
            return;
        } finally {
            allErrDataList.addAll(errDataList);
            OutImportTaskUtils.updateImportDataProgress(successCount, failCount, cacheKey, (String)null);
        }
    }

    private void saveData(Map<Integer, Map<Integer, Object>> passDataRowMap, Map<Integer, List<Map<String, String>>> commonColumnIndexMap, Map<Integer, List<Map<String, String>>> columnIndexMap) {
        if (passDataRowMap != null && passDataRowMap.size() != 0) {
            DynamicObjectCollection result = new DynamicObjectCollection();
            MainEntityType type = EntityMetadataCache.getDataEntityType("nckd_outtempdata");
            EntityType entryType = (EntityType)type.getAllEntities().get("entryentity");
            DynamicObject temporary = null;
            Map<Integer, Object> rowMap = null;
            Iterator<Map.Entry<Integer, Map<Integer, Object>>> it = passDataRowMap.entrySet().iterator();
            while(it.hasNext()) {
                Map.Entry<Integer, Map<Integer, Object>> entry = (Map.Entry)it.next();
                temporary = (DynamicObject)type.createInstance();
                rowMap = (Map)entry.getValue();
                temporary.set("nckd_datastatus", "0");
                //OutImportTaskUtils.setTemporaryValueForCommon(commonColumnIndexMap, temporary, rowMap, periodNumber);
                temporary.set("nckd_importtask", this.importTaskId);
                temporary.set("creator", RequestContext.get().getCurrUserId());
                temporary.set("createtime", new Date());
                temporary.set("modifier", RequestContext.get().getCurrUserId());
                temporary.set("modifytime", new Date());
                temporary.set("billstatus", "A");
                temporary.set("entryentity", OutImportTaskUtils.getItemEntryList(temporary, rowMap, columnIndexMap, commonColumnIndexMap, entryType));
                result.add(temporary);
            }
            HRBaseServiceHelper helper = new HRBaseServiceHelper("nckd_outtempdata");
            TXHandle txHandle = TX.requiresNew();

            try {
                helper.save(result);
                helper.setEntityName("nckd_outimptask");
                DynamicObject importTask = helper.queryOne(this.importTaskId);
                importTask.set("nckd_taskstatus", "1");
                importTask.set("modifytime", new Date());
                importTask.set("modifier", RequestContext.get().getCurrUserId());
                importTask.set("nckd_count", importTask.getInt("nckd_count") + result.size());
                helper.updateOne(importTask);
            } catch (Exception var18) {
                logger.error("save error,", var18);
                txHandle.markRollback();
                throw new KDBizException(ExceptionUtils.getFullStackTrace(var18));
            } finally {
                txHandle.close();
            }
        }
    }

    private boolean checkItemValue(Map<Integer, List<Map<String, String>>> columnIndexMap,
                                   Map<Integer, Object> passRowMap,
                                   List<Map<String, String>> errDataList,
                                   int lineIndex,
                                   Map<Integer, String> rowMap) {
        boolean isPass = true;
        String errorMsgTemplate = ResManager.loadKDString("{0}：数据格式填写错误。",
                "ImportTaskGuideImportService_4", "swc-hsas-business", new Object[0]);

        for (Map.Entry<Integer, List<Map<String, String>>> columnEntry : columnIndexMap.entrySet()) {
            Integer columnIndex = columnEntry.getKey();
            String itemValue = rowMap.get(columnIndex);

            if (StringUtils.isEmpty(itemValue)) {
                continue;
            }

            for (Map<String, String> itemMap : columnEntry.getValue()) {
                long dataTypeId = Long.parseLong(itemMap.get("datatypeid"));
                String itemName = itemMap.get("name");

                try {
                    String processedValue = processValueByType(itemValue, dataTypeId);
                    passRowMap.put(columnIndex, processedValue);
                } catch (Exception e) {
                    isPass = false;
                    errDataList.add(assembleErrMap(lineIndex, false,
                            MessageFormat.format(errorMsgTemplate, itemName)));
                }
            }
        }
        return isPass;
    }

    private String processValueByType(String value, long dataTypeId) throws Exception {
        if (DATE_TYPE_ID == dataTypeId) {
            return DateTimeUtils.format(DateTimeUtils.parseDate(value), DATE_FORMAT);
        }

        if (NUMBER_TYPE_ID == dataTypeId || DECIMAL_TYPE_ID == dataTypeId) {
            return new BigDecimal(value).toPlainString();
        }

        return value;
    }

    private void setErrorData(int size, int lineIndexVer, List<Map<String, String>> errDataList, String errorMsg) {
        for(int i = 0; i < size; ++i) {
            errDataList.add(this.assembleErrMap(lineIndexVer, false, errorMsg));
            ++lineIndexVer;
        }
    }

    private boolean checkFieldRequriedForCommon(Map<Integer, List<Map<String, String>>> commonColumnIndexMap, Map<Integer, String> rowMap, List<Map<String, String>> errDataList, int lineIndex, Map<Integer, Object> passRowMap) {
        String msg = ResManager.loadKDString("请填写“{0}”。", "ImportTaskGuideImportService_2", "swc-hsas-business", new Object[0]);

        for (Map.Entry<Integer, List<Map<String, String>>> columnEntry : commonColumnIndexMap.entrySet()) {
            Integer columnIndex = columnEntry.getKey();
            String itemValue = rowMap.get(columnIndex);

            if (StringUtils.isEmpty(itemValue)) {
                errDataList.add(this.assembleErrMap(lineIndex, false, MessageFormat.format(msg, columnEntry.getValue().get(0).get("name"))));
                return false;
            }

            for (Map<String, String> itemMap : columnEntry.getValue()) {
                passRowMap.put(columnIndex, itemValue);
            }
        }
        return true;
    }

    private Map<Integer, List<Map<String, String>>> getColumnIndexMap() {
        List<Map<String, String>> columnHeadList = OutImportTaskUtils.getColumnHeadList(this.tplId, this.importTaskId);
        Map<Integer, List<Map<String, String>>> columnIndexMap = new HashMap<>(columnHeadList.size());

        for (Map<String, String> obj : columnHeadList) {
            // 只处理非固定列（itemType != "0"）
            if (!"0".equals(obj.get("itemType"))) {
                // 使用 computeIfAbsent 简化逻辑
                columnIndexMap.computeIfAbsent(Integer.valueOf(obj.get("columnIndex")),
                        k -> new ArrayList<>()).add(obj);
            }
        }
        return columnIndexMap;
    }

    /**
     * 获取默认项目Map
     * @return
     */
    private Map<Integer, List<Map<String, String>>> getCommonColumnIndexMap() {
        List<Map<String, String>> columnHeadList = OutImportTaskUtils.getColumnHeadList(this.tplId, this.importTaskId);
        Map<Integer, List<Map<String, String>>> columnIndexMap = new HashMap<>(columnHeadList.size());

        for (Map<String, String> obj : columnHeadList) {
            // 只处理固定列（itemType == "0"）
            if ("0".equals(obj.get("itemType"))) {
                // 使用 computeIfAbsent 简化逻辑
                columnIndexMap.computeIfAbsent(Integer.valueOf(obj.get("columnIndex")),
                        k -> new ArrayList<>()).add(obj);
            }
        }
        return columnIndexMap;
    }


    private Map<String, String> assembleErrMap(int dataIndex, boolean isAll, String msg) {
        Map<String, String> errMap = new HashMap(4);
        errMap.put("errMsg", msg);
        errMap.put("isAll", isAll ? "1" : "0");
        errMap.put("dataIndex", String.valueOf(dataIndex));
        return errMap;
    }

    private void checkExcelTemplate(List<Map<Integer, String>> dataHeadList, Set<String> errDataList, Map<Integer, OutImpPresetItem> tplFixItemMap) {
        Map<Integer, String> dataHeadMap = (Map)dataHeadList.get(0);
        String excelName = null;
        StringBuilder tplName = new StringBuilder();
        Iterator var7 = tplFixItemMap.entrySet().iterator();
        while(var7.hasNext()) {
            Map.Entry<Integer, OutImpPresetItem> entry = (Map.Entry)var7.next();
            if(entry.getValue() == null)  continue;
            excelName = (String)dataHeadMap.get(entry.getKey());
            if (StringUtils.isEmpty(excelName)) {
                errDataList.add(((OutImpPresetItem)entry.getValue()).getItemName());
            } else {
                tplName.setLength(0);
                tplName.append('*');
//                if (((OutImpPresetItem)entry.getValue()).isRequire()) {
//                    tplName.append('*');
//                }
                tplName.append(((OutImpPresetItem)entry.getValue()).getItemName());
                if (!excelName.equals(tplName.toString())) {
                    // 系统导出来的模板列名有*，线下的模板可能不会加*号，这里再判断一下，如果有*号，去掉*号再匹配一下
                    if(tplName.toString().startsWith("*")) {
                        if(!excelName.equals(tplName.toString().substring(1, tplName.toString().length()))) {
                            errDataList.add(((OutImpPresetItem)entry.getValue()).getItemName());
                        }
                    }
                }
            }
        }
    }

    private Map<Integer, OutImpPresetItem> getTplFixItemMap(List<Map<String, String>> columnList) {
        Map<Integer, OutImpPresetItem> map = new HashMap(16);
        Map<Long, OutImpPresetItem> presetItemEnumMap = OutImpTemplateHelper.getPresetItemEnumMap();
        OutImpPresetItem tempItemEnum = null;
        Iterator it = columnList.iterator();
        while(it.hasNext()) {
            Map<String, String> temp = (Map)it.next();
            if ("0".equals(temp.get("itemType"))) {
                Long itemId = Long.valueOf((String)temp.get("id"));
                String columnIndexStr = (String)temp.get("columnIndex");
                tempItemEnum = (OutImpPresetItem)presetItemEnumMap.get(itemId);
                map.put(Integer.valueOf(columnIndexStr), tempItemEnum);
            }
        }
        return map;
    }

    /**
     * 读excel数据
     * @param dataHeadList
     * @param dataRowList
     * @param headIndex
     * @param dataEndIndex
     * @throws KDBizException
     */
    private void readExcelData(final List<Map<Integer, String>> dataHeadList, final List<Map<Integer, String>> dataRowList, final int headIndex, final int dataEndIndex) throws KDBizException {
        FileService service = FileServiceFactory.getAttachmentFileService();
        try {
            InputStream in = service.getInputStream(this.url);
            Throwable exp = null;
            try {
                (new ExcelReader()).read(in, new SheetHandler() {
                    public void handleRow(ParsedRow row) {
                        if (row.getRowNum() == headIndex) {
                            dataHeadList.add(row.getData());
                        } else if (row.getRowNum() > headIndex && row.getRowNum() <= dataEndIndex && row.getData().size() > 0) {
                            dataRowList.add(row.getData());
                        }

                    }
                });
            } catch (Throwable e) {
                exp = e;
                throw e;
            } finally {
                if (in != null) {
                    if (exp != null) {
                        try {
                            in.close();
                        } catch (Throwable e1) {
                            exp.addSuppressed(e1);
                        }
                    } else {
                        in.close();
                    }
                }
            }
        } catch (Throwable e2) {
            logger.error("parse excel error,", e2);
            throw new KDBizException(ExceptionUtils.getFullStackTrace(e2));
        }
    }

}
