package com.engine.salary.service.impl;

import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import com.api.formmode.mybatis.util.SqlProxyHandle;
import com.engine.common.util.ServiceUtil;
import com.engine.core.impl.Service;
import com.engine.hrmelog.entity.dto.LoggerContext;
import com.engine.salary.biz.SalaryItemBiz;
import com.engine.salary.biz.SysSalaryItemBiz;
import com.engine.salary.config.SalaryElogConfig;
import com.engine.salary.constant.SalaryDefaultTenantConstant;
import com.engine.salary.entity.salaryformula.param.SalaryFormulaSaveParam;
import com.engine.salary.entity.salaryformula.po.FormulaPO;
import com.engine.salary.entity.salaryformula.po.FormulaVar;
import com.engine.salary.entity.salaryitem.bo.SalaryItemBO;
import com.engine.salary.entity.salaryitem.config.SalaryItemAllConfig;
import com.engine.salary.entity.salaryitem.config.SalaryItemExcelConfig;
import com.engine.salary.entity.salaryitem.param.*;
import com.engine.salary.entity.salaryitem.po.SalaryItemPO;
import com.engine.salary.entity.salaryitem.po.SysSalaryItemPO;
import com.engine.salary.entity.salarysob.param.SalarySobQueryParam;
import com.engine.salary.entity.salarysob.po.SalarySobBackItemPO;
import com.engine.salary.entity.salarysob.po.SalarySobItemPO;
import com.engine.salary.entity.salarysob.po.SalarySobPO;
import com.engine.salary.entity.taxagent.param.TaxAgentQueryParam;
import com.engine.salary.entity.taxagent.po.TaxAgentPO;
import com.engine.salary.enums.*;
import com.engine.salary.enums.auth.AuthFilterTypeEnum;
import com.engine.salary.enums.salaryformula.ReferenceTypeEnum;
import com.engine.salary.enums.salaryformula.ReturnTypeEnum;
import com.engine.salary.enums.salaryitem.SalaryDataTypeEnum;
import com.engine.salary.enums.sicategory.SharedTypeEnum;
import com.engine.salary.exception.SalaryRunTimeException;
import com.engine.salary.mapper.salaryitem.SalaryItemMapper;
import com.engine.salary.service.*;
import com.engine.salary.service.auth.AuthService;
import com.engine.salary.service.auth.AuthServiceImpl;
import com.engine.salary.sys.entity.vo.UploadConfigResponse;
import com.engine.salary.util.JsonUtil;
import com.engine.salary.util.SalaryEntityUtil;
import com.engine.salary.util.SalaryI18nUtil;
import com.engine.salary.util.db.IdGenerator;
import com.engine.salary.util.excel.ExcelParseHelper;
import com.engine.salary.util.excel.ExcelSupport;
import com.engine.salary.util.excel.ExcelUtil;
import com.engine.salary.util.excel.ImportExcelResponse;
import com.engine.salary.util.page.PageInfo;
import com.engine.salary.util.page.SalaryPageUtil;
import com.engine.salary.util.valid.ValidUtil;
import com.google.common.collect.Lists;
import com.weaver.general.Util;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.util.IOUtils;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.BeanUtils;
import weaver.file.ImageFileManager;
import weaver.hrm.User;

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

import static com.engine.salary.util.excel.ExcelSupport.EXCEL_TYPE_XLSX;

/**
 * 薪资项目
 * <p>Copyright: Copyright (c) 2022</p>
 * <p>Company: 泛微软件</p>
 *
 * @author qiantao
 * @version 1.0
 **/
@Slf4j
public class SalaryItemServiceImpl extends Service implements SalaryItemService {

    private SalaryItemBiz salaryItemBiz = new SalaryItemBiz();

    private SalarySobItemService getSalarySobItemService(User user) {
        return ServiceUtil.getService(SalarySobItemServiceImpl.class, user);
    }

    private TaxAgentService getTaxAgentService(User user) {
        return ServiceUtil.getService(TaxAgentServiceImpl.class, user);
    }

    private SysSalaryItemService getSysSalaryItemService(User user) {
        return ServiceUtil.getService(SysSalaryItemServiceImpl.class, user);
    }

    private SalaryFormulaService getSalaryFormulaService(User user) {
        return ServiceUtil.getService(SalaryFormulaServiceImpl.class, user);
    }


    private SalarySobBackItemService getSalarySobBackItemService(User user) {
        return ServiceUtil.getService(SalarySobBackItemServiceImpl.class, user);
    }

    public SalarySobService getSalarySobService(User user) {
        return ServiceUtil.getService(SalarySobServiceImpl.class, user);
    }

    private SysSalaryItemBiz sysSalaryItemBiz = new SysSalaryItemBiz();

    private SalaryItemMapper getSalaryItemMapper() {
        return SqlProxyHandle.getProxy(SalaryItemMapper.class);
    }

    public AuthService getAuthService(User user) {
        return ServiceUtil.getService(AuthServiceImpl.class, user);
    }

    @Override
    public SalaryItemPO getById(Long id) {
        return salaryItemBiz.getById(id);
    }

    @Override
    public List<SalaryItemPO> listAll() {
        return salaryItemBiz.listAll();
    }

    @Override
    public List<SalaryItemPO> listByIds(Collection<Long> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            return Collections.emptyList();
        }
        return salaryItemBiz.listSome(SalaryItemPO.builder().ids(ids).build());
    }

    @Override
    public List<SalaryItemPO> listBySysSalaryItemIds(Collection<Long> sysSalaryItemIds) {
        if (CollectionUtils.isEmpty(sysSalaryItemIds)) {
            return Collections.emptyList();
        }
        return salaryItemBiz.listSome(SalaryItemPO.builder().sysSalaryItemIds(sysSalaryItemIds).build());
    }

    @Override
    public List<SalaryItemPO> listByName(String name) {
        return salaryItemBiz.listSome(SalaryItemPO.builder().name(name).build());
    }

    @Override
    public SalaryItemPO getByName(String name) {
        return getSalaryItemMapper().getByName(name);
    }


    @Override
    public List<SalaryItemPO> listBySystemType(SalarySystemTypeEnum systemType) {
        return salaryItemBiz.listSome(SalaryItemPO.builder().systemType(systemType.getValue()).build());
    }

    @Override
    public List<SalaryItemPO> listBySystemTypeAndUseDefault(SalarySystemTypeEnum systemType, Integer useDefault) {
        return salaryItemBiz.listSome(SalaryItemPO.builder().systemType(systemType.getValue()).useDefault(useDefault).build());
    }

    @Override
    public List<SalaryItemPO> listByParam(SalaryItemSearchParam searchParam) {
        return salaryItemBiz.listByParam(searchParam);
    }

    @Override
    public List<SalaryItemPO> listByParamOrderById(SalaryItemSearchParam searchParam) {
        return salaryItemBiz.listByParamOrderById(searchParam);
    }

    @Override
    public PageInfo<SalaryItemPO> listPageByParam(SalaryItemSearchParam searchParam) {
        List<SalaryItemPO> salaryItemPOS = salaryItemBiz.listByParam(searchParam);

        TaxAgentQueryParam param = TaxAgentQueryParam.builder().build();
        param.setFilterType(AuthFilterTypeEnum.ADMIN_DATA);
        List<TaxAgentPO> taxAgentPOS = getTaxAgentService(user).listAuth(param);
        Set<Long> userTaxAgentIds = SalaryEntityUtil.properties(taxAgentPOS,TaxAgentPO::getId);
        salaryItemPOS = salaryItemPOS.stream()
                .filter(po -> filterInRange(userTaxAgentIds, po))
                .sorted((o1, o2) -> {
                    if (o1.getSortedIndex() == null && o2.getSortedIndex() == null) {
                        Integer systemType1 = o1.getSystemType() == null ? 0 : o1.getSystemType();
                        Integer systemType2 = o2.getSystemType() == null ? 0 : o2.getSystemType();
                        return systemType1.compareTo(systemType2);
                    } else {
                        Integer sortedIndex1 = o1.getSortedIndex() == null ? 0 : o1.getSortedIndex();
                        Integer sortedIndex2 = o2.getSortedIndex() == null ? 0 : o2.getSortedIndex();
                        return sortedIndex2.compareTo(sortedIndex1);
                    }
                })
                .collect(Collectors.toList());
        return SalaryPageUtil.buildPage(searchParam.getCurrent(), searchParam.getPageSize(), salaryItemPOS);
    }


    @Override
    public boolean filterInRange(Set<Long> userTaxAgentIds, SalaryItemPO po) {
        return null == po.getSharedType()
                || SharedTypeEnum.PUBLIC.getValue().equals(po.getSharedType().toString())
                || (StrUtil.isNotBlank(po.getTaxAgentIds())
                && Arrays.stream(po.getTaxAgentIds().split(","))
                .map(Long::valueOf)
                .anyMatch(userTaxAgentIds::contains));
    }

//    @Override
//    public Page<SalaryItemPO> listPageByParam(SalaryItemSearchParam searchParam, String tenantKey) {
//        // 分页参数
//        Page<SalaryItemPO> page = SalaryPageUtil.buildPage(searchParam.getCurrent(), searchParam.getPageSize());
//        // 构建查询参数
//        Wrapper<SalaryItemPO> queryWrapper = SalaryItemBO.buildQueryWrapper(searchParam, tenantKey);
//        // 返回查询结果
//        return SalaryItemBiz.selectPage(page, queryWrapper);
//    }

    @Override
    public SalaryItemPO save(SalaryItemSaveParam saveParam) {
        // 名称不能和已有的自定义薪资项目重名
        List<SalaryItemPO> salaryItemPOS = listByName(saveParam.getName());
        if (CollectionUtils.isNotEmpty(salaryItemPOS)) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(98326, "薪资项目名称已存在，请重新命名"));
        }
        // 名称不能和已有的系统薪资项目重名
        SysSalaryItemPO sysSalaryItem = sysSalaryItemBiz.selectOneByName(saveParam.getName());
        if (sysSalaryItem != null) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(98328, "自定义薪资项目的名称不能和系统薪资项目的名称重名"));
        }
        SalaryItemPO salaryItemPO = SalaryItemBO.convert2SalaryItemPO(saveParam, (long) user.getUID());
        salaryItemBiz.insert(salaryItemPO);
        return salaryItemPO;
    }

    @Override
    public void batchSave(Collection<SalaryItemPO> salaryItemPOS) {
        salaryItemBiz.batchSave(salaryItemPOS);
    }

    @Override
    public SalaryItemPO update(SalaryItemSaveParam saveParam) {
        // 查询薪资项目，判断薪资项目是否存在
        SalaryItemPO salaryItemPO = getById(saveParam.getId());
        if (Objects.isNull(salaryItemPO)) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(98299, "参数错误，薪资项目不存在或已被删除"));
        }
        // 名称不能和已有的自定义薪资项目重名
        List<SalaryItemPO> salaryItemPOS = listByName(saveParam.getName());
        boolean nameExist = salaryItemPOS.stream().anyMatch(e -> !Objects.equals(salaryItemPO.getId(), e.getId()));
        if (nameExist) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(98326, "薪资项目名称已存在，请重新命名"));
        }
        // 名称不能和已有的系统薪资项目重名
        List<SysSalaryItemPO> sysSalaryItemPOS = sysSalaryItemBiz.listSome(SysSalaryItemPO.builder().name(saveParam.getName()).build());
        boolean sysNameExist = sysSalaryItemPOS.stream().anyMatch(e -> !Objects.equals(salaryItemPO.getSysSalaryItemId(), e.getId()));
        if (sysNameExist) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(98328, "自定义薪资项目的名称不能和系统薪资项目的名称重名"));
        }
        // 更新薪资项目
        SalaryItemPO newSalaryItemPO = new SalaryItemPO();
        BeanUtils.copyProperties(salaryItemPO, newSalaryItemPO);
        newSalaryItemPO.setName(saveParam.getName());
        newSalaryItemPO.setUseDefault(saveParam.getUseDefault());
        newSalaryItemPO.setUseInEmployeeSalary(saveParam.getUseInEmployeeSalary());
        newSalaryItemPO.setHideDefault(saveParam.getHideDefault());
        newSalaryItemPO.setRoundingMode(saveParam.getRoundingMode());
        newSalaryItemPO.setPattern(saveParam.getPattern());
        newSalaryItemPO.setValueType(saveParam.getValueType());
        newSalaryItemPO.setDataType(saveParam.getDataType());
        newSalaryItemPO.setFormulaId(Objects.equals(saveParam.getValueType(), SalaryValueTypeEnum.INPUT.getValue()) ? 0L : saveParam.getFormulaId());
        newSalaryItemPO.setDescription(saveParam.getDescription());
        newSalaryItemPO.setUpdateTime(new Date());
        newSalaryItemPO.setSharedType(saveParam.getSharedType());
        newSalaryItemPO.setTaxAgentIds(saveParam.getTaxAgentIds());
        newSalaryItemPO.setSortedIndex(saveParam.getSortedIndex());
        newSalaryItemPO.setWidth(saveParam.getWidth());
        newSalaryItemPO.setDefaultValue(saveParam.getDefaultValue());
        salaryItemBiz.updateById(newSalaryItemPO);

        //改名后更新公式
        String oldName = salaryItemPO.getName();
        String newName = saveParam.getName();
        if (!StringUtils.equals(oldName, newName)) {

            //薪资项目

            if (newSalaryItemPO.getUseInEmployeeSalary() == 0) {
                String itemPrefix = "salaryItem_";
                String fieldNamePrefix = "{薪资项目.%s}";
                changeName(salaryItemPO, oldName, newName, itemPrefix, fieldNamePrefix);
            }

            if (newSalaryItemPO.getUseInEmployeeSalary() == 1) {
                //引用档案
                String itemPrefix = "salaryArchives_";
                String fieldNamePrefix = "{薪资档案.%s}";
                changeName(salaryItemPO, oldName, newName, itemPrefix, fieldNamePrefix);

                //引用项目
                itemPrefix = "salaryItem_";
                fieldNamePrefix = "{薪资项目.%s}";
                changeName(salaryItemPO, oldName, newName, itemPrefix, fieldNamePrefix);
            }
        }

        return salaryItemPO;
    }

    private void changeName(SalaryItemPO salaryItemPO, String oldName, String newName, String itemPrefix, String fieldNamePrefix) {
        String code = itemPrefix + salaryItemPO.getCode();
        Date now = new Date();
        List<FormulaVar> formulaVars = getSalaryFormulaService(user).listByCode(code);
        formulaVars.forEach(v -> {
            FormulaVar formulaVar = FormulaVar.builder()
                    .id(v.getId())
                    .name(newName)
                    .fieldName(String.format(fieldNamePrefix, newName))
                    .updateTime(now)
                    .build();
            getSalaryFormulaService(user).updateVar(formulaVar);
        });

        List<Long> formulaIds = SalaryEntityUtil.properties(formulaVars, FormulaVar::getFormulaId, Collectors.toList());
        List<FormulaPO> formulaPOS = getSalaryFormulaService(user).listByIds(formulaIds);
        formulaPOS.forEach(f -> {
            String formula = f.getFormula();
            formula = formula.replace(String.format(fieldNamePrefix, oldName), String.format(fieldNamePrefix, newName));
            FormulaPO formulaPO = FormulaPO.builder()
                    .id(f.getId())
                    .formula(formula)
                    .updateTime(now)
                    .build();
            getSalaryFormulaService(user).update(formulaPO);
        });
    }

    @Override
    public void deleteByIds(Collection<Long> ids) {
        SalaryItemServiceImpl.UsingItem usingItem = getUsingItem();
        List<Long> usingItemIds = usingItem.getUsingItemIds();
        List<String> usingCodes = usingItem.getUsingCodes();

        // 查询薪资项目
        List<SalaryItemPO> salaryItemPOS = listByIds(ids);
        if (CollectionUtils.isEmpty(salaryItemPOS)) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(98299, "参数错误，薪资项目不存在或已被删除"));
        }
        if (CollectionUtils.containsAny(usingItemIds, ids)) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(98322, "薪资账套正在使用该薪资项目，不允许删除"));
        }
        List<String> codes = SalaryEntityUtil.properties(salaryItemPOS, SalaryItemPO::getCode, Collectors.toList());
        if (CollectionUtils.containsAny(usingCodes, codes)) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(98322, "公式正在使用该薪资项目，不允许删除"));
        }


        // 删除薪资项目
        ids = SalaryEntityUtil.properties(salaryItemPOS, SalaryItemPO::getId);
        salaryItemBiz.deleteByIds(ids);
        Integer useInEmployeeSalary = salaryItemPOS.get(0).getUseInEmployeeSalary();
        // 记录删除日志
        if (useInEmployeeSalary == 0) {
            // 薪资项目
            salaryItemPOS.forEach(salaryItemPO -> {
                LoggerContext<SalaryItemPO> loggerContext = new LoggerContext<>();
                loggerContext.setUser(user);
                loggerContext.setTargetId(String.valueOf(salaryItemPO.getId()));
                loggerContext.setTargetName(salaryItemPO.getName());
                loggerContext.setOperateType(OperateTypeEnum.DELETE.getValue());
                loggerContext.setOperateTypeName(SalaryI18nUtil.getI18nLabel(0, "删除薪资项目"));
                loggerContext.setOperatedesc(SalaryI18nUtil.getI18nLabel(0, "删除薪资项目") + ": " + salaryItemPO.getName());
                loggerContext.setOldValues(salaryItemPO);
                SalaryElogConfig.salaryItemLoggerTemplate.write(loggerContext);
            });
        } else {
            // 字段管理
            salaryItemPOS.forEach(salaryItemPO -> {
                LoggerContext<SalaryItemPO> loggerContext = new LoggerContext<>();
                loggerContext.setUser(user);
                loggerContext.setTargetId(String.valueOf(salaryItemPO.getId()));
                loggerContext.setTargetName(salaryItemPO.getName());
                loggerContext.setOperateType(OperateTypeEnum.DELETE.getValue());
                loggerContext.setOperateTypeName(SalaryI18nUtil.getI18nLabel(0, "删除字段"));
                loggerContext.setOperatedesc(SalaryI18nUtil.getI18nLabel(0, "删除字段") + ": " + salaryItemPO.getName());
                loggerContext.setOldValues(salaryItemPO);
                SalaryElogConfig.salaryArchiveFieldLoggerTemplate.write(loggerContext);
            });
        }

    }


    /**
     * 获取使用中的项目id、项目编码、公式id
     *
     * @return 项目id、项目编码、公式id
     */
    @Override
    public UsingItem getUsingItem() {

        //系统项目
        List<SysSalaryItemPO> sysItems = getSysSalaryItemService(user).listAll();
        List<Long> sysItemFormulaIds = SalaryEntityUtil.properties(sysItems, SysSalaryItemPO::getFormulaId, Collectors.toList());

        //薪资项目
        List<SalaryItemPO> items = listAll();
        List<Long> itemFormulaIds = SalaryEntityUtil.properties(items, SalaryItemPO::getFormulaId, Collectors.toList());

        //账套项目
        List<SalarySobItemPO> sobItems = getSalarySobItemService(user).list();
        List<Long> sobItemFormulaIds = SalaryEntityUtil.properties(sobItems, SalarySobItemPO::getFormulaId, Collectors.toList());

        //回算项目
        List<SalarySobBackItemPO> backItems = getSalarySobBackItemService(user).listAll();
        List<Long> backItemFormulaIds = SalaryEntityUtil.properties(backItems, SalarySobBackItemPO::getFormulaId, Collectors.toList());

        List<Long> usingFormulaIds = new ArrayList<>();
        usingFormulaIds.addAll(sysItemFormulaIds);
        usingFormulaIds.addAll(itemFormulaIds);
        usingFormulaIds.addAll(sobItemFormulaIds);
        usingFormulaIds.addAll(backItemFormulaIds);


        //账套里的项目，项目里的项目
        List<Long> sobItemIds = SalaryEntityUtil.properties(sobItems, SalarySobItemPO::getSalaryItemId, Collectors.toList());
        List<Long> backItemIds = SalaryEntityUtil.properties(backItems, SalarySobBackItemPO::getSalaryItemId, Collectors.toList());
        List<Long> usingItemIds = new ArrayList<>();
        usingItemIds.addAll(sobItemIds);
        usingItemIds.addAll(backItemIds);

        //使用中的code
        List<FormulaVar> formulaVars = getSalaryFormulaService(user).listVarByFormulaIds(usingFormulaIds);
        List<String> itemCode = formulaVars.stream()
                .map(FormulaVar::getFieldId)
                .map(v -> v.replaceAll("variableItem_", ""))
                .map(v -> v.replaceAll("salaryItem_", ""))
                .map(v -> v.replaceAll("salaryArchives_", ""))
                .map(v -> v.replaceAll("ISSUED_", ""))
                .collect(Collectors.toList());

        return UsingItem.builder().usingFormulaIds(usingFormulaIds).usingItemIds(usingItemIds).usingCodes(itemCode).build();
    }

    @Override
    public void batchUpdateSortedIndex(List<SalaryItemPO> values) {
        salaryItemBiz.batchUpdateSortedIndex(values);
    }

    @Override
    public List<Map<String, Object>> getSalarySobBySalaryItem(Long salaryItemId) {
        // 查询所有管理的薪资账套
        SalarySobQueryParam salarySobQueryParam = new SalarySobQueryParam();
        salarySobQueryParam.setFilterType(AuthFilterTypeEnum.ADMIN_DATA);
        List<SalarySobPO> salarySobs = getSalarySobService(user).listAuth(salarySobQueryParam);
        return salarySobs.stream().map(m -> {
            Map<String, Object> map = new HashMap<>();
            map.put("id", String.valueOf(m.getId()));
            map.put("content", m.getName());
            return map;
        }).collect(Collectors.toList());
    }

    @Override
    public void syncSalaryItemToSalarySobItem(SyncSalaryItemParam param) {
        ValidUtil.doValidator(param);
        // 获取薪资项目信息
        SalaryItemPO salaryItemPO = salaryItemBiz.getById(param.getSalaryItemId());
        if (salaryItemPO == null) {
            throw new SalaryRunTimeException("薪资项目不存在或已被删除");
        }
        // 获取薪资账套中薪资项目信息
        List<SalarySobItemPO> salarySobItemPOS = getSalarySobItemService(user).listBySalaryItemIds(Collections.singleton(param.getSalaryItemId()));
        salarySobItemPOS = salarySobItemPOS.stream().filter(po -> param.getSalarySobIds().contains(po.getSalarySobId())).collect(Collectors.toList());
        // 更新薪资账套中的薪资项目信息
        Date now = new Date();
        for (SalarySobItemPO sobItem : salarySobItemPOS) {
            sobItem.setFormulaId(salaryItemPO.getFormulaId());
            sobItem.setRoundingMode(salaryItemPO.getRoundingMode());
            sobItem.setPattern(salaryItemPO.getPattern());
            sobItem.setValueType(salaryItemPO.getValueType());
            sobItem.setDescription(salaryItemPO.getDescription());
            sobItem.setUpdateTime(now);
            sobItem.setDefaultValue(salaryItemPO.getDefaultValue() == null ? "" : salaryItemPO.getDefaultValue());
            getSalarySobItemService(user).update(sobItem);
        }
    }


    @Data
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    public static class UsingItem {
        private List<Long> usingFormulaIds;

        private List<Long> usingItemIds;

        private List<String> usingCodes;

    }


    public UsingItem getUsingItem(Long id) {

        //薪资项目
        SalaryItemPO items = getById(id);

        //账套项目
        List<SalarySobItemPO> sobItems = getSalarySobItemService(user).listBySalaryItemIds(Collections.singleton(id));

        //回算项目
        List<SalarySobBackItemPO> backItems = getSalarySobBackItemService(user).listBySalaryItemIds(Collections.singleton(id));


//        //账套里的项目，项目里的项目
//        List<Long> sobItemIds = SalaryEntityUtil.properties(sobItems, SalarySobItemPO::getSalaryItemId, Collectors.toList());
//        List<Long> backItemIds = SalaryEntityUtil.properties(backItems, SalarySobBackItemPO::getSalaryItemId, Collectors.toList());
//        List<Long> usingItemIds = new ArrayList<>();
//        usingItemIds.addAll(sobItemIds);
//        usingItemIds.addAll(backItemIds);
//
//        //使用中的code
//        List<FormulaVar> formulaVars = getSalaryFormulaService(user).listVarByFormulaIds(usingFormulaIds);
//        List<String> itemCode = formulaVars.stream().map(FormulaVar::getFieldId).map(v -> v.replaceAll("salaryItem_", "")).collect(Collectors.toList());

        return UsingItem.builder().build();
    }


    @Override
    public List<SalaryItemExcelConfig> getConfig(SalaryItemExportParam param) {
        return getSalaryItemMapper().getConfig(param.getIds());
    }


    @Override
    public Map<String, Object> preview(SalaryItemImportParam param) {
        InputStream fileInputStream = null;
        try {
            fileInputStream = ImageFileManager.getInputStreamById(Integer.parseInt(param.getImageId()));
            Sheet sheet = ExcelSupport.parseFile(fileInputStream, 0, EXCEL_TYPE_XLSX);
            Map<String, Object> apidatas = new HashMap<String, Object>();
            apidatas.put("headers", ExcelSupport.getSheetHeader(sheet, 0));
            apidatas.put("list", ExcelParseHelper.parse2List(sheet, 1));
            return apidatas;
        } finally {
            IOUtils.closeQuietly(fileInputStream);
        }
    }

    @Override
    public ImportExcelResponse importSalaryItem(SalaryItemImportParam param) {

        Date now = new Date();
        InputStream fileInputStream = null;
        try {
            fileInputStream = ImageFileManager.getInputStreamById(Integer.parseInt(param.getImageId()));
            List<SalaryItemExcelConfig> configs = ExcelParseHelper.parse2Map(fileInputStream, SalaryItemExcelConfig.class, 0, 1, 13, EXCEL_TYPE_XLSX);

            // 错误excel内容
            ImportExcelResponse response = ImportExcelResponse.builder().totalCount(configs.size()).successCount(configs.size()).errorCount(0).errorData(new ArrayList<>()).build();

            List<SysSalaryItemPO> sysSalaryItemPOS = sysSalaryItemBiz.listAll();
            Set<String> sysItems = SalaryEntityUtil.properties(sysSalaryItemPOS, SysSalaryItemPO::getName);

            List<SalaryItemPO> adds = new ArrayList<>();
            List<SalaryItemPO> updates = new ArrayList<>();
            Map<Long, Map<String, String>> formulaItems = new HashMap<>();
            for (int i = 0; i < configs.size(); i++) {
                String rowIndex = String.format("第%s行", i + 2);
                SalaryItemExcelConfig config = configs.get(i);
                String name = config.getName();
                SalaryValueTypeEnum salaryValueTypeEnum = SalaryValueTypeEnum.parseByDefaultLabel(config.getValueType());
                SalaryDataTypeEnum salaryDataTypeEnum = SalaryDataTypeEnum.parseByDefaultLabel(config.getDataType());
                SalaryItemPO itemPO = getByName(name);
                if (itemPO == null) {

                    if (sysItems.contains(name)) {
                        ImportExcelResponse.Error error = ImportExcelResponse.Error.builder().message(String.format("%s“%s”的名称与系统名称冲突", rowIndex, name)).build();
                        response.setSuccessCount(response.getSuccessCount() - 1);
                        response.setErrorCount(response.getErrorCount() + 1);
                        response.getErrorData().add(error);
                        continue;
                    }

                    itemPO = SalaryItemPO.builder()
                            .id(IdGenerator.generate())
                            .code(IdGenerator.getUUID())
                            .name(name)
                            .systemType(SalarySystemTypeEnum.CUSTOM.getValue())
                            .sysSalaryItemId(org.apache.commons.lang3.math.NumberUtils.LONG_ZERO)
                            .useDefault(SalaryOnOffEnum.parseByDefaultLabel(config.getUseDefault()).getValue())
                            .useInEmployeeSalary(SalaryOnOffEnum.OFF.getValue())
                            .hideDefault(SalaryOnOffEnum.parseByDefaultLabel(config.getHideDefault()).getValue())
                            .roundingMode(SalaryRoundingModeEnum.parseByDefaultLabel(config.getRoundingMode()).getValue())
                            .pattern(NumberUtil.isNumber(config.getPattern()) ? Integer.parseInt(config.getPattern()) : 2)
                            .valueType(salaryValueTypeEnum.getValue())
                            .dataType(salaryDataTypeEnum.getValue())
                            //后补
                            .formulaId(0L)
                            .description(config.getDescription())
                            .canEdit(org.apache.commons.lang3.math.NumberUtils.INTEGER_ONE)
                            .creator((long) user.getUID())
                            .deleteType(0)
                            .createTime(now)
                            .updateTime(now)
                            .tenantKey(SalaryDefaultTenantConstant.DEFAULT_TENANT_KEY)
                            .sharedType(0)
                            .taxAgentIds("")
                            .sortedIndex(NumberUtil.isNumber(config.getSortedIndex()) ? Integer.parseInt(config.getSortedIndex()) : null)
                            .width(NumberUtil.isNumber(config.getWidth()) ? Integer.parseInt(config.getWidth()) : null)
                            .defaultValue(config.getDefaultValue())
                            .build();
                    adds.add(itemPO);
                } else {

                    if (SalarySystemTypeEnum.parseByValue(itemPO.getSystemType()) == SalarySystemTypeEnum.SYSTEM) {
                        ImportExcelResponse.Error error = ImportExcelResponse.Error.builder().message(String.format("%s“%s”是系统薪资项目不支持修改", rowIndex, name)).build();
                        response.setSuccessCount(response.getSuccessCount() - 1);
                        response.setErrorCount(response.getErrorCount() + 1);
                        response.getErrorData().add(error);
                        continue;
                    }

                    itemPO.setUseDefault(SalaryOnOffEnum.parseByDefaultLabel(config.getUseDefault()).getValue());
                    itemPO.setHideDefault(SalaryOnOffEnum.parseByDefaultLabel(config.getHideDefault()).getValue());
                    itemPO.setRoundingMode(SalaryRoundingModeEnum.parseByDefaultLabel(config.getRoundingMode()).getValue());
                    itemPO.setPattern(NumberUtil.isNumber(config.getPattern()) ? Integer.parseInt(config.getPattern()) : 2);
                    itemPO.setValueType(salaryValueTypeEnum.getValue());
                    itemPO.setDataType(salaryDataTypeEnum.getValue());
                    //后补
                    itemPO.setFormulaId(0L);
                    itemPO.setDescription(config.getDescription());
                    itemPO.setUpdateTime(now);
                    itemPO.setSortedIndex(NumberUtil.isNumber(config.getSortedIndex()) ? Integer.parseInt(config.getSortedIndex()) : null);
                    itemPO.setWidth(NumberUtil.isNumber(config.getWidth()) ? Integer.parseInt(config.getWidth()) : null);
                    itemPO.setDefaultValue(config.getDefaultValue());
                    updates.add(itemPO);
                }

                //解析公式
                if (salaryValueTypeEnum == SalaryValueTypeEnum.FORMULA || salaryValueTypeEnum == SalaryValueTypeEnum.SQL) {
                    Long id = itemPO.getId();
                    String formula = config.getFormula();
                    String sqlReturnKey = config.getExtendParam();
                    Map<String, String> map = new HashMap<>();
                    map.put("rowIndex", rowIndex);
                    map.put("formula", formula);
                    map.put("sqlReturnKey", sqlReturnKey);
                    formulaItems.put(id, map);
                }
            }

            adds.forEach(getSalaryItemMapper()::insertIgnoreNull);
            updates.forEach(getSalaryItemMapper()::updateIgnoreNull);

            for (Long id : formulaItems.keySet()) {
                SalaryItemPO itemPO = getById(id);
                Map<String, String> map = formulaItems.get(id);
                String rowIndex = map.get("rowIndex");
                String formula = map.get("formula");
                String sqlReturnKey = map.get("sqlReturnKey");
                SalaryValueTypeEnum salaryValueTypeEnum = SalaryValueTypeEnum.parseByValue(itemPO.getValueType());
                SalaryDataTypeEnum salaryDataTypeEnum = SalaryDataTypeEnum.parseByValue(itemPO.getDataType());
                SalaryFormulaSaveParam saveParam = SalaryFormulaSaveParam.builder()
                        .name(itemPO.getName())
                        .module("salary")
                        .useFor("salaryitem")
                        .referenceType(salaryValueTypeEnum == SalaryValueTypeEnum.FORMULA ? ReferenceTypeEnum.FORMULA.getValue() : ReferenceTypeEnum.SQL.getValue())
                        .returnType(salaryDataTypeEnum == SalaryDataTypeEnum.STRING ? ReturnTypeEnum.STRING.getValue() : ReturnTypeEnum.NUMBER.getValue())
                        .validateType(salaryDataTypeEnum == SalaryDataTypeEnum.STRING ? ReturnTypeEnum.STRING.getValue() : ReturnTypeEnum.NUMBER.getValue())
                        .extendParam("{\"isCustomFunction\":\"0\",\"sqlReturnKey\":\"" + sqlReturnKey + "\",\"openDecrypt\":\"0\",\"datasource\":{\"datasourceId\":\"\"}}")
                        .formula(formula)
                        .build();

                try {
                    FormulaPO formulaPO = getSalaryFormulaService(user).save(saveParam);
                    itemPO.setFormulaId(formulaPO.getId());
                    getSalaryItemMapper().updateIgnoreNull(itemPO);
                } catch (Exception e) {
                    ImportExcelResponse.Error error = ImportExcelResponse.Error.builder().message(String.format("%s“%s”的公式设置有误：%s", rowIndex, itemPO.getName(), e.getMessage())).build();
                    response.setSuccessCount(response.getSuccessCount() - 1);
                    response.setErrorCount(response.getErrorCount() + 1);
                    response.getErrorData().add(error);
                }
            }
            return response;
        } finally {
            IOUtils.closeQuietly(fileInputStream);
        }
    }

    @Override
    public XSSFWorkbook export(SalaryItemExportParam param) {

        List<Object> titles = Lists.newArrayList();
        List<String> dataIndexList = Lists.newArrayList();
        ExcelUtil.parseHeader(SalaryItemExcelConfig.class, titles, dataIndexList);

        List<SalaryItemExcelConfig> configs = getConfig(param);

        List<List<Object>> rows = new ArrayList<>();
        rows.add(titles);
        configs.forEach(e -> {
            List<Object> row = new ArrayList<>();
            row.add(e.getName());
            row.add(e.getUseDefault() == null ? "否" : SalaryOnOffEnum.parseByValue(Integer.parseInt(e.getUseDefault())).getDefaultLabel());
            row.add(e.getHideDefault() == null ? "否" : SalaryOnOffEnum.parseByValue(Integer.parseInt(e.getHideDefault())).getDefaultLabel());
            row.add(SalaryDataTypeEnum.parseByValue(Util.null2String(e.getDataType())).getDefaultLabel());
            row.add(e.getRoundingMode() == null ? "四舍五入" : SalaryRoundingModeEnum.parseByValue(Integer.parseInt(e.getRoundingMode())).getDefaultLabel());
            row.add(e.getPattern() == null ? "2" : e.getPattern());
            row.add(e.getValueType() == null ? "输入" : SalaryValueTypeEnum.parseByValue(Integer.parseInt(e.getValueType())).getDefaultLabel());
            row.add(e.getFormula() == null ? "" : e.getFormula());
            String sqlReturnKey = "";
            if (e.getExtendParam() != null) {
                sqlReturnKey = JsonUtil.parseMap(Util.null2String(e.getExtendParam()), String.class).getOrDefault("sqlReturnKey", "");
                if ("null".equals(sqlReturnKey)) {
                    sqlReturnKey = "";
                }
            }
            row.add(sqlReturnKey);
            row.add(e.getSortedIndex() == null ? "" : e.getSortedIndex());
            row.add(e.getWidth() == null ? "" : e.getWidth());
            row.add(e.getDefaultValue() == null ? "" : e.getDefaultValue());
            row.add(e.getDescription() == null ? "" : e.getDescription());
            rows.add(row);
        });

        return ExcelUtil.genWorkbookV2(rows, "薪资项目");
    }

    @Override
    public XSSFWorkbook downloadTemplate(SalaryItemDownloadTemplateParam param) {
        List<Object> titles = Lists.newArrayList();
        List<String> dataIndexList = Lists.newArrayList();
        ExcelUtil.parseHeader(SalaryItemExcelConfig.class, titles, dataIndexList);


        List<List<Object>> rows = new ArrayList<>();
        rows.add(titles);
        if (param.isHasData()) {
            List<SalaryItemExcelConfig> configs = getConfig(SalaryItemExportParam.builder().build());
            configs.forEach(e -> {
                List<Object> row = new ArrayList<>();
                row.add(e.getName());
                row.add(e.getUseDefault() == null ? "否" : SalaryOnOffEnum.parseByValue(Integer.parseInt(e.getUseDefault())).getDefaultLabel());
                row.add(e.getHideDefault() == null ? "否" : SalaryOnOffEnum.parseByValue(Integer.parseInt(e.getHideDefault())).getDefaultLabel());
                row.add(SalaryDataTypeEnum.parseByValue(Util.null2String(e.getDataType())).getDefaultLabel());
                row.add(e.getRoundingMode() == null ? "四舍五入" : SalaryRoundingModeEnum.parseByValue(Integer.parseInt(e.getRoundingMode())).getDefaultLabel());
                row.add(e.getPattern() == null ? "2" : e.getPattern());
                row.add(e.getValueType() == null ? "输入" : SalaryValueTypeEnum.parseByValue(Integer.parseInt(e.getValueType())).getDefaultLabel());
                row.add(e.getFormula() == null ? "" : e.getFormula());
                String sqlReturnKey = "";
                if (e.getExtendParam() != null) {
                    sqlReturnKey = JsonUtil.parseMap(Util.null2String(e.getExtendParam()), String.class).getOrDefault("sqlReturnKey", "");
                    if ("null".equals(sqlReturnKey)) {
                        sqlReturnKey = "";
                    }
                }
                row.add(sqlReturnKey);
                row.add(e.getSortedIndex() == null ? "" : e.getSortedIndex());
                row.add(e.getWidth() == null ? "" : e.getWidth());
                row.add(e.getDefaultValue() == null ? "" : e.getDefaultValue());
                row.add(e.getDescription() == null ? "" : e.getDescription());
                rows.add(row);
            });
        }
        return ExcelUtil.genWorkbookV2(rows, "薪资项目");
    }

    @Override
    public SalaryItemAllConfig getConfig() {
        List<SalaryItemPO> salaryItemPOList = listByParam(SalaryItemSearchParam.builder().useInEmployeeSalary(0).build());
        List<FormulaPO> formulaPOS = getSalaryFormulaService(user).listAll();
        Map<Long, FormulaPO> formulaMap = SalaryEntityUtil.convert2Map(formulaPOS, FormulaPO::getId);
        List<FormulaVar> formulaVars = getSalaryFormulaService(user).listAllVar();
        Map<Long, List<FormulaVar>> varListMap = SalaryEntityUtil.group2Map(formulaVars, FormulaVar::getFormulaId);

        salaryItemPOList.forEach(itemPO -> {
            if (itemPO.getFormulaId() != null) {
                FormulaPO formulaPO = formulaMap.get(itemPO.getFormulaId());
                if (formulaPO != null) {
                    formulaPO.setFormulaVars(varListMap.get(itemPO.getFormulaId()));
                    itemPO.setFormula(formulaPO);
                }
            }
        });

        return SalaryItemAllConfig.builder().salaryItems(salaryItemPOList).build();
    }

    @Override
    public UploadConfigResponse.Result parseConfig(SalaryItemAllConfig salaryItemConfig) {
        UploadConfigResponse.Result result = UploadConfigResponse.Result.builder()
                .message("薪资项目加载完毕")
                .success(new ArrayList<>())
                .warning(new ArrayList<>())
                .error(new ArrayList<>())
                .build();
        List<SalaryItemPO> salaryItemPOList = listAll();
        Map<String, SalaryItemPO> nameMap = SalaryEntityUtil.convert2Map(salaryItemPOList, SalaryItemPO::getName);
        Map<String, SalaryItemPO> codeMap = SalaryEntityUtil.convert2Map(salaryItemPOList, SalaryItemPO::getCode);

        Optional.ofNullable(salaryItemConfig.getSalaryItems())
                .orElse(new ArrayList<>())
                .forEach(itemPO -> {
                    try {
                        add(itemPO, nameMap, codeMap, result);
                    } catch (Exception e) {
                        log.error("{}项目加载异常，", itemPO.getName(), e);
                        result.getError().add(String.format("错误，薪资项目[%s]加载异常，%s", itemPO.getName(), e.getMessage()));
                    }
                });
        return result;
    }

    @Override
    public SalaryItemPO getItem(SalaryItemPO salaryItemPO) {
        SalaryItemPO po = getByName(salaryItemPO.getName());
        if (po == null) {
            po = getSalaryItemMapper().getByCode(salaryItemPO.getCode());
        }
        return po;
    }


    private Long add(SalaryItemPO itemPO, Map<String, SalaryItemPO> nameMap, Map<String, SalaryItemPO> codeMap, UploadConfigResponse.Result result) {
        long uid = user.getUID();
        Long formulaId = getSalaryFormulaService(user).add(itemPO.getFormula());
        itemPO.setFormulaId(formulaId);

        if (nameMap.containsKey(itemPO.getName())) {
            SalaryItemPO salaryItemPO = nameMap.get(itemPO.getName());
            if (!StrUtil.equals(salaryItemPO.getCode(), itemPO.getCode())) {
                result.getWarning().add(String.format("警告，薪资项目[%s]名称已存在,但项目code不一致,迁入code:%s,当前系统code：%s", itemPO.getName(), itemPO.getCode(), salaryItemPO.getCode()));
            }
            itemPO.setId(salaryItemPO.getId());
            //不许修改code
            itemPO.setCode(null);
            itemPO.setCreator(uid);
            getSalaryItemMapper().updateIgnoreNull(itemPO);
        } else if (codeMap.containsKey(itemPO.getCode())) {
            SalaryItemPO salaryItemPO = codeMap.get(itemPO.getCode());
            if (!StrUtil.equals(salaryItemPO.getName(), itemPO.getName())) {
                result.getWarning().add(String.format("警告，薪资项目[%s]code已存在,但项目code不一致,迁入名称:%s,当前系统名称：%s", itemPO.getName(), itemPO.getName(), salaryItemPO.getName()));
            }
            itemPO.setId(salaryItemPO.getId());
            //不许修改名字
            itemPO.setName(null);
            itemPO.setCreator(uid);
            getSalaryItemMapper().updateIgnoreNull(itemPO);
        } else {
            itemPO.setId(IdGenerator.generate());
            getSalaryItemMapper().insertIgnoreNull(itemPO);
        }
        result.getSuccess().add(String.format("成功，薪资项目[%s]加载成功", itemPO.getName()));

        return itemPO.getId();
    }
}
