package jnpf.attendance.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import jnpf.attendance.mapper.PublicHolidayRulesMapper;
import jnpf.attendance.service.*;
import jnpf.entity.attendance.AttendanceBalanceRecordEntity;
import jnpf.entity.attendance.AttendancePublicHolidayRules;
import jnpf.entity.attendance.AttendanceRuleScope;
import jnpf.enums.attendance.ScopeBizType;
import jnpf.model.attendance.dto.AttendancePublicHolidayRulesDto;
import jnpf.model.attendance.vo.attendance.AttendancePublicHolidayBalance;
import jnpf.model.attendance.vo.attendance.AttendancePublicHolidayRulesVo;
import jnpf.model.common.PageDto;
import jnpf.util.ConstantUtil;
import jnpf.util.FtbUtil;
import jnpf.util.UserProvider;
import jnpf.util.attendance.ExpiresTimeUtil;
import jnpf.util.attendance.RuleScopeUtil;
import org.apache.commons.lang3.tuple.MutablePair;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author panpan
 */
@Service
public class PublicHolidayRulesServiceImpl implements PublicHolidayRulesService {

    @Resource
    private PublicHolidayRulesMapper publicHolidayRulesMapper;

    @Resource
    private RuleScopeUtil ruleScopeUtil;

    @Resource
    private UserProvider userProvider;

    @Resource
    private RuleScopeService ruleScopeService;

    @Resource
    @Lazy
    private AttendanceDailyRuleService dailyRuleService;

    @Resource
    @Lazy
    private AttendanceUserBalanceService attendanceUserBalanceService;

    @Resource
    @Lazy
    private AttendanceUserBalanceRecordService attendanceUserBalanceRecordService;

    @Resource
    private ExpiresTimeUtil expiresTimeUtil;

    @Override
    public PageInfo<AttendancePublicHolidayRulesVo> list(String iText, PageDto pageDto) {
        PageHelper.startPage(pageDto.getCurrentPage(), pageDto.getPageSize());
        PageInfo<AttendancePublicHolidayRulesVo> attendancePublicHolidayRulesVoPageInfo = new PageInfo<>(publicHolidayRulesMapper.list(iText));
        // 当适配范围为指定组织或成员时查询适配范围
        if (null != attendancePublicHolidayRulesVoPageInfo.getList() && !attendancePublicHolidayRulesVoPageInfo.getList().isEmpty()) {
            // 过滤出适配范围为指定组织或成员的规则id集合
            List<String> scopeOfAdaptationIds = attendancePublicHolidayRulesVoPageInfo.getList().stream().filter(v -> v.getScopeOfAdaptation().equals(1)).map(AttendancePublicHolidayRulesVo::getId).collect(Collectors.toList());
            if (!scopeOfAdaptationIds.isEmpty()) {
                // 拼接适配范围
                Map<String, MutablePair<List<String>, List<String>>> stringMutablePairMap = ruleScopeUtil.selectScopeListBatch(scopeOfAdaptationIds, ScopeBizType.PUBLIC_HOLIDAY_RULE);
                // 遍历vo列表，根据规则id设置适配范围
                for (AttendancePublicHolidayRulesVo v : attendancePublicHolidayRulesVoPageInfo.getList()) {
                    MutablePair<List<String>, List<String>> pair = stringMutablePairMap.get(v.getId());
                    if (null != pair) {
                        v.setOrganizeNum(pair.getLeft().size());
                        v.setUserIdNum(pair.getRight().size());
                    }
                }
            }
        }
        return attendancePublicHolidayRulesVoPageInfo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void add(AttendancePublicHolidayRulesDto publicHolidayRulesDto) throws Exception {
        // 新增的范围为全部清空其他
        if (0 == publicHolidayRulesDto.getScopeOfAdaptation()) {
            publicHolidayRulesMapper.updateScopeOfAdaptation();
        }
        publicHolidayRulesDto.setId(FtbUtil.getId());
        publicHolidayRulesMapper.insert(publicHolidayRulesDto, userProvider.get().getId());
        // 当适配范围为指定组织或成员时绑定适配范围
        List<AttendanceRuleScope> ruleScopeList = ruleScopeUtil.getRuleScopeList(publicHolidayRulesDto.getId(), publicHolidayRulesDto.getScopeOfAdaptation(), MutablePair.of(publicHolidayRulesDto.getOrganizeList(), publicHolidayRulesDto.getUserIdList()), ScopeBizType.PUBLIC_HOLIDAY_RULE);
        ruleScopeUtil.saveBatch(ruleScopeList);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(AttendancePublicHolidayRulesDto publicHolidayRulesDto) throws Exception {
        // 修改的范围为全部清空其他
        if (0 == publicHolidayRulesDto.getScopeOfAdaptation()) {
            publicHolidayRulesMapper.updateScopeOfAdaptation();
        }
        // 查询旧数据
        AttendancePublicHolidayRules oldEntity = publicHolidayRulesMapper.selectById(publicHolidayRulesDto.getId());
        publicHolidayRulesMapper.update(publicHolidayRulesDto, userProvider.get().getId());
        // 当适配范围为指定组织或成员时绑定适配范围
        ruleScopeUtil.updateRuleScopeList(publicHolidayRulesDto.getId(), oldEntity.getScopeOfAdaptation(), publicHolidayRulesDto.getScopeOfAdaptation(), MutablePair.of(publicHolidayRulesDto.getOrganizeList(), publicHolidayRulesDto.getUserIdList()), ScopeBizType.PUBLIC_HOLIDAY_RULE);
    }

    @Override
    public AttendancePublicHolidayRulesVo selectOne(String id) {
        AttendancePublicHolidayRules attendancePublicHolidayRules = publicHolidayRulesMapper.selectById(id);
        // 将attendancePublicHolidayRules转换为AttendancePublicHolidayRulesVo
        AttendancePublicHolidayRulesVo bean = BeanUtil.toBean(attendancePublicHolidayRules, AttendancePublicHolidayRulesVo.class);
        // 当适配范围为指定组织或成员时查询适配范围
        if (bean.getScopeOfAdaptation().equals(1)) {
            MutablePair<List<String>, List<String>> pair = ruleScopeUtil.selectScopeList(bean.getId(), ScopeBizType.PUBLIC_HOLIDAY_RULE);
            if (null != pair) {
                bean.setOrganizeList(pair.getLeft());
                bean.setUserIdList(pair.getRight());
            }
        } else if (Objects.equals(bean.getScopeOfAdaptation(), -1)) {
            bean.setScopeOfAdaptation(null);
        }
        return bean;
    }

    @Override
    public void delete(String id) {
        publicHolidayRulesMapper.deleteById(id);
        // 删除适配范围
        ruleScopeService.remove(new LambdaQueryWrapper<AttendanceRuleScope>()
                .eq(AttendanceRuleScope::getRuleId, id)
                .eq(AttendanceRuleScope::getBizType, ScopeBizType.PUBLIC_HOLIDAY_RULE));
        //

    }

    @Override
    public void updateState(AttendancePublicHolidayRulesDto publicHolidayRulesDto) {
        publicHolidayRulesMapper.updateState(publicHolidayRulesDto.getId(), publicHolidayRulesDto.getState(), userProvider.get().getId());
    }

    /**
     * 获取公休余额列表
     * 公休记录 在封账时触发缓存(缓存公休总额及仅记录发放薪资的天数)，没有缓存的默认为动态的
     *
     * @param yearMonth 年月
     * @param userIds   用户ID集合
     * @return 公休余额列表
     */
    @Override
    public List<AttendancePublicHolidayBalance> getBalanceList(String yearMonth, List<String> userIds) {
        // 公休记录 在封账时触发缓存，没有缓存的默认为动态的
        List<AttendancePublicHolidayBalance> balanceList = publicHolidayRulesMapper.getBalanceList(yearMonth, userIds);
        if (balanceList.isEmpty() || balanceList.size() != userIds.size()) {
            // 找出缺少的用户ID
            List<String> missingUserIds = userIds.stream().filter(v -> balanceList.stream().noneMatch(b -> b.getUserId().equals(v))).collect(Collectors.toList());
            // 动态查询当前年月的公休余额
            if (CollUtil.isEmpty(missingUserIds)) {
                return balanceList;
            }
            List<AttendanceRuleScope> attendanceRuleScopes = ruleScopeUtil.selectUserEffectListBatch(missingUserIds, ScopeBizType.PUBLIC_HOLIDAY_RULE, ConstantUtil.NUM_TRUE);
            Map<String, List<AttendanceRuleScope>> map = attendanceRuleScopes.stream().collect(Collectors.groupingBy(AttendanceRuleScope::getUserId));
            List<AttendancePublicHolidayRules> rules = publicHolidayRulesMapper.selectList(new QueryWrapper<AttendancePublicHolidayRules>()
                    .lambda()
                    .eq(AttendancePublicHolidayRules::getDeleteMark, 0)
                    .eq(AttendancePublicHolidayRules::getState, 1)
                    .in(CollUtil.isNotEmpty(attendanceRuleScopes), AttendancePublicHolidayRules::getId, attendanceRuleScopes.stream().map(AttendanceRuleScope::getRuleId).collect(Collectors.toList()))
            );
            // 根据规则Id 启用的公休规则
            Map<String, AttendancePublicHolidayRules> ruleMap = rules.stream().collect(Collectors.toMap(AttendancePublicHolidayRules::getId, entity -> entity));
            // 没有的用户ID，默认值为0
            missingUserIds.forEach(v -> {
                AttendancePublicHolidayBalance balance = new AttendancePublicHolidayBalance();
                // 计算公休总额
                List<AttendanceRuleScope> userRules = map.get(v);
                if (CollUtil.isNotEmpty(userRules)) {
                    BigDecimal total = BigDecimal.ZERO;
                    for (AttendanceRuleScope ruleScope : userRules) {
                        AttendancePublicHolidayRules rule = ruleMap.get(ruleScope.getRuleId());
                        if (rule != null) {
                            total = total.add(BigDecimal.valueOf(rule.getDayNum()));
                        }
                    }
                    balance.setTotal(total);
                }
                balance.setUserId(v);
                balanceList.add(balance);
            });
        }
        return balanceList;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void processPublicHoliday(String yearMonth, List<String> userIds, Map<String, BigDecimal> ratioMap) {
        // 获取当月有多少天
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM");
        // 解析为 YearMonth 对象
        YearMonth ym = YearMonth.parse(yearMonth, formatter);
        // 获取当月天数
        int daysInMonth = ym.lengthOfMonth();

        // 仅记录
        List<AttendancePublicHolidayBalance> list = new ArrayList<>();
        // 需要发劵的用户余额记录
        List<AttendanceBalanceRecordEntity> balanceRecordList = new ArrayList<>();
        // 查询用户对应的公休规则
        List<AttendanceRuleScope> attendanceRuleScopes = ruleScopeUtil.selectUserEffectListBatch(userIds, ScopeBizType.PUBLIC_HOLIDAY_RULE, ConstantUtil.NUM_TRUE);
        Map<String, AttendanceRuleScope> map = attendanceRuleScopes.stream().collect(Collectors.toMap(AttendanceRuleScope::getUserId, entity -> entity));
        List<AttendancePublicHolidayRules> rules = publicHolidayRulesMapper.selectList(new QueryWrapper<AttendancePublicHolidayRules>()
                .lambda()
                .eq(AttendancePublicHolidayRules::getDeleteMark, 0)
                .eq(AttendancePublicHolidayRules::getState, 1)
                .in(CollUtil.isNotEmpty(attendanceRuleScopes), AttendancePublicHolidayRules::getId, attendanceRuleScopes.stream().map(AttendanceRuleScope::getRuleId).collect(Collectors.toList()))
        );
        // 根据规则Id 启用的公休规则
        Map<String, AttendancePublicHolidayRules> ruleMap = rules.stream().collect(Collectors.toMap(AttendancePublicHolidayRules::getId, entity -> entity));
        // 获取用户当月使用的公休天数
        Map<String, BigDecimal> userPublicHoliday = dailyRuleService.getUserPublicHoliday(yearMonth, userIds);
        // 没有的用户ID，默认值为0
        userIds.forEach(v -> {
            AttendancePublicHolidayBalance balance = new AttendancePublicHolidayBalance();
            balance.setId(FtbUtil.getId());
            balance.setUserId(v);
            // 计算公休总额  理论上只会命中一个userRules
            AttendanceRuleScope userRules = map.get(v);
            if (null != userRules) {
                AttendancePublicHolidayRules rule = ruleMap.get(userRules.getRuleId());
                if (rule != null) {
                    // 考虑月份大小，最大值为当月的天数
                    balance.setTotal(daysInMonth > rule.getDayNum() ? BigDecimal.valueOf(rule.getDayNum()) : BigDecimal.valueOf(daysInMonth));
                    balance.setTransformationType(rule.getTransformationType());
                    balance.setRuleId(rule.getId());
                    balance.setYearMonth(yearMonth);
                    BigDecimal use = userPublicHoliday.get(v);
                    // 总数-使用了的，剩余数小于0 取0
                    balance.setBalance(balance.getTotal().subtract(null == use ? BigDecimal.ZERO : use).max(BigDecimal.ZERO));
                    balance.setAttendanceRatio(ratioMap.get(v));
                    list.add(balance);
                }
            }
        });
        // 移除已经有记录的用户
        List<String> uIds = publicHolidayRulesMapper.selectPublicHolidayBalanceList(yearMonth, userIds);
        List<AttendancePublicHolidayBalance> list1 = list;
        if (CollUtil.isNotEmpty(uIds)) {
            list1 = list.stream().filter(v -> !uIds.contains(v.getUserId())).collect(Collectors.toList());
        }
        // 批量添加公休余额
        if (CollUtil.isNotEmpty(list1)) {
            publicHolidayRulesMapper.batchAddPublicHolidayBalance(list1, yearMonth);
            // 过滤出需要转存休且余额大于0的数据
            List<AttendancePublicHolidayBalance> needRetirementLeave = list1.stream().filter(v -> v.getTransformationType() == 0 && v.getBalance().compareTo(BigDecimal.ZERO) > 0).collect(Collectors.toList());
            if (CollUtil.isNotEmpty(needRetirementLeave)) {
                attendanceUserBalanceService.addUserBalances(needRetirementLeave);
                // 组装劵信息
                needRetirementLeave.forEach(v -> {
                    AttendanceBalanceRecordEntity record = new AttendanceBalanceRecordEntity();
                    record.setUserId(v.getUserId());
                    record.setType(3);
                    record.setBalance(v.getBalance());
                    record.setTotal(v.getBalance());
                    record.setUnit(2);
                    //计算过期时间
                    AttendancePublicHolidayRules rule = ruleMap.get(v.getRuleId());
                    record.setExpireTime(expiresTimeUtil.getExpiresTime(rule.getLifespanType(), rule.getFixedDay(), rule.getSpecifyDay()));
                    balanceRecordList.add(record);
                });
                // 发放劵
                attendanceUserBalanceRecordService.saveBatch(balanceRecordList);
            }
        }
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void rollbackPublicHoliday(String yearMonth, List<String> userIds) {
        // 查出对应的公休余额列表
        List<AttendancePublicHolidayBalance> publicHolidayBalanceList = publicHolidayRulesMapper.getPublicHolidayBalanceList(yearMonth, userIds);
        // 过滤出转存存休且余额大于0的数据
        List<AttendancePublicHolidayBalance> needRetirementLeave = publicHolidayBalanceList.stream().filter(v -> v.getTransformationType() == 0 && v.getBalance().compareTo(BigDecimal.ZERO) > 0).collect(Collectors.toList());
        if (CollUtil.isNotEmpty(needRetirementLeave)) {
            // 转存休数据删除
            attendanceUserBalanceService.rollbackUserBalance(needRetirementLeave);
            // 回退对应的劵数据
            attendanceUserBalanceRecordService.rollbackUserBalanceRecord(needRetirementLeave, null, null, true);
        }
        // 删除对应数据
        publicHolidayRulesMapper.deletePublicHolidayBalance(yearMonth, userIds);
    }


}
