package jnpf.personnels.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import com.fantaibao.permission.handling.PermissionHandling;
import com.google.common.collect.Maps;
import jnpf.base.ActionResult;
import jnpf.base.UserInfo;
import jnpf.base.entity.SuperBaseEntity;
import jnpf.entity.workflow.PunishmentsApproval;
import jnpf.entity.workflow.RewardApproval;
import jnpf.model.personnels.bo.FtbRewardsImportRedisBO;
import jnpf.model.personnels.dto.rewardspunishments.*;
import jnpf.model.personnels.dto.salary.FtbSalaryMetaDataQueryDto;
import jnpf.model.personnels.po.FtbPersonnelsRewardsPunishments;
import jnpf.model.personnels.vo.rewardspunishments.*;
import jnpf.permission.V2UserApi;
import jnpf.permission.vo.v2.user.UserBoundVO;
import jnpf.personnels.listeners.BaseEasyExcelCommonListener;
import jnpf.personnels.mapper.FtbPersonnelsRewardsPunishmentsMapper;
import jnpf.personnels.mapper.FtbPersonnelsStaffRosterMapper;
import jnpf.personnels.service.FtbPersonnelsRewardsPunishmentsService;
import jnpf.util.RedisUtil;
import jnpf.util.UserProvider;
import jnpf.util.excel.EasyExcelUtils;
import jnpf.workflow.mapper.PunishmentsApprovalMapper;
import jnpf.workflow.mapper.RewardApprovalMapper;
import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 人事奖惩表 服务实现类
 * </p>
 *
 * @author wcx
 * @since 2024-05-08
 */
@Service
public class FtbPersonnelsRewardsPunishmentsServiceImpl extends ServiceImpl<FtbPersonnelsRewardsPunishmentsMapper, FtbPersonnelsRewardsPunishments> implements FtbPersonnelsRewardsPunishmentsService {

    @Autowired
    private V2UserApi v2UserApi;
    @Resource
    private FtbPersonnelsStaffRosterMapper ftbPersonnelsStaffRosterMapper;

    @Resource(name = "ftbPersonnelsRewardsService")
    private BaseEasyExcelCommonListener<FtbEmploymentRewardExcelVO, FtbEmploymentRecordsExcelERRVO> baseEasyExcelCommonListener;

    @Resource
    private RedisUtil redisUtil;

    @Resource
    private PermissionHandling permissionHandling;

    public static final String R_REDIS_KEY = "ftb:personnels:rewards:punishment:%s";

    @Resource
    RewardApprovalMapper rewardApprovalMapper;

    @Resource
    PunishmentsApprovalMapper punishmentsApprovalMapper;

    @Override
    public synchronized void add(FtbPersonnelsRewardsPunishmentsAddDTO ftbPersonnelsRewardsPunishmentsAddDTO) {
        // 校验名称重复
        LambdaQueryWrapper<FtbPersonnelsRewardsPunishments> query = Wrappers.lambdaQuery();
        query.eq(FtbPersonnelsRewardsPunishments::getEnableMark, 0);
        query.eq(FtbPersonnelsRewardsPunishments::getType, ftbPersonnelsRewardsPunishmentsAddDTO.getType());
        query.eq(FtbPersonnelsRewardsPunishments::getRegularName, ftbPersonnelsRewardsPunishmentsAddDTO.getRegularName());
        if (baseMapper.selectCount(query) > 0) {
            throw new RuntimeException("类型名称不允许重复");
        }
        FtbPersonnelsRewardsPunishments ftbPersonnelsRewardsPunishments = FtbPersonnelsRewardsPunishmentsAddDTO.convertFtbPersonnelsRewardsPunishments(ftbPersonnelsRewardsPunishmentsAddDTO);
        ftbPersonnelsRewardsPunishments.setLastModifyTime(new Date());
        ftbPersonnelsRewardsPunishments.setLastModifyUserId(UserProvider.getUser().getUserId());
        // 排序计算
        Integer sorting = baseMapper.queryTheMaximumValueOfSorting();
        ftbPersonnelsRewardsPunishments.setSort(sorting + 1);
        baseMapper.insert(ftbPersonnelsRewardsPunishments);
    }

    @Override
    @Transactional
    public void delete(String id) {
        FtbPersonnelsRewardsPunishments ftbPersonnelsRewardsPunishments = baseMapper.selectById(id);
        Integer sorting = baseMapper.queryTheMaximumValueOfSorting();
        // 所有小于该sort值的数据-1
        if (!ftbPersonnelsRewardsPunishments.getSort().equals(sorting)) {
            baseMapper.updateSortValue(ftbPersonnelsRewardsPunishments.getSort());
        }
        LambdaUpdateWrapper<FtbPersonnelsRewardsPunishments> updateWrapper = Wrappers.lambdaUpdate();
        updateWrapper.set(FtbPersonnelsRewardsPunishments::getEnableMark, 1);
        updateWrapper.eq(SuperBaseEntity.SuperIBaseEntity::getId, id);
        baseMapper.update(new FtbPersonnelsRewardsPunishments(), updateWrapper);
    }

    @Override
    public void updateData(FtbPersonnelsRewardsPunishmentsUpdateDTO ftbPersonnelsRewardsPunishmentsUpdateDTO) {
        LambdaUpdateWrapper<FtbPersonnelsRewardsPunishments> updateWrapper = Wrappers.lambdaUpdate();
        updateWrapper.eq(SuperBaseEntity.SuperIBaseEntity::getId, ftbPersonnelsRewardsPunishmentsUpdateDTO.getId());
        updateWrapper.set(FtbPersonnelsRewardsPunishments::getRegularName, ftbPersonnelsRewardsPunishmentsUpdateDTO.getRegularName());
        updateWrapper.set(FtbPersonnelsRewardsPunishments::getMoney, ftbPersonnelsRewardsPunishmentsUpdateDTO.getMoney());
        updateWrapper.set(FtbPersonnelsRewardsPunishments::getIsRecorded, ftbPersonnelsRewardsPunishmentsUpdateDTO.getIsRecorded());
        updateWrapper.set(FtbPersonnelsRewardsPunishments::getType, ftbPersonnelsRewardsPunishmentsUpdateDTO.getType());
        baseMapper.update(new FtbPersonnelsRewardsPunishments(), updateWrapper);
    }

    @Override
    public void startAndStop(FtbPersonnelsRewardsPunishmentStartDTO ftbPersonnelsRewardsPunishmentStartDTO) {
        LambdaUpdateWrapper<FtbPersonnelsRewardsPunishments> updateWrapper = Wrappers.lambdaUpdate();
        updateWrapper.eq(SuperBaseEntity.SuperIBaseEntity::getId, ftbPersonnelsRewardsPunishmentStartDTO.getId());
        updateWrapper.set(FtbPersonnelsRewardsPunishments::getStatus, ftbPersonnelsRewardsPunishmentStartDTO.getStatus());
        baseMapper.update(new FtbPersonnelsRewardsPunishments(), updateWrapper);
    }

    @Override
    @Transactional
    public void exchangeOrder(FtbPersonnelsRewardsPunishmentExchangeOrderDTO ftbPersonnelsRewardsPunishmentExchangeOrderDTO) {
        // 点击数据，即拖动
        doExchangeOrder(ftbPersonnelsRewardsPunishmentExchangeOrderDTO.getFirstExchangeInner());
        // 从下往上点
        if (ftbPersonnelsRewardsPunishmentExchangeOrderDTO.getFirstExchangeInner().getSort() <
                ftbPersonnelsRewardsPunishmentExchangeOrderDTO.getSecondExchangeInner().getSort()) {
            // 点击的数据做递增
            baseMapper.updateSortDownward(ftbPersonnelsRewardsPunishmentExchangeOrderDTO.getFirstExchangeInner().getSort(),
                    ftbPersonnelsRewardsPunishmentExchangeOrderDTO.getSecondExchangeInner().getSort(),
                    ftbPersonnelsRewardsPunishmentExchangeOrderDTO.getFirstExchangeInner().getId());
        } else {
            // 从上往下点
            baseMapper.updateSortDownwardS(ftbPersonnelsRewardsPunishmentExchangeOrderDTO.getSecondExchangeInner().getSort(),
                    ftbPersonnelsRewardsPunishmentExchangeOrderDTO.getFirstExchangeInner().getSort(),
                    ftbPersonnelsRewardsPunishmentExchangeOrderDTO.getFirstExchangeInner().getId());
        }
    }

    @Override
    public List<FtbPersonnelsRewardsPunishmentQueryVO> listQuery(FtbPersonnelsRewardsPunishmentQueryDTO rewardsPunishmentQueryDTO) {
        List<FtbPersonnelsRewardsPunishmentQueryVO> result = baseMapper.listQuery(rewardsPunishmentQueryDTO);
        List<String> userIds = result.stream().map(FtbPersonnelsRewardsPunishmentQueryVO::getLastModifyUserId).collect(Collectors.toList());
        ActionResult<List<UserBoundVO>> userPrimaryBoundBatch = v2UserApi.getUserPrimaryBoundBatch(userIds, null);
        if (userPrimaryBoundBatch == null || CollUtil.isEmpty(userPrimaryBoundBatch.getData())) {
            return result;
        }
        Map<String, UserBoundVO> userBoundVOMap = userPrimaryBoundBatch.getData().stream().collect(Collectors.toMap(UserBoundVO::getId, v -> v));
        for (FtbPersonnelsRewardsPunishmentQueryVO ftbPersonnelsRewardsPunishmentQueryVO : result) {
            UserBoundVO userBoundVO = userBoundVOMap.get(ftbPersonnelsRewardsPunishmentQueryVO.getLastModifyUserId());
            if (Objects.nonNull(userBoundVO)) {
                ftbPersonnelsRewardsPunishmentQueryVO.setUserName(userBoundVO.getUserName());
            }
        }
        return result;
    }

    @Override
    public List<FtbPersonnelsRewardsPunishmentApprovalVO> listApproval(Integer type) {
        LambdaQueryWrapper<FtbPersonnelsRewardsPunishments> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(FtbPersonnelsRewardsPunishments::getEnableMark, 0);
        queryWrapper.eq(FtbPersonnelsRewardsPunishments::getStatus, 0);
        queryWrapper.eq(type != null,FtbPersonnelsRewardsPunishments::getType, type);
        queryWrapper.orderByAsc(FtbPersonnelsRewardsPunishments::getSort);
        List<FtbPersonnelsRewardsPunishments> ftbPersonnelsRewardsPunishments = baseMapper.selectList(queryWrapper);
        return ftbPersonnelsRewardsPunishments.stream().map(FtbPersonnelsRewardsPunishmentApprovalVO::convert).collect(Collectors.toList());
    }

    @Override
    public List<FtbPersonnelSalaryRewardVO> salaryReward(FtbPersonnelSalaryRewardDTO ftbPersonnelSalaryRewardDTO) {
        return this.baseMapper.queryTheTotalSalaryAndRewards(ftbPersonnelSalaryRewardDTO);
    }

    @Override
    public List<FtbPersonnelSalaryRewardVO> totalSalaryPenalty(FtbPersonnelSalaryRewardDTO ftbPersonnelSalaryRewardDTO) {
        return this.baseMapper.queryTheTotalSalaryAndPenaltys(ftbPersonnelSalaryRewardDTO);
    }

    @Override
    public FtbPersonnelsRewardsPunishmentApprovalVO listApprovalMoney(String id) {
        FtbPersonnelsRewardsPunishments ftbPersonnelsRewardsPunishments = baseMapper.selectById(id);
        return FtbPersonnelsRewardsPunishmentApprovalVO.convert(ftbPersonnelsRewardsPunishments);
    }

    @Override
    public BigDecimal salaryMetaDataQuery(FtbSalaryMetaDataQueryDto dto) {
        BigDecimal value = baseMapper.salaryMetaDataQuery(dto);
        return Optional.ofNullable(value)
                .orElse(BigDecimal.ZERO);
    }

    @Override
    public List<FtbXcEmployeeRewardRecordsVO> salaryBatchMetaDataQuery(FtbSalaryMetaDataQueryDto dto) {
        return baseMapper.salaryBatchMetaDataQuery(dto);
    }

    @Override
    public Page<FtbEmployeeRewardRecordsVO> employeeRewardAndPunishmentRecords(Page<FtbEmployeeRewardRecordsVO> page, FtbEmployeeRewardRecordsQueryDTO queryDTO) {
        UserInfo userInfo = UserProvider.getUser();
        if (!userInfo.getIsAdministrator()) {
            List<String> userIdsByUserId = permissionHandling.getUserIdsByUserId(userInfo.getUserId());
            if (userIdsByUserId != null && userIdsByUserId.isEmpty()) {
                return page;
            }
            queryDTO.setUserIds(userIdsByUserId);
        }
        Page<FtbEmployeeRewardRecordsVO> ftbEmployeeRewardRecordsVOPage = baseMapper.employeeRewardAndPunishmentRecords(page, queryDTO);
        if (ftbEmployeeRewardRecordsVOPage.getRecords().isEmpty()) {
            return ftbEmployeeRewardRecordsVOPage;
        }
        // 组织、岗位，操作人姓名
        List<String> operatorsIds = ftbEmployeeRewardRecordsVOPage.getRecords().stream()
                .map(FtbEmployeeRewardRecordsVO::getOperator)
                .collect(Collectors.toList());
        ActionResult<List<UserBoundVO>> userPrimaryBoundBatch = v2UserApi.getUserPrimaryBoundBatch(operatorsIds, null);
        // 检查远程调用状态
        if (userPrimaryBoundBatch == null) {
            throw new RuntimeException("远程获取用户信息失败");
        }
        Map<String, String> operatorMaps = userPrimaryBoundBatch.getData().stream().collect(Collectors.toMap(UserBoundVO::getId, UserBoundVO::getUserName));
        ftbEmployeeRewardRecordsVOPage.getRecords().forEach(a -> {
            a.setOperator(operatorMaps.getOrDefault(a.getOperator(), "--"));
        });
        return ftbEmployeeRewardRecordsVOPage;
    }

    @Override
    public Map<String, Object> importData(InputStream inputStream) throws IOException {
        EasyExcelUtils.universalImport(FtbEmploymentRewardExcelVO.class,
                inputStream,
                baseEasyExcelCommonListener,
                0,2);
        String redisKey = getRedisKey();
        if (!redisUtil.exists(redisKey)) {
            throw new RuntimeException("导入数据已经过期,请重新导入!");
        }
        FtbRewardsImportRedisBO importRedisBO = getRedisBO(redisKey);
        HashMap<String, Object> map = Maps.newHashMap();
        map.put("normalData", importRedisBO.getNormalData().size());
        map.put("errorData", importRedisBO.getErrorData().size());
        map.put("data", importRedisBO);
        return map;
    }

    @Override
    public void importDataClick(Integer type, String orgId) {
        String redisKey = getRedisKey();
        if (!redisUtil.exists(redisKey)) {
            throw new RuntimeException("导入数据已经过期,请重新导入!");
        }
        FtbRewardsImportRedisBO importRedisBO = getRedisBO(redisKey);
        List<FtbEmploymentRewardExcelVO> normalData = importRedisBO.getNormalData();
        if (CollUtil.isEmpty(normalData)) {
            throw new RuntimeException("当前暂无数据导入!");
        }
        List<FtbEmploymentRewardExcelVO> list = new ArrayList<>();
        for (FtbEmploymentRewardExcelVO item : normalData){
           if (StringUtils.isNotEmpty(item.getCurrOrgId()) && orgId.equals(item.getCurrOrgId())){
               list.add(item);
           }else {
               list.add(item);
           }
       }
        if (CollUtil.isEmpty(list) && !normalData.isEmpty()){
            throw new RuntimeException("当前选择的组织下暂无导入数据!");
        }
        // 0奖励，1惩罚
        if (type == 0){
            List<RewardApproval> approvals = list.stream()
                    .filter(a -> a.getType().equals("奖励") ).map(FtbEmploymentRewardExcelVO::convertReward).collect(Collectors.toList());
            Db.saveBatch(approvals);
        }else {
            List<PunishmentsApproval> approvals = list.stream()
                    .filter(a -> a.getType().equals("乐捐")).map(FtbEmploymentRewardExcelVO::convertDonate).collect(Collectors.toList());
            Db.saveBatch(approvals);
        }

    }

    @Override
    public void saveDataToRedis(List<FtbEmploymentRewardExcelVO> nomarlData, List<FtbEmploymentRecordsExcelERRVO> errorData) {
        String redisKey = getRedisKey();
        redisUtil.insert(redisKey, JSON.toJSONString(new FtbRewardsImportRedisBO(nomarlData, errorData)));
        // 30分钟自动过期
        redisUtil.expire(redisKey, 30 * 60);
    }

    @Override
    public void deleteUserInfo(String id, Integer type) {
        if (type == 0) {
            LambdaQueryWrapper<RewardApproval> updateWrapper = Wrappers.lambdaQuery();
            updateWrapper.eq(SuperBaseEntity.SuperIBaseEntity::getId, id);
            updateWrapper.eq(RewardApproval::getStatus,4);
            rewardApprovalMapper.delete(updateWrapper);
        }else {
            LambdaQueryWrapper<PunishmentsApproval> updateWrapper = Wrappers.lambdaQuery();
            updateWrapper.eq(SuperBaseEntity.SuperIBaseEntity::getId, id);
            updateWrapper.eq(PunishmentsApproval::getStatus,4);
            punishmentsApprovalMapper.delete(updateWrapper);
        }
    }

    @Override
    public void exportErrData(HttpServletResponse response) throws IOException {
        String redisKey = getRedisKey();
        FtbRewardsImportRedisBO importRedisBO = getRedisBO(redisKey);
        assert importRedisBO != null;
        List<FtbEmploymentRecordsExcelERRVO> errorData = importRedisBO.getErrorData();
        String fileName = "导入奖惩员工错误信息";
        EasyExcelUtils.exportExcel(response, fileName, errorData, FtbEmploymentRecordsExcelERRVO.class);
    }

    @Override
    public FtbEmployeeRewardRecordsVO queryDetail(String id) {
        return baseMapper.queryDetail(id);
    }

    @Nullable
    private FtbRewardsImportRedisBO getRedisBO(String redisKey) {
        if (!redisUtil.exists(redisKey)) {
            throw new RuntimeException("导入数据已经过期,请重新导入!");
        }
        Object utilString = redisUtil.getString(redisKey);
        return JSON.parseObject(utilString.toString(), FtbRewardsImportRedisBO.class);
    }
    private String getRedisKey() {
        String loginUserId = UserProvider.getLoginUserId();
        return String.format(R_REDIS_KEY, loginUserId);
    }

    private void doExchangeOrder(FtbPersonnelsRewardsPunishmentExchangeOrderDTO.ExchangeInner exchangeInner) {
        LambdaUpdateWrapper<FtbPersonnelsRewardsPunishments> updateWrapper = Wrappers.lambdaUpdate();
        updateWrapper.eq(SuperBaseEntity.SuperIBaseEntity::getId, exchangeInner.getId());
        updateWrapper.set(FtbPersonnelsRewardsPunishments::getSort, exchangeInner.getSort());
        baseMapper.update(null, updateWrapper);
    }

}
