package com.engine.salary.service.impl;

import cn.hutool.core.util.StrUtil;
import com.engine.common.util.ServiceUtil;
import com.engine.core.impl.Service;
import com.engine.salary.biz.SalaryItemBiz;
import com.engine.salary.biz.SysSalaryItemBiz;
import com.engine.salary.config.SalaryElogConfig;
import com.engine.hrmelog.entity.dto.LoggerContext;
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.param.SalaryItemSaveParam;
import com.engine.salary.entity.salaryitem.param.SalaryItemSearchParam;
import com.engine.salary.entity.salaryitem.param.SyncSalaryItemParam;
import com.engine.salary.entity.salaryitem.po.SalaryItemPO;
import com.engine.salary.entity.salaryitem.po.SysSalaryItemPO;
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.po.TaxAgentPO;
import com.engine.salary.enums.OperateTypeEnum;
import com.engine.salary.enums.SalarySystemTypeEnum;
import com.engine.salary.enums.SalaryValueTypeEnum;
import com.engine.salary.enums.sicategory.SharedTypeEnum;
import com.engine.salary.exception.SalaryRunTimeException;
import com.engine.salary.service.*;
import com.engine.salary.util.SalaryEntityUtil;
import com.engine.salary.util.SalaryI18nUtil;
import com.engine.salary.util.page.PageInfo;
import com.engine.salary.util.page.SalaryPageUtil;
import com.engine.salary.util.valid.ValidUtil;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import weaver.hrm.User;

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

/**
 * 薪资项目
 * <p>Copyright: Copyright (c) 2022</p>
 * <p>Company: 泛微软件</p>
 *
 * @author qiantao
 * @version 1.0
 **/
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();
//    @Autowired
//    private LoggerTemplate salaryItemLoggerTemplate;

    @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 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) {
        Boolean needAuth = getTaxAgentService(user).isNeedAuth((long) user.getUID());
        Boolean isAdminEnable = getTaxAgentService(user).isAdminEnable((long) user.getUID());
        List<SalaryItemPO> salaryItemPOS = salaryItemBiz.listByParam(searchParam);
        if (needAuth) {
            if (isAdminEnable) {
                //管理员
                Set<Long> userTaxAgentIds = getTaxAgentService(user).listAllTaxAgents((long) user.getUID())
                        .stream().map(TaxAgentPO::getId).collect(Collectors.toSet());
                salaryItemPOS = salaryItemPOS.stream()
                        .filter(po -> filterInRange(userTaxAgentIds, po))
                        .sorted(new Comparator<SalaryItemPO>() {
                            @Override
                            public int compare(SalaryItemPO o1, SalaryItemPO 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());
            } else {
                //普通用户
                salaryItemPOS = new ArrayList<>();
            }
        }
        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());
        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("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) {
        // 查询所有启用的薪资账套
        List<SalarySobItemPO> salarySobItemList = getSalarySobItemService(user).listBySalaryItemIds(Collections.singleton(salaryItemId));
        Set<Long> salarySobIds = SalaryEntityUtil.properties(salarySobItemList, SalarySobItemPO::getSalarySobId);
        List<SalarySobPO> salarySobs = getSalarySobService(user).listByIds(salarySobIds);
        // 获取能够管理的义务人
        Boolean isChief = getTaxAgentService(user).isChief(Long.valueOf(user.getUID()));
        if (!isChief) {
            Set<Long> taxAgentIds = SalaryEntityUtil.properties(getTaxAgentService(user).listAllTaxAgentsAsAdmin(Long.valueOf(user.getUID())), TaxAgentPO::getId);
            salarySobs = salarySobs.stream().filter(sob -> taxAgentIds.contains(sob.getTaxAgentId())).collect(Collectors.toList());
        }
        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);
            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();
    }

}
