package jnpf.attendance.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import jnpf.attendance.mapper.AttendanceGroupMapper;
import jnpf.attendance.mapper.OvertimeRuleMapper;
import jnpf.attendance.service.AttendanceFestivalRulesService;
import jnpf.attendance.service.OvertimeRuleDetailService;
import jnpf.attendance.service.OvertimeRuleService;
import jnpf.attendance.service.RuleScopeService;
import jnpf.base.service.SuperServiceImpl;
import jnpf.entity.attendance.AttendanceOvertimeRule;
import jnpf.entity.attendance.AttendanceOvertimeRuleDetail;
import jnpf.entity.attendance.AttendanceRuleScope;
import jnpf.enums.attendance.AttendanceTypeEnum;
import jnpf.enums.attendance.OvertimeType;
import jnpf.enums.attendance.ScopeBizType;
import jnpf.exception.HandleException;
import jnpf.model.attendance.dto.FestivalDto;
import jnpf.model.attendance.dto.OvertimeRuleDetailDto;
import jnpf.model.attendance.dto.OvertimeRuleDto;
import jnpf.model.attendance.dto.OvertimeRuleQueryDto;
import jnpf.model.attendance.vo.attendance.OvertimeRuleDetailVo;
import jnpf.model.attendance.vo.attendance.OvertimeRulePageVo;
import jnpf.model.attendance.vo.attendance.OvertimeRuleVo;
import jnpf.util.*;
import jnpf.util.attendance.RuleScopeUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.MutablePair;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 加班规则服务实现
 *
 * @author yanwenfu
 * @create 2025-09-18
 */
@Service
public class OvertimeRuleServiceImpl extends SuperServiceImpl<OvertimeRuleMapper, AttendanceOvertimeRule> implements OvertimeRuleService {

    @Resource
    private OvertimeRuleMapper overtimeRuleMapper;
    @Resource
    private OvertimeRuleDetailService overtimeRuleDetailService;
    @Resource
    private RuleScopeService ruleScopeService;
    @Resource
    private RuleScopeUtil ruleScopeUtil;
    @Resource
    private AttendanceFestivalRulesService festivalRulesService;
    @Resource
    private AttendanceGroupMapper attendanceGroupMapper;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void addOvertimeRule(OvertimeRuleDto overtimeRuleDto) throws HandleException {

        // 生成主表信息
        AttendanceOvertimeRule overtimeRule = getAttendanceOvertimeRule(overtimeRuleDto);
        // 生成数据范围信息(全部则无value)
        List<AttendanceRuleScope> ruleScopeList = ruleScopeUtil.getRuleScopeList(overtimeRule.getId(), overtimeRuleDto.getRuleScope(),
                MutablePair.of(overtimeRuleDto.getOrganizeList(), overtimeRuleDto.getUserIdList()), ScopeBizType.OVERTIME_RULE);
        // 生成子表信息
        List<AttendanceOvertimeRuleDetail> detailList = getOvertimeDetailList(overtimeRule.getId(), overtimeRuleDto.getList());
        this.save(overtimeRule);
        if (!ruleScopeList.isEmpty()) {
            ruleScopeUtil.saveBatch(ruleScopeList);
        }
        if (!detailList.isEmpty()) {
            overtimeRuleDetailService.saveBatch(detailList);
        }
    }

    @NotNull
    private static AttendanceOvertimeRule getAttendanceOvertimeRule(OvertimeRuleDto overtimeRuleDto) {

        AttendanceOvertimeRule overtimeRule = new AttendanceOvertimeRule();
        overtimeRule.setId(FtbUtil.getId());
        overtimeRule.setRuleName(overtimeRuleDto.getRuleName());
        overtimeRule.setRuleScope(overtimeRuleDto.getRuleScope());
        overtimeRule.setEnabled(ConstantUtil.NUM_FALSE);
        overtimeRule.setCreatorUserId(UserProvider.getLoginUserId());
        overtimeRule.setLastModifyUserId(UserProvider.getLoginUserId());
        overtimeRule.setDeleteMark(ConstantUtil.NUM_FALSE);
        return overtimeRule;
    }

    private List<AttendanceOvertimeRuleDetail> getOvertimeDetailList(String ruleId, List<OvertimeRuleDetailDto> list) {

        List<AttendanceOvertimeRuleDetail> returnList = new ArrayList<>();
        list.forEach(v -> {
            AttendanceOvertimeRuleDetail detail = JsonUtil.getJsonToBean(v, AttendanceOvertimeRuleDetail.class);
            if (StringUtils.isEmpty(detail.getId())) detail.setId(FtbUtil.getId());
            if (StringUtils.isEmpty(detail.getRuleId())) detail.setRuleId(ruleId);
            detail.setLastModifyUserId(UserProvider.getLoginUserId());
            returnList.add(detail);
        });
        return returnList;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateOvertimeRule(String id, OvertimeRuleDto overtimeRuleDto) throws HandleException {

        // 查询数据库主表信息
        AttendanceOvertimeRule overtimeRule = this.getById(id);
        if (null == overtimeRule) {
            throw new HandleException("未查询到加班规则");
        }
        Integer oldScope = overtimeRule.getRuleScope();
        Integer newScope = overtimeRuleDto.getRuleScope();
        // 更新主表字段
        overtimeRule.setRuleName(overtimeRuleDto.getRuleName());
        overtimeRule.setRuleScope(overtimeRuleDto.getRuleScope());
        overtimeRule.setLastModifyTime(DateUtil.getNowDate());
        overtimeRule.setLastModifyUserId(UserProvider.getLoginUserId());
        this.updateById(overtimeRule);
        // 更新适配范围
        ruleScopeUtil.updateRuleScopeList(id, oldScope, newScope, MutablePair.of(overtimeRuleDto.getOrganizeList(), overtimeRuleDto.getUserIdList()), ScopeBizType.OVERTIME_RULE);
        // 更新子表字段
        List<AttendanceOvertimeRuleDetail> detailList = getOvertimeDetailList(overtimeRule.getId(), overtimeRuleDto.getList());
        if (!detailList.isEmpty()) {
            overtimeRuleDetailService.updateBatchById(detailList);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void deleteOvertimeRule(String id) {

        // 主表标识删除
        this.update(new LambdaUpdateWrapper<AttendanceOvertimeRule>()
                .set(AttendanceOvertimeRule::getDeleteMark, ConstantUtil.NUM_TRUE)
                .set(AttendanceOvertimeRule::getDeleteTime, DateUtil.getNowDate())
                .set(AttendanceOvertimeRule::getDeleteUserId, UserProvider.getLoginUserId())
                .eq(AttendanceOvertimeRule::getId, id));
        // 适配范围直接删除
        ruleScopeService.remove(new LambdaQueryWrapper<AttendanceRuleScope>()
                .eq(AttendanceRuleScope::getRuleId, id));
    }

    @Override
    public void updateEnableStatus(String id) throws HandleException {

        AttendanceOvertimeRule overtimeRule = this.getById(id);
        if (null == overtimeRule) {
            throw new HandleException("未找到加班规则");
        }
        this.update(new LambdaUpdateWrapper<AttendanceOvertimeRule>()
                .set(AttendanceOvertimeRule::getEnabled, overtimeRule.getEnabled().equals(ConstantUtil.NUM_TRUE) ? ConstantUtil.NUM_FALSE : ConstantUtil.NUM_TRUE)
                .set(AttendanceOvertimeRule::getLastModifyTime, DateUtil.getNowDate())
                .set(AttendanceOvertimeRule::getLastModifyUserId, UserProvider.getLoginUserId())
                .eq(AttendanceOvertimeRule::getId, id));
    }

    @Override
    public OvertimeRuleVo getDetail(String id) {

        AttendanceOvertimeRule overtimeRule = this.getById(id);
        return setDetailInfo(overtimeRule);
    }

    private OvertimeRuleVo setDetailInfo(AttendanceOvertimeRule overtimeRule) {
        // 查主表
        OvertimeRuleVo vo = JsonUtil.getJsonToBean(overtimeRule, OvertimeRuleVo.class);
        // 查子表
        List<AttendanceOvertimeRuleDetail> detailList = overtimeRuleDetailService.list(new LambdaQueryWrapper<AttendanceOvertimeRuleDetail>()
                .eq(AttendanceOvertimeRuleDetail::getRuleId, vo.getId()));
        List<OvertimeRuleDetailVo> voDetailList = JsonUtil.getJsonToList(detailList, OvertimeRuleDetailVo.class);
        vo.getList().addAll(voDetailList);
        // 查适配范围
        MutablePair<List<String>, List<String>> data = ruleScopeUtil.selectScopeList(vo.getId(), ScopeBizType.OVERTIME_RULE);
        if (null != data) {
            if (data.getLeft().isEmpty() && data.getRight().isEmpty()) {
                // 无适配范围
                vo.setRuleScope(-1);
            } else {
                vo.getOrganizeList().addAll(data.getLeft());
                vo.getUserIdList().addAll(data.getRight());
            }
        }
        return vo;
    }

    @Override
    public OvertimeRuleVo getActiveDetail(String ruleId) {

        AttendanceOvertimeRule overtimeRule = this.getById(ruleId);
        if (overtimeRule.getEnabled().equals(ConstantUtil.NUM_FALSE)) {
            return null;
        }
        OvertimeRuleVo ruleVo = setDetailInfo(overtimeRule);
        List<OvertimeRuleDetailVo> collect = ruleVo.getList().stream().filter(v -> v.getEnabled().equals(ConstantUtil.NUM_TRUE)).collect(Collectors.toList());
        ruleVo.getList().clear();
        ruleVo.setList(collect);
        return ruleVo;
    }

    @Override
    public OvertimeRuleVo getEffectDetail(String userId, Integer attendanceType, Date date) {

        // 查询用户加班配置
        OvertimeRuleVo overtimeRule = null;
        List<AttendanceRuleScope> scopeList = ruleScopeUtil.selectUserEffectList(userId, ScopeBizType.OVERTIME_RULE, UserProvider.getUser().getTenantId());
        if (!scopeList.isEmpty()) {
            AttendanceRuleScope scope = scopeList.get(0);
            overtimeRule = getActiveDetail(scope.getRuleId());
        }
        if (null != overtimeRule) {
            // 查询昨天是否节假日
            Map<String, Map<String, FestivalDto>> map = festivalRulesService.checkFestivalRulesBatch(date, List.of(userId));
            Map<String, FestivalDto> checkMap = map.getOrDefault(userId, getDefaultMap(date));
            OvertimeRuleDetailVo vo = getOvertimeRuleDetail(overtimeRule, checkMap.get(DateDetail.getDate2Str(date, DateDetail.DF)), attendanceType);
            overtimeRule.setFestival(checkMap);
            overtimeRule.setEffectRuleDetail(vo);
        }
        return overtimeRule;
    }

    @Override
    public OvertimeRuleVo getGroupOvertimeRule(String groupId) {

        String orgId = attendanceGroupMapper.getOrgId(groupId);
        List<AttendanceRuleScope> scopeList = ruleScopeUtil.selectUserEffectList(null, orgId, ScopeBizType.OVERTIME_RULE);
        // 查询用户加班配置
        OvertimeRuleVo overtimeRule = null;
        if (!scopeList.isEmpty()) {
            AttendanceRuleScope scope = scopeList.get(0);
            overtimeRule = getActiveDetail(scope.getRuleId());
        }
        return overtimeRule;
    }

    @Override
    public Map<String, OvertimeRuleVo> getEffectDetailByUserList(List<String> userIds, Date date) {

        // 查询用户加班配置
        List<AttendanceRuleScope> scopeList = ruleScopeUtil.selectUserEffectListBatch(userIds, ScopeBizType.OVERTIME_RULE, ConstantUtil.NUM_TRUE);
        if (scopeList.isEmpty()) {
            return new HashMap<>();
        }
        ConcurrentMap<String, List<AttendanceRuleScope>> scopeMap = scopeList.stream().collect(Collectors.groupingByConcurrent(AttendanceRuleScope::getRuleId));
        LambdaQueryWrapper<AttendanceOvertimeRule> overtimeRuleQuery = new LambdaQueryWrapper<AttendanceOvertimeRule>()
                .in(AttendanceOvertimeRule::getId, scopeMap.keySet())
                .eq(AttendanceOvertimeRule::getEnabled, ConstantUtil.NUM_TRUE)
                .eq(AttendanceOvertimeRule::getDeleteMark, ConstantUtil.NUM_FALSE);
        // 查询生效中的加班规则
        List<AttendanceOvertimeRule> overtimeList = this.list(overtimeRuleQuery);
        if (overtimeList.isEmpty()) {
            return new HashMap<>();
        }
        // 查询生效中的加班规则详情
        Map<String, AttendanceOvertimeRule> ruleMap = overtimeList.stream().collect(Collectors.toMap(AttendanceOvertimeRule::getId, Function.identity()));
        List<AttendanceOvertimeRuleDetail> detailList = overtimeRuleDetailService.list(new LambdaQueryWrapper<AttendanceOvertimeRuleDetail>()
                .in(AttendanceOvertimeRuleDetail::getRuleId, ruleMap.keySet())
                .eq(AttendanceOvertimeRuleDetail::getEnabled, ConstantUtil.NUM_TRUE));
        ConcurrentMap<String, List<OvertimeRuleDetailVo>> detailMap;
        if (!detailList.isEmpty()) {
            List<OvertimeRuleDetailVo> voDetailList = JsonUtil.getJsonToList(detailList, OvertimeRuleDetailVo.class);
            detailMap = voDetailList.stream().collect(Collectors.groupingByConcurrent(OvertimeRuleDetailVo::getRuleId));
        } else {
            detailMap = new ConcurrentHashMap<>();
        }
        // 判定节假日
        Map<String, Map<String, FestivalDto>> checkMap = festivalRulesService.checkFestivalRulesBatch(date, userIds);
        // 返回结果
        Map<String, OvertimeRuleVo> returnMap = new HashMap<>();
        ruleMap.forEach((k, v) -> {
            List<AttendanceRuleScope> list = scopeMap.get(k);
            List<String> users = list.stream().map(AttendanceRuleScope::getUserId).collect(Collectors.toList());
            OvertimeRuleVo vo = JsonUtil.getJsonToBean(v, OvertimeRuleVo.class);
            List<OvertimeRuleDetailVo> dList = detailMap.getOrDefault(k, List.of());
            vo.setList(dList);
            users.forEach(user -> {
                Map<String, FestivalDto> check = checkMap.getOrDefault(user, getDefaultMap(date));
                vo.setFestival(check);
                returnMap.put(user, vo);
            });
        });
        return returnMap;
    }

    private Map<String, FestivalDto> getDefaultMap(Date date) {

        Map<String, FestivalDto> map = new HashMap<>();
        DateDetail dateDetail = new DateDetail(date);
        String endDate = DateDetail.getDate2Str(dateDetail.getCurrentDate(), DateDetail.DF);
        dateDetail.getYesterday();
        String startDate = DateDetail.getDate2Str(dateDetail.getCurrentDate(), DateDetail.DF);
        map.put(endDate, new FestivalDto(Boolean.FALSE));
        map.put(startDate, new FestivalDto(Boolean.FALSE));
        return map;
    }

    @Override
    public OvertimeRuleDetailVo getEffectWorkDetail(OvertimeRuleVo rule, Date date, Integer attendanceType) {
        String day = DateDetail.getDate2Str(date, DateDetail.DF);
        return getOvertimeRuleDetail(rule, rule.getFestival().get(day), attendanceType);
    }

    private OvertimeRuleDetailVo getOvertimeRuleDetail(OvertimeRuleVo overtimeRule, FestivalDto festival, Integer attendanceType) {

        if (null == overtimeRule || null == attendanceType) {
            return null;
        }
        if (festival.isFestival()) {
            OvertimeRuleDetailVo overtimeRuleDetailVo = overtimeRule.getList().stream().filter(v -> v.getOvertimeType().equals(OvertimeType.HOLIDAY.getValue())).findFirst().orElse(null);
            if (null != overtimeRuleDetailVo){
                overtimeRuleDetailVo.setFestivalStr(festival.getFestivalStr());
            }
            return overtimeRuleDetailVo;
        }
        if (attendanceType.equals(AttendanceTypeEnum.ORDINARY.getCode())) {
            return overtimeRule.getList().stream().filter(v -> v.getOvertimeType().equals(OvertimeType.WORK_DAY.getValue())).findFirst().orElse(null);
        }
        if (attendanceType.equals(AttendanceTypeEnum.REST.getCode())) {
            return overtimeRule.getList().stream().filter(v -> v.getOvertimeType().equals(OvertimeType.REST_DAY.getValue())).findFirst().orElse(null);
        }
        return null;
    }

    @Override
    public PageInfo<OvertimeRulePageVo> getPage(OvertimeRuleQueryDto queryDto) {

        PageHelper.startPage(queryDto.getCurrentPage(), queryDto.getPageSize());
        return new PageInfo<>(overtimeRuleMapper.selectOvertimeRulePage(queryDto.getRuleName()));
    }
}
