package com.engine.salary.service.impl;

import com.engine.common.util.ServiceUtil;
import com.engine.core.impl.Service;
import com.engine.salary.encrypt.EncryptUtil;
import com.engine.salary.entity.siaccount.bo.InsuranceComparisonResultBO;
import com.engine.salary.entity.siaccount.dto.InsuranceComparisonResultListDTO;
import com.engine.salary.entity.siaccount.param.InsuranceComparisonResultQueryParam;
import com.engine.salary.entity.siarchives.po.InsuranceArchivesBaseInfoPO;
import com.engine.salary.entity.sicategory.po.ICategoryPO;
import com.engine.salary.entity.siexport.param.InsuranceExportParam;
import com.engine.salary.entity.siexport.po.AccountExportPO;
import com.engine.salary.entity.siexport.po.ExcelAccountExportPO;
import com.engine.salary.entity.sischeme.po.InsuranceSchemeDetailPO;
import com.engine.salary.enums.siaccount.EmployeeStatusEnum;
import com.engine.salary.mapper.InsuranceExportMapper;
import com.engine.salary.mapper.siarchives.InsuranceBaseInfoMapper;
import com.engine.salary.mapper.sicategory.ICategoryMapper;
import com.engine.salary.mapper.sischeme.InsuranceSchemeDetailMapper;
import com.engine.salary.service.SIAComparisonResultService;
import com.engine.salary.service.SIArchivesService;
import com.engine.salary.service.SISchemeService;
import com.engine.salary.sys.entity.vo.OrderRuleVO;
import com.engine.salary.sys.service.SalarySysConfService;
import com.engine.salary.sys.service.impl.SalarySysConfServiceImpl;
import com.engine.salary.util.SalaryI18nUtil;
import com.engine.salary.util.db.MapperProxyFactory;
import com.engine.salary.util.excel.ExcelUtilPlus;
import com.engine.salary.util.page.Column;
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 org.apache.commons.lang3.BooleanUtils;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import weaver.hrm.User;

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

/**
 * @Author: sy
 * @Description: 福利核算的线下对比结果
 * @Date: 2022/9/28
 **/
public class SIAComparisonResultServiceImpl extends Service implements SIAComparisonResultService {
    private EncryptUtil encryptUtil = new EncryptUtil();

//    private SIArchivesBiz siArchivesBiz = new SIArchivesBiz();

    private InsuranceExportMapper getInsuranceExportMapper() {
        return MapperProxyFactory.getProxy(InsuranceExportMapper.class);
    }

    private InsuranceBaseInfoMapper getInsuranceBaseInfoMapper() {
        return MapperProxyFactory.getProxy(InsuranceBaseInfoMapper.class);
    }

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

    private ICategoryMapper getICategoryMapper() {
        return MapperProxyFactory.getProxy(ICategoryMapper.class);
    }

    private InsuranceSchemeDetailMapper getInsuranceSchemeDetailMapper() {
        return MapperProxyFactory.getProxy(InsuranceSchemeDetailMapper.class);
    }

    private SISchemeService getSISchemeService(User user) {
        return ServiceUtil.getService(SISchemeServiceImpl.class, user);
    }

    public SIArchivesService getSIArchivesService(User user) {
        return ServiceUtil.getService(SIArchivesServiceImpl.class, user);
    }

    /**
     * 根据列表查询条件查询线下对比结果（分页）
     */
    @Override
    public InsuranceComparisonResultListDTO listPageByParam(InsuranceComparisonResultQueryParam queryParam) {
        return listByParam(true, queryParam);
    }

    /**
     * 根据列表查询条件查询线下对比结果
     */
    @Override
    public InsuranceComparisonResultListDTO listByParam(InsuranceComparisonResultQueryParam queryParam) {
        return listByParam(false, queryParam);
    }

    @Override
    public XSSFWorkbook exportComparisonResult(InsuranceComparisonResultQueryParam queryParam) {
        ValidUtil.doValidator(queryParam);

        // 查询线下对比结果
        InsuranceComparisonResultListDTO insuranceComparisonResultListDTO = listByParam(queryParam);
        // 薪资核算线下对比结果列表表头
        List<Object> headerList = Lists.newArrayList();

        Set<String> employeeInfo = employeeInfo();
        Set<String> welfareInfo = welfareInfo();
        for (Column weaTableColumn : insuranceComparisonResultListDTO.getWeaTableColumns()) {
            // 员工信息字段
            if (employeeInfo.contains(weaTableColumn.getKey())) {
                headerList.add(weaTableColumn.getTitle());
            }
            // 薪资项目的表头
            if (welfareInfo.contains(weaTableColumn.getKey())) {
                headerList.add(weaTableColumn.getTitle() + " (线上值)");
                headerList.add(weaTableColumn.getTitle() + " (线下值)");
            }
        }

        List<Map<String, Object>> resultMapList = insuranceComparisonResultListDTO.getData().getList();
        // excel导出的数据
        List<List<Object>> rows = new ArrayList<>();
        rows.add(headerList);
        for (Map<String, Object> map : resultMapList) {
            List<Object> row = Lists.newArrayList();
            for (Column weaTableColumn : insuranceComparisonResultListDTO.getWeaTableColumns()) {
                // 员工信息字段的值
                if (employeeInfo.contains(weaTableColumn.getKey())) {
                    row.add(map.get(weaTableColumn.getKey()));
                }
                // 福利项目的值
                if (welfareInfo.contains(weaTableColumn.getKey())) {
                    Map tempMap = (Map) map.getOrDefault(weaTableColumn.getKey(), Collections.emptyMap());
                    row.add(tempMap.get("acctResultValue"));
                    row.add(tempMap.get("excelResultValue"));
                }
            }
            rows.add(row);
        }

        String sheetName = "线下对比结果";

        return ExcelUtilPlus.genWorkbookV2(rows, sheetName);
    }


    /**
     * 根据福利核算人员查询福利核算线下对比结果
     */
    private InsuranceComparisonResultListDTO listByParam(boolean needPage, InsuranceComparisonResultQueryParam queryParam) {

//        boolean welBaseDiffSign = siArchivesBiz.isDiffWelBase();
        boolean welBaseDiffSign = getSIArchivesService(user).isDiffWelBase();
        //排序配置
        OrderRuleVO orderRule = getSalarySysConfService(user).orderRule();

        //1-查询线上福利核算记录
        InsuranceExportParam insuranceExportParam = new InsuranceExportParam();
        insuranceExportParam.setBillMonth(queryParam.getBillMonth());
        insuranceExportParam.setPaymentOrganization(queryParam.getPaymentOrganization());
        insuranceExportParam.setOrderRule(orderRule);
        //系统人员核算明细
        List<AccountExportPO> accountExportPOS = getInsuranceExportMapper().exportAccount(queryParam.getPaymentStatus(), insuranceExportParam);
        //非系统人员核算明细
        List<AccountExportPO> extAccountExportPOS = getInsuranceExportMapper().exportExtAccount(queryParam.getPaymentStatus(), insuranceExportParam);
        accountExportPOS.addAll(extAccountExportPOS);
        SalaryI18nUtil.i18nList(accountExportPOS);
        //如果入参包含姓名信息
        if (queryParam.getUserName() != null) {
            accountExportPOS = accountExportPOS.stream().filter(v -> v.getUserName().contains(queryParam.getUserName())).collect(Collectors.toList());
        }

        //过滤出福利档案基础信息表中runStatus为正在缴纳和待减员的人员
        List<InsuranceArchivesBaseInfoPO> baseInfoPOList = getInsuranceBaseInfoMapper().listAll();
        List<Long> canAccountIds = baseInfoPOList.stream()
                .filter(f->f.getPaymentOrganization().toString().equals(queryParam.getPaymentOrganization())
                        && (f.getRunStatus().equals(EmployeeStatusEnum.PAYING.getValue()) || f.getRunStatus().equals(EmployeeStatusEnum.STAY_DEL.getValue())))
                .map(InsuranceArchivesBaseInfoPO::getEmployeeId)
                .collect(Collectors.toList());
        accountExportPOS = accountExportPOS.stream().filter(v -> canAccountIds.contains(v.getEmployeeId())).collect(Collectors.toList());

        encryptUtil.decryptList(accountExportPOS, AccountExportPO.class);
        //2-查询线下对比数据
        //系统人员对比数据
        List<ExcelAccountExportPO> excelAccountExportPOS = getInsuranceExportMapper().exportExcelAccount(queryParam);
        //非系统人员对比数据
        List<ExcelAccountExportPO> extExcelAccountExportPOS = getInsuranceExportMapper().exportExtExcelAccount(queryParam);
        excelAccountExportPOS.addAll(extExcelAccountExportPOS);
        encryptUtil.decryptList(excelAccountExportPOS, ExcelAccountExportPO.class);

        //整理线上核算记录相关的福利方案，并以此整理需要对比的福利项类别数据
        Set<Long> welfareSchemeIds = new HashSet<>();
        accountExportPOS.forEach(f -> {
            welfareSchemeIds.add(f.getSocialSchemeId());
            welfareSchemeIds.add(f.getFundSchemeId());
            welfareSchemeIds.add(f.getOtherSchemeId());
        });
        List<InsuranceSchemeDetailPO> insuranceSchemeDetailPos = getInsuranceSchemeDetailMapper().listAll();
        List<Long> insuranceBaseIds = insuranceSchemeDetailPos.stream()
                .filter(f -> welfareSchemeIds.contains(f.getPrimaryId()) && f.getIsPayment() == 1)
                .map(InsuranceSchemeDetailPO::getInsuranceId)
                .collect(Collectors.toList());
        List<Long> insurancePerPayIds = insuranceSchemeDetailPos.stream()
                .filter(f -> welfareSchemeIds.contains(f.getPrimaryId()) && f.getIsPayment() == 1 && f.getPaymentScope() == 2)
                .map(InsuranceSchemeDetailPO::getInsuranceId)
                .collect(Collectors.toList());
        List<Long> insuranceComPayIds = insuranceSchemeDetailPos.stream()
                .filter(f -> welfareSchemeIds.contains(f.getPrimaryId()) && f.getIsPayment() == 1 && f.getPaymentScope() == 1)
                .map(InsuranceSchemeDetailPO::getInsuranceId)
                .collect(Collectors.toList());
        Set<Long> insuranceBaseSet = new HashSet<>(insuranceBaseIds);
        Set<Long> insurancePerPaySet = new HashSet<>(insurancePerPayIds);
        Set<Long> insuranceComPaySet = new HashSet<>(insuranceComPayIds);
        //3-构建福利核算对比结果列表表头
        List<Column> weaTableColumns = InsuranceComparisonResultBO.buildTableColumns4ComparisonResult(insuranceBaseSet, insurancePerPaySet, insuranceComPaySet, welBaseDiffSign);
        //4-通过线上线下两份数据获得对比结果
        Map<Long, String> schemeIdNameMap = getSISchemeService(user).getSchemeIdNameMap();
        List<Map<String, Object>> resultMapList = InsuranceComparisonResultBO.buildComparisonTableData(accountExportPOS, excelAccountExportPOS, schemeIdNameMap, welBaseDiffSign);

        // 系统值和线下值一致的人员
        if (queryParam.isOnlyDiffEmployee()) {
            // 过滤系统值和线下值一致的薪资核算人员
            resultMapList = resultMapList.stream()
                    .filter(map -> BooleanUtils.toBoolean(String.valueOf(map.get("different"))))
                    .collect(Collectors.toList());
        }
        // 分页
        PageInfo<Map<String, Object>> dtoPage = new PageInfo<>();
        dtoPage.setTotal(resultMapList.size());
        if (needPage) {
            dtoPage.setList(SalaryPageUtil.subList(queryParam.getCurrent(), queryParam.getPageSize(), resultMapList));
            dtoPage.setPageSize(queryParam.getPageSize());
            dtoPage.setPageNum(queryParam.getCurrent());
        } else {
            dtoPage.setList(resultMapList);
        }
        // 返回结果
        return new InsuranceComparisonResultListDTO().setWeaTableColumns(weaTableColumns).setData(dtoPage);
    }

    private Set<String> welfareInfo() {
//        boolean welBaseDiffSign = siArchivesBiz.isDiffWelBase();
        boolean welBaseDiffSign = getSIArchivesService(user).isDiffWelBase();
        Set<String> info = new HashSet<>();

        List<ICategoryPO> listAll = getICategoryMapper().listAll();
        List<ICategoryPO> socialWelfareList = listAll.stream().filter(e -> e.getWelfareType() == 1).collect(Collectors.toList());
        List<ICategoryPO> fundWelfareList = listAll.stream().filter(e -> e.getWelfareType() == 2).collect(Collectors.toList());
        List<ICategoryPO> otherWelfareList = listAll.stream().filter(e -> e.getWelfareType() == 3).collect(Collectors.toList());

        if (welBaseDiffSign) {
            //组装社保基数
            for (ICategoryPO po : socialWelfareList) {
                info.add(po.getId() + "socialPerBase");
                info.add(po.getId() + "socialComBase");
            }
            //组装公积金基数
            for (ICategoryPO po : fundWelfareList) {
                info.add(po.getId() + "fundPerBase");
                info.add(po.getId() + "fundComBase");
            }
            //组装其他福利基数
            for (ICategoryPO po : otherWelfareList) {
                info.add(po.getId() + "otherPerBase");
                info.add(po.getId() + "otherComBase");
            }
        } else {
            //组装社保基数
            for (ICategoryPO po : socialWelfareList) {
                info.add(po.getId() + "socialBase");
            }
            //组装公积金基数
            for (ICategoryPO po : fundWelfareList) {
                info.add(po.getId() + "fundBase");
            }
            //组装其他福利基数
            for (ICategoryPO po : otherWelfareList) {
                info.add(po.getId() + "otherBase");
            }
        }

        //社保个人（生育保险个人、工伤保险个人、失业保险个人、养老保险个人、医疗保险个人）
        for (ICategoryPO po : socialWelfareList) {
            info.add(po.getId() + "socialPer");
        }
        info.add("socialPerSum");

        //住房公积金个人、补充住房公积金个人
        for (ICategoryPO po : fundWelfareList) {
            info.add(po.getId() + "fundPer");
        }
        info.add("fundPerSum");

        //其他个人（比如企业年金个人）
        for (ICategoryPO po : otherWelfareList) {
            info.add(po.getId() + "otherPer");
        }
        info.add("otherPerSum");
        info.add("perSum");

        //社保单位（生育保险单位、工伤保险单位、失业保险单位、养老保险单位、医疗保险单位）
        for (ICategoryPO po : socialWelfareList) {
            info.add(po.getId() + "socialCom");
        }
        info.add("socialComSum");

        //住房公积金单位、补充住房公积金单位
        for (ICategoryPO po : fundWelfareList) {
            info.add(po.getId() + "fundCom");
        }
        info.add("fundComSum");

        //其他单位（比如企业年金单位）
        for (ICategoryPO po : otherWelfareList) {
            info.add(po.getId() + "otherCom");
        }
        info.add("otherComSum");
        info.add("comSum");

        info.add("socialSum");
        info.add("fundSum");
        info.add("otherSum");
        info.add("total");

        return info;
    }

    private Set<String> employeeInfo() {
        Set<String> info = new HashSet<>();

        info.add("userName");
        info.add("department");
        info.add("mobile");
        info.add("workcode");
        info.add("socialPayOrg");

        return info;
    }
}
