package com.engine.salary.wrapper;

import com.engine.common.util.ServiceUtil;
import com.engine.core.impl.Service;
import com.engine.salary.cache.SalaryCacheKey;
import com.engine.salary.entity.datacollection.DataCollectionEmployee;
import com.engine.salary.entity.salaryBill.dto.SalarySendCheckDTO;
import com.engine.salary.entity.salaryacct.bo.SalaryAcctConfig;
import com.engine.salary.entity.salaryacct.bo.SalaryAcctRecordBO;
import com.engine.salary.entity.salaryacct.dto.SalaryAcctEmployeeCountDTO;
import com.engine.salary.entity.salaryacct.dto.SalaryAcctRecordFormDTO;
import com.engine.salary.entity.salaryacct.dto.SalaryAcctRecordListDTO;
import com.engine.salary.entity.salaryacct.param.SalaryAcctRecordQueryParam;
import com.engine.salary.entity.salaryacct.param.SalaryAcctRecordSaveParam;
import com.engine.salary.entity.salaryacct.po.SalaryAcctRecordPO;
import com.engine.salary.entity.salaryacct.po.SalaryAcctSobConfigPO;
import com.engine.salary.entity.salarysob.dto.SalarySobCycleDTO;
import com.engine.salary.entity.salarysob.po.SalaryApprovalRulePO;
import com.engine.salary.entity.salarysob.po.*;
import com.engine.salary.entity.taxagent.po.TaxAgentPO;
import com.engine.salary.enums.salaryaccounting.SalaryAcctRecordStatusEnum;
import com.engine.salary.exception.SalaryRunTimeException;
import com.engine.salary.service.*;
import com.engine.salary.service.impl.*;
import com.engine.salary.sys.constant.SalarySysConstant;
import com.engine.salary.sys.entity.po.SalarySysConfPO;
import com.engine.salary.sys.service.SalarySysConfService;
import com.engine.salary.sys.service.impl.SalarySysConfServiceImpl;
import com.engine.salary.util.SalaryDateUtil;
import com.engine.salary.util.SalaryEntityUtil;
import com.engine.salary.util.SalaryI18nUtil;
import com.engine.salary.util.SalarySobUtil;
import com.engine.salary.util.page.Column;
import com.engine.salary.util.page.PageInfo;
import com.engine.salary.util.valid.ValidUtil;
import com.engine.salary.wrapper.proxy.SalaryAcctRecordWrapperProxy;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
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 SalaryAcctRecordWrapper extends Service implements SalaryAcctRecordWrapperProxy {

    private SalaryAcctRecordService getSalaryAcctRecordService(User user) {
        return ServiceUtil.getService(SalaryAcctRecordServiceImpl.class, user);
    }

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

    private SalaryAcctEmployeeService getSalaryAcctEmployeeService(User user) {
        return ServiceUtil.getService(SalaryAcctEmployeeServiceImpl.class, user);
    }

    private SalarySendService getSalarySendService(User user) {
        return ServiceUtil.getService(SalarySendServiceImpl.class, user);
    }

    private SalaryEmployeeService getSalaryEmployeeService(User user) {
        return ServiceUtil.getService(SalaryEmployeeServiceImpl.class, user);
    }

    private SalaryItemService getSalaryItemService(User user) {
        return ServiceUtil.getService(SalaryItemServiceImpl.class, user);
    }

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

    private SalaryAcctSobConfigService getSalaryAcctSobConfigService(User user) {
        return ServiceUtil.getService(SalaryAcctSobConfigServiceImpl.class, user);
    }

    private SalaryCacheService getSalaryCacheService(User user) {
        return ServiceUtil.getService(SalaryCacheServiceImpl.class, user);
    }


    private SalaryApprovalRuleService getSalaryApprovalRuleService(User user) {
        return ServiceUtil.getService(SalaryApprovalRuleServiceImpl.class, user);
    }

    private SalarySysConfService getSalarySysConfService(User user) {
        return ServiceUtil.getService(SalarySysConfServiceImpl.class, user);
    }

//    private ComInfoCache comInfoCache;

    public PageInfo<SalaryAcctRecordListDTO> listPage(SalaryAcctRecordQueryParam queryParam) {

        // 处理历史数据将薪资账套中将关联人员状态转换为List
        SalarySobUtil.handleEmployeeStatusHistory();
        getSalarySobService(user).handleHistory();

        // 处理工资单发放历史数据
        getSalarySendService(user).handleHistory();
        // 查询薪资核算记录（分页）
        PageInfo<SalaryAcctRecordPO> page = getSalaryAcctRecordService(user).listPageByParam(queryParam);
        PageInfo<SalaryAcctRecordListDTO> dtoPage = new PageInfo<SalaryAcctRecordListDTO>(SalaryAcctRecordListDTO.class);
        dtoPage.setPageNum(queryParam.getCurrent());
        dtoPage.setPageSize(queryParam.getPageSize());
        dtoPage.setTotal(page.getTotal());
        List<SalaryAcctRecordPO> list = page.getList();
        if (CollectionUtils.isNotEmpty(list)) {
            // 查询薪资账套
            Set<Long> salarySobIds = SalaryEntityUtil.properties(list, SalaryAcctRecordPO::getSalarySobId);
            List<SalarySobPO> salarySobPOS = getSalarySobService(user).listByIds(salarySobIds);
            // 规则设置中是否开启薪资审批按钮，默认关闭
            SalarySysConfPO salaryApprovalStatusConf = getSalarySysConfService(user).getOneByCode(SalarySysConstant.SALARY_APPROVAL_STATUS);
            List<Long> needApprovalSalarySobIds = new ArrayList<>();
            // 默认可以重新核算、可以手动归档
            boolean approvalCanReCalc = true;
            boolean approvalCanFile = true;
            if (salaryApprovalStatusConf != null && salaryApprovalStatusConf.getConfValue().equals("1")) {
                // 开启了薪资审批，获取这开启审批的账套id
                List<SalaryApprovalRulePO> salaryApprovalRules = getSalaryApprovalRuleService(user).listBySalarySobIds(salarySobIds);
                needApprovalSalarySobIds = salaryApprovalRules.stream().filter(po -> po.getOpenApproval().equals(1)).map(SalaryApprovalRulePO::getSalarySobId).collect(Collectors.toList());

                // 获取 开启审批的核算记录允许重新核算设置，默认开启
                SalarySysConfPO approvalCanReCalcFileStatusPO = getSalarySysConfService(user).getOneByCode(SalarySysConstant.APPROVAL_CAN_RE_CALC_STATUS);
                approvalCanReCalc = approvalCanReCalcFileStatusPO == null ? true : approvalCanReCalcFileStatusPO.getConfValue().equals("1");
                // 获取 开启审批的核算记录允许手动归档，默认开启
                SalarySysConfPO approvalCanManualFileStatusPO = getSalarySysConfService(user).getOneByCode(SalarySysConstant.APPROVAL_CAN_MANUAL_FILE_STATUS);
                approvalCanFile = approvalCanManualFileStatusPO == null ? true : approvalCanManualFileStatusPO.getConfValue().equals("1");
            }
            // 查询薪资核算记录的创建人员的人员信息
            List<Long> employeeIds = SalaryEntityUtil.properties(list, SalaryAcctRecordPO::getCreator, Collectors.toList());
            List<DataCollectionEmployee> employeeComInfos = getSalaryEmployeeService(user).getEmployeeByIdsAll(employeeIds);
            // 查询薪资核算人数的数量
            Set<Long> salaryAcctRecordIds = SalaryEntityUtil.properties(list, SalaryAcctRecordPO::getId);
            List<SalaryAcctEmployeeCountDTO> salaryAcctEmployeeCountDTOS = getSalaryAcctEmployeeService(user).countBySalaryAcctRecordId(salaryAcctRecordIds);
            // 查询工资单的查询情况
            List<SalarySendCheckDTO> salarySendCheckResult = getSalarySendService(user).getSalarySendCheckResult(salaryAcctRecordIds);

            List<TaxAgentPO> taxAgentPOS = getTaxAgentService(user).listAll();

            // 转换成列表dto
            List<SalaryAcctRecordListDTO> salaryAcctRecordListDTOS = SalaryAcctRecordBO.convert2ListDTO(list, salarySobPOS, employeeComInfos, salaryAcctEmployeeCountDTOS, salarySendCheckResult, taxAgentPOS, needApprovalSalarySobIds, approvalCanFile, approvalCanReCalc);
            dtoPage.setList(salaryAcctRecordListDTOS);
            // 规则设置中是否开启审批功能，默认关闭
            if (CollectionUtils.isEmpty(needApprovalSalarySobIds)) {
                // 没有开启审批的
                List<Column> columnList = dtoPage.getColumns().stream().filter(col -> !col.getKey().equals("approvalStatus")).collect(Collectors.toList());
                dtoPage.clearAndSetColumns(columnList);
            }
        }
        return dtoPage;
    }

    /**
     * @description 处理历史数据给薪资项目都增加个默认排序
     * @return null
     * @author Harryxzy
     * @date 2023/5/29 14:37
     */
//    private void generateSalaryItemSortedIndex() {
//        // 查询所有薪资项目
//        List<SalaryItemPO> salaryItemList = getSalaryItemService(user).listByParamOrderById(SalaryItemSearchParam.builder().build());
//        Optional<SalaryItemPO> needGenerate = salaryItemList.stream().filter(item -> item.getSortedIndex() == null).findFirst();
//        if(needGenerate.isPresent()){
//            Map<Integer, List<SalaryItemPO>> salaryItemMap = SalaryEntityUtil.group2Map(salaryItemList, SalaryItemPO::getUseInEmployeeSalary);
//            for(Map.Entry<Integer, List<SalaryItemPO>> entry : salaryItemMap.entrySet()){
//                List<SalaryItemPO> values = entry.getValue();
//                if(CollectionUtils.isNotEmpty(values)){
//                    // 将系统薪资项目放到最后
//                    values = values.stream()
//                            .sorted(Comparator.comparing(SalaryItemPO::getSystemType))
//                            .collect(Collectors.toList());
//                    // 添加默认sortedIndex
//                    AtomicInteger index = new AtomicInteger(values.size());
//                    values.stream().forEach(value -> value.setSortedIndex(index.getAndDecrement()));
//                    // 保存排序
//                    getSalaryItemService(user).batchUpdateSortedIndex(values);
//                }
//            }
//        }
//    }

    /**
     * 薪资核算记录详情
     *
     * @param id 薪资核算记录id
     * @return
     */
    public Map<String, Object> getForm(Long id) {
        SalaryAcctRecordFormDTO formDTO = new SalaryAcctRecordFormDTO();
        // 查询所有启用的薪资账套
        List<SalarySobPO> salarySobPOS = getSalarySobService(user).listByDisable(NumberUtils.INTEGER_ZERO);
        // 薪资核算详情表单中"薪资账套"的下拉选择项
        List<Map<String, Object>> salarySobs = salarySobPOS.stream()
                .map(salarySobPO -> {
                    Map<String, Object> map = new HashMap<>();
                    map.put("id", salarySobPO.getId());
                    map.put("name", salarySobPO.getName());
                    return map;
                })
                .collect(Collectors.toList());

        if (Objects.nonNull(id)) {
            // 查询薪资核算
            SalaryAcctRecordPO salaryAcctRecordPO = getSalaryAcctRecordService(user).getById(id);
            if (Objects.isNull(salaryAcctRecordPO)) {
                throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(98747, "薪资核算记录不存在或已被删除"));
            }
            // 查询薪资账套
            SalarySobPO salarySobPO = getSalarySobService(user).getById(salaryAcctRecordPO.getSalarySobId());
            if (Objects.isNull(salarySobPO)) {
                throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(98769, "薪资账套不存在或已被删除"));
            }
            // 转换成薪资核算记录详情dto
            formDTO.setId(salaryAcctRecordPO.getId())
                    .setSalarySobId(salarySobPO.getId())
                    .setSalarySobName(salarySobPO.getName())
                    .setSalaryMonth(SalaryDateUtil.localDate2YearMonth(salaryAcctRecordPO.getSalaryMonth()).toString())
                    .setDescription(salaryAcctRecordPO.getDescription());
        }
        // 转换成前端所需的数据格式
        Map<String, Object> result = new HashMap<>();
        result.put("formDTO", formDTO);
        result.put("salarySobs", salarySobs);
        return result;
    }

    /**
     * 薪资核算记录所用的薪资账套薪资周期、考勤周期……
     *
     * @param id 薪资核算记录的id
     * @return
     */
    public SalarySobCycleDTO getSalarySobCycleById(Long id) {
        return getSalaryAcctRecordService(user).getSalarySobCycleById(id);
    }

    /**
     * 保存
     *
     * @param saveParam 保存参数
     * @return
     */
    public Long save(SalaryAcctRecordSaveParam saveParam) {
        return getSalaryAcctRecordService(user).save(saveParam);
    }

    /**
     * 删除
     *
     * @param ids 薪资核算记录的id
     */
    public void delete(Collection<Long> ids) {
        getSalaryAcctRecordService(user).deleteByIds(ids);
    }

    /**
     * 归档
     *
     * @param id 薪资核算记录的id
     */
    public void file(Long id) {
        getSalaryAcctRecordService(user).file(id);
    }

    /**
     * 重新核算
     *
     * @param id 薪资核算记录的id
     */
    public void reCalculate(Long id) {
        getSalaryAcctRecordService(user).reCalculate(id);
    }


    /**
     * 判断是否存在合并计税
     *
     * @param id 主键id
     * @return
     */
    public Integer hasConsolidatedTax(Long id) {
        return getSalaryAcctRecordService(user).hasConsolidatedTax(id);
    }

    /**
     * @return void
     * @description 回算
     * @author Harryxzy
     * @date 2022/11/24 15:52
     */
    public void backCalculate(Long salaryAcctRecordId) {
        getSalaryAcctRecordService(user).backCalculate(salaryAcctRecordId);
    }


    /**
     * 薪资核算的账套配置是否已经发生变更
     *
     * @param id
     * @return true--发生了变更、false--没有发生变更
     */
    public boolean compareSobConfig(Long id) {
        SalaryAcctRecordPO salaryAcctRecord = getSalaryAcctRecordService(user).getById(id);
        if (salaryAcctRecord == null) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(156474, "参数错误，薪资核算记录不存在或已被删除"));
        }
        // 查询薪资核算记录已有的账套配置
        SalaryAcctSobConfigPO salaryAcctSobConfig = getSalaryAcctSobConfigService(user).getBySalaryAcctRecordId(false, id);
        // 查询最新的账套配置
        SalaryAcctSobConfigPO newSalaryAcctSobConfig = getSalaryAcctSobConfigService(user).initBySalaryAcctRecord(salaryAcctRecord);
        if (Objects.isNull(salaryAcctSobConfig)) {
            // 为避免并发造成的事物问题，需要做一下并发处理
            String cacheIndex = id.toString();
            String acctSobConfigFlag = getSalaryCacheService(user).get(SalaryCacheKey.ACCT_SOB_CONFIG + cacheIndex);
            if (acctSobConfigFlag != null && StringUtils.equals(acctSobConfigFlag, "TRUE")) {
                throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(160540, "其他人员正在操作此条薪资核算记录，请稍后再试"));
            }
            getSalaryCacheService(user).set(SalaryCacheKey.ACCT_SOB_CONFIG + cacheIndex, "TRUE");
            getSalaryAcctSobConfigService(user).save(newSalaryAcctSobConfig);
            getSalaryCacheService(user).remove(SalaryCacheKey.ACCT_SOB_CONFIG + cacheIndex);
            return false;
        }

        // 如果薪资核算记录已经归档了，不再校验薪资核算的账套配置是否已经发生变更
        if (!Objects.equals(salaryAcctRecord.getStatus(), SalaryAcctRecordStatusEnum.NOT_ARCHIVED.getValue())) {
            return false;
        }

        SalaryAcctConfig config = SalaryAcctConfig.parse(salaryAcctSobConfig);
        SalaryAcctConfig newConfig = SalaryAcctConfig.parse(newSalaryAcctSobConfig);

        // 判断账套配置是否已经变更了
        SalarySobPO salarySobPO = config.getSalarySob();
        SalarySobPO newSalarySobPO = newConfig.getSalarySob();
        if (!ValidUtil.compare(salarySobPO, newSalarySobPO)) {
            return true;
        }

        List<SalarySobEmpFieldPO> salarySobEmpFields = config.getSalarySobEmpFields();
        List<SalarySobEmpFieldPO> newSalarySobEmpFields = newConfig.getSalarySobEmpFields();
        if (SalarySobEmpFieldPO.toCompareString(salarySobEmpFields).compareTo(SalarySobEmpFieldPO.toCompareString(newSalarySobEmpFields)) != 0) {
            return true;
        }

        List<SalarySobItemGroupPO> salarySobItemGroups = config.getSalarySobItemGroups();
        List<SalarySobItemGroupPO> newSalarySobItemGroups = newConfig.getSalarySobItemGroups();
        if (SalarySobItemGroupPO.toCompareString(salarySobItemGroups).compareTo(SalarySobItemGroupPO.toCompareString(newSalarySobItemGroups)) != 0) {
            return true;
        }

        List<SalarySobItemPO> salarySobItems = config.getSalarySobItems();
        List<SalarySobItemPO> newSalarySobItems = newConfig.getSalarySobItems();
        if (SalarySobItemPO.toCompareString(salarySobItems).compareTo(SalarySobItemPO.toCompareString(newSalarySobItems)) != 0) {
            return true;
        }

        List<SalarySobBackItemPO> salarySobBackItems = config.getSalarySobBackItems();
        List<SalarySobBackItemPO> newSalarySobBackItems = newConfig.getSalarySobBackItems();
        if (SalarySobBackItemPO.toCompareString(salarySobBackItems).compareTo(SalarySobBackItemPO.toCompareString(newSalarySobBackItems)) != 0) {
            return true;
        }
        return false;
    }

    /**
     * 更新薪资核算的账套配置
     *
     * @param id
     */
    public void updateSobConfig(Long id) {
        getSalaryAcctSobConfigService(user).updateBySalaryAcctRecordId(id);
    }
}
