package com.pusong.business.batch;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.pusong.business.domain.*;
import com.pusong.business.domain.vo.PsContractBusinessDetailVo;
import com.pusong.business.domain.vo.PsContractBusinessVo;
import com.pusong.business.domain.vo.PsContractPayVo;
import com.pusong.business.enums.CommonStatusEnum;
import com.pusong.business.enums.ContractStatusEnum;
import com.pusong.business.enums.PayStatusEnum;
import com.pusong.business.mapper.*;
import com.pusong.business.service.IPsContractBusinessService;
import com.pusong.common.core.utils.DateUtils;
import com.pusong.common.core.utils.StringUtils;
import com.pusong.system.domain.SysUser;
import com.pusong.system.domain.SysUserPost;
import com.pusong.system.mapper.SysUserMapper;
import com.pusong.system.mapper.SysUserPostMapper;
import jakarta.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;


@Component
public class CalSalaryBatch {
    private static final Logger log = LoggerFactory.getLogger(CalSalaryBatch.class);
    @Resource
    private SysUserMapper sysUserMapper;
    @Resource
    private PsContractInfoMapper psContractInfoMapper;
    @Autowired
    private IPsContractBusinessService businessService;
    @Resource
    private SysUserPostMapper sysUserPostMapper;
    @Resource
    private PsSalaryConfigMapper psSalaryConfigMapper;
    @Resource
    private PsSalaryMapper psSalaryMapper;
    @Resource
    private PsContractPayMapper payMapper;

    @Resource
    private PsSalaryContractMapper salaryContractMapper;
    LocalDate now = null;

    public void setNow(LocalDate now) {
        this.now = now;
    }

    /**
     * 每月1日凌晨1点执行
     */
    @Transactional(rollbackFor = Exception.class)
    @Scheduled(cron = "30 40 23 * * ?")
    public void execute(){
        log.info("员工提成批处理开始");
        LocalDate now = this.now == null ? LocalDate.now(): this.now;
//        LocalDate lastDayOfLastMonth = now.with(TemporalAdjusters.firstDayOfMonth());//这个月第一天0分0秒
//        LocalDate firstDayOfLastMonth = lastDayOfLastMonth.minusMonths(1);//上个月第一天0分0秒
        LocalDate firstDayOfLastMonth = now.with(TemporalAdjusters.firstDayOfMonth());//这个月第一天0分0秒
        LocalDate lastDayOfLastMonth = firstDayOfLastMonth.plusMonths(1);//下个月第一天0分0秒

        //删除该月历史数据
        psSalaryMapper.delete(Wrappers.<PsSalary>lambdaQuery().eq(PsSalary::getSalaryMonth,DateUtils.toString(firstDayOfLastMonth,"yyyy-MM")));
        log.info("查询所有员工");
        List<SysUser> list = sysUserMapper.selectList(Wrappers.<SysUser>lambdaQuery().select(SysUser::getUserId).eq(SysUser::getStatus, 0));
        log.info("查询所有员工{}", list.size());

        log.info("查询符合条件的合同");
        //当月新签+当月之前未完成合同数 = 全部合同 - 完成时间在当月之前的
        List<PsContractInfo> contractInfos = psContractInfoMapper.selectList(Wrappers.<PsContractInfo>lambdaQuery()
                .ne(PsContractInfo::getIsCancel, CommonStatusEnum.SUCCESS.getCode())
                .and(wq->wq.lt(PsContractInfo::getFinishDate, firstDayOfLastMonth).or().isNull(PsContractInfo::getFinishDate)));

//        List<PsContractInfo> contractInfos = psContractInfoMapper.selectList(Wrappers.<PsContractInfo>lambdaQuery()
//                .ne(PsContractInfo::getIsCancel, CommonStatusEnum.SUCCESS.getCode())
//                .between(PsContractInfo::getFinishDate, firstDayOfLastMonth, lastDayOfLastMonth));
        log.info("查询本月所有的合同{}", contractInfos.size());
        List<PsContractInfo> finists = contractInfos.stream().filter(item -> StringUtils.equals(item.getContractStatus(), ContractStatusEnum.SUCCESS.getCode())).toList();
        List<PsContractInfo> unfinists = contractInfos.stream().filter(item -> !StringUtils.equals(item.getContractStatus(), ContractStatusEnum.SUCCESS.getCode())).toList();
        log.info("本月已完成合同数量{},未完成数量{}", finists.size(),unfinists.size());

        //转换 客户经理有的合同编码
        Map<Long, List<PsContractInfo>> finistMap = finists.stream().collect(Collectors.groupingBy(PsContractInfo::getCustomManager,Collectors.toList()));
        Map<Long, List<PsContractInfo>> unfinistMap = unfinists.stream().collect(Collectors.groupingBy(PsContractInfo::getCustomManager,Collectors.toList()));;
        //查询所有岗位配置信息
        List<PsSalaryConfig> configs = psSalaryConfigMapper.selectList();
        Map<Long, Map<String, BigDecimal>> configMap = configs.stream().collect(Collectors.groupingBy(PsSalaryConfig::getPostId,
                Collectors.toMap(PsSalaryConfig::getServiceProject, PsSalaryConfig::getSalaryRatio)));
        Map<Long, Map<String, BigDecimal>> costMap = configs.stream().collect(Collectors.groupingBy(PsSalaryConfig::getPostId,
                Collectors.toMap(PsSalaryConfig::getServiceProject, PsSalaryConfig::getCost)));
        //循环计算每个员工的提成
        for (SysUser user : list){
            Long userId = user.getUserId();
            //默认员工仅有一个岗位
            log.info("查询员工岗位{}",userId);
            SysUserPost post = sysUserPostMapper.selectOne(new LambdaQueryWrapper<SysUserPost>().eq(SysUserPost::getUserId, userId).last("limit 1"));
            if(post == null){continue;}
            log.info("查询员工岗位{}",post.getPostId());
            //已完成合同的合同编码
            List<String> codeList = new ArrayList<>(finistMap.get(userId) == null ? new ArrayList<>() : finistMap.get(userId).stream().map(PsContractInfo::getContractCode).toList());
            //服务列表
            List<PsContractBusinessVo> busin = businessService.selectBusinessList(codeList);
            //所有合同的退款列表
//            codeList.addAll(unfinistMap.get(userId) == null ? new ArrayList<>() : unfinistMap.get(userId).stream().map(PsContractInfo::getContractCode).toList());

            log.info("进行计算提成计算");
            BigDecimal salary = calSalary(busin, configMap.get(post.getPostId()),costMap.get(post.getPostId()));
            List<PsContractPay> payList = new ArrayList<>();
            //查询已完成合同的退款列表
            if(CollectionUtils.isNotEmpty(codeList)){
                payList = payMapper.getRefundList(Wrappers.<PsContractPay>query()
                        .in("pay.contract_code", codeList).isNotNull("info.finish_date")
                        .apply("pay.pay_date>info.finish_date")
                        .eq("pay.business_type","2").eq("pay.pay_status",PayStatusEnum.SUCCESS.getCode()));
            }
            //salary- 退费金额*服务项目最高提成
            salary = salary.subtract(this.calReturnSalary(payList,configMap.get(post.getPostId())));
            log.info("进行计算提成计算{}",salary);
            log.info("进行存表");
            PsSalary psSalary = new PsSalary();
            psSalary.setSalaryMoney(salary);
            psSalary.setSalaryMonth(DateUtils.toString(firstDayOfLastMonth,"yyyy-MM"));
            psSalary.setSalaryDate(DateUtils.toDate(firstDayOfLastMonth));
            psSalary.setPostId(post.getPostId());
            psSalary.setUserId(userId);
            //已完成合同数量
            int finishNum = CollectionUtils.isEmpty(finistMap.get(userId))?0:finistMap.get(userId).size();
            //未完成合同数量
            int unFinishNum = CollectionUtils.isEmpty(unfinistMap.get(userId))?0:unfinistMap.get(userId).size();
            psSalary.setContractNum(finishNum+unFinishNum);
            psSalary.setFinistContractNum(finishNum);
            psSalary.setUnfinistContractNum(unFinishNum);
            //已完成合同金额
            BigDecimal finishMoney = CollectionUtils.isEmpty(finistMap.get(userId))?BigDecimal.ZERO:
                    finistMap.get(userId).stream().map(PsContractInfo::getContractAmount).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
            //未完成合同金额
            BigDecimal unFinishMoney = CollectionUtils.isEmpty(unfinistMap.get(userId))?BigDecimal.ZERO:
                    unfinistMap.get(userId).stream().map(PsContractInfo::getContractAmount).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);;
            psSalary.setContractMoney(finishMoney.add(unFinishMoney));
            psSalary.setFinistContractMoney(finishMoney);
            psSalary.setUnfinistContractMoney(unFinishMoney);
            //已完成合同编码
            List<String> finCode = new ArrayList<>();
            //未完成合同编码
            List<String> unfinCode = new ArrayList<>();
            if(CollectionUtils.isNotEmpty(finistMap.get(userId))){
                finCode = finistMap.get(userId).stream().map(PsContractInfo::getContractCode).toList();
            }
            if(CollectionUtils.isNotEmpty(unfinistMap.get(userId))){
                unfinCode = unfinistMap.get(userId).stream().map(PsContractInfo::getContractCode).toList();
            }
//            psSalary.setFinishContractCode(String.join(",",finCode));
//            psSalary.setUnfinishContractCode(String.join(",",unfinCode));
            psSalaryMapper.insert(psSalary);
            List<PsSalaryContract> lis = new ArrayList<>();
            if(CollectionUtils.isNotEmpty(finCode)) {
                List<PsSalaryContract> fin = psContractInfoMapper.querySalaryListByCodes(new QueryWrapper<PsContractInfo>().in("info.contract_code", finCode));
                if(CollectionUtils.isNotEmpty(fin)) {
                    fin.forEach(item->{item.setStatus("1");item.setSalaryId(psSalary.getId());});
                    lis.addAll(fin);
                }
            }
            if(CollectionUtils.isNotEmpty(unfinCode)) {
                List<PsSalaryContract> unfin = psContractInfoMapper.querySalaryListByCodes(new QueryWrapper<PsContractInfo>().in("info.contract_code", unfinCode));
                if(CollectionUtils.isNotEmpty(unfin)) {
                    unfin.forEach(item->{item.setStatus("2");item.setSalaryId(psSalary.getId());});
                    lis.addAll(unfin);
                }
            }
            lis.forEach(item->{
                item.setUserId(user.getUserId());
                item.setSalaryRatioDate(DateUtils.toDate(firstDayOfLastMonth));
            });
            salaryContractMapper.insertBatch(lis);
        }

        log.info("员工提成批处理结束");
    }

    /**
     * （服务项目1收费-服务项目1成本）*服务项目1提成比例+（服务项目2收费-服务项目2成本）*服务项目2提成比例
     * +（服务项目3收费-服务项目3成本）*服务项目3提成比例
     *
     * @param businessVo 服务项目
     * @param configMap 配置map
     * @param costMap 成本map
     * @return
     */
    private BigDecimal calSalary(List<PsContractBusinessVo> businessVo,Map<String, BigDecimal> configMap,
                                 Map<String, BigDecimal> costMap){
        String cbStr = "0.0";
        String rateStr = "0.1";

        configMap = CollectionUtils.isEmpty(configMap)?new HashMap<>():configMap;
        costMap = CollectionUtils.isEmpty(costMap)?new HashMap<>():costMap;
        BigDecimal salary = BigDecimal.ZERO;
        for(PsContractBusinessVo item : businessVo){

            //如果详情没有填写金额的话按照详情中最低的提成比例进行计算（按照详情算）
            if(item.getDetailVoList().get(0).getAmount() == null
                    || BigDecimal.ZERO.compareTo(item.getDetailVoList().get(0).getAmount()) == 0){
                BigDecimal rate = new BigDecimal(cbStr);
                BigDecimal cb = new BigDecimal(rateStr);
                for (PsContractBusinessDetailVo detail:item.getDetailVoList()){
                    BigDecimal rateConfig = configMap.get(detail.getBusinessProject()) == null?new BigDecimal("0.1"):configMap.get(detail.getBusinessProject());
                    rate = rate.compareTo(rateConfig)<0?rate:rateConfig;
                    cb = cb.add(costMap.get(detail.getBusinessProject()) == null ? new BigDecimal(cbStr):costMap.get(detail.getBusinessProject()));
                }
                //服务类型金额 - 成本
                BigDecimal amount = item.getBusinessAmount().subtract(cb);
                // ×比例
                salary = salary.add(amount.multiply(rate));
            }else{//（按照类型算）
                for (PsContractBusinessDetailVo detail:item.getDetailVoList()){
                    //比例
                    BigDecimal rateConfig = configMap.get(detail.getBusinessProject()) == null?new BigDecimal("0.1"):configMap.get(detail.getBusinessProject());
                    //成本
                    BigDecimal rateCost = costMap.get(detail.getBusinessProject()) == null ? new BigDecimal(cbStr):costMap.get(detail.getBusinessProject());
                    //（项目金额-成本）） *比例
                    salary = salary.add(detail.getAmount().subtract(rateCost).multiply(rateConfig));
                }
            }

        }
        return salary;

    }

    /**
     * 算退费金额
     * @param payList
     * @param configMap
     * @return
     */
    private BigDecimal calReturnSalary(List<PsContractPay> payList,Map<String, BigDecimal> configMap){
        String cbStr = "0.0";
        BigDecimal retur = BigDecimal.ZERO;
        if(CollectionUtils.isEmpty(payList)){return retur;}
        configMap = configMap==null?new HashMap<>():configMap;
        for(PsContractPay pay : payList){
            List<PsContractBusinessVo> list = businessService.selectBusinessList(List.of(pay.getContractCode()));
            BigDecimal rate = new BigDecimal(cbStr);
            for(PsContractBusinessVo item : list){
                for (PsContractBusinessDetailVo detail:item.getDetailVoList()){
                    BigDecimal rateConfig = configMap.get(detail.getBusinessProject()) == null?new BigDecimal("0.1"):configMap.get(detail.getBusinessProject());
                    rate = rate.compareTo(rateConfig)>0?rate:rateConfig;
                }
            }
            retur = retur.add(pay.getMoney().multiply(rate));
        }
        return retur;
    }
}
