package jnpf.cultivate.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
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.github.pagehelper.PageInfo;
import jnpf.cultivate.mapper.FtbCultivateLabelMapper;
import jnpf.cultivate.mapper.TeachingSkillMapper;
import jnpf.cultivate.service.TeachingSkillService;
import jnpf.cultivate.v2.service.FtbCultivateLabelService;
import jnpf.entity.cultivate.TeachingSkill;
import jnpf.enums.cultivate.BusinessScenarioTypeEnum;
import jnpf.model.cultivate.dto.teaching.TeachingSkillAddDto;
import jnpf.model.cultivate.dto.teaching.TeachingSkillPageDto;
import jnpf.model.cultivate.dto.teaching.TeachingSkillSortDto;
import jnpf.model.cultivate.dto.teaching.TeachingSkillUpdateDto;
import jnpf.model.cultivate.po.label.FtbCultivateLabel;
import jnpf.model.cultivate.v2.exam.vo.ImportObjectVo;
import jnpf.model.cultivate.v2.exam.vo.SkillVo;
import jnpf.model.cultivate.vo.teaching.SkillInfoVo;
import jnpf.model.cultivate.vo.teaching.TeachingSkillVo;
import jnpf.util.*;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
public class TeachingSkillServiceImpl extends ServiceImpl<TeachingSkillMapper, TeachingSkill> implements TeachingSkillService {
    @Autowired
    private RedissonClient redissonClient;
    @Resource
    private FtbCultivateLabelMapper cultivateLabelMapper;
    @Resource
    private FtbCultivateLabelService cultivateLabelService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean addData(TeachingSkillAddDto addDto) {
        LambdaQueryWrapper<TeachingSkill> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(TeachingSkill::getName, addDto.getName());
        queryWrapper.last("limit 1");
        TeachingSkill teachingSkill = baseMapper.selectOne(queryWrapper);
        if (Objects.nonNull(teachingSkill) && teachingSkill.getDeleteMark().equals(0)) {
            throw new RuntimeException("该技能点已存在！");
        }
        //判断技能点数量是否达到上限
        LambdaQueryWrapper<TeachingSkill> infoLambdaQueryWrapper = Wrappers.lambdaQuery();
        infoLambdaQueryWrapper.eq(TeachingSkill::getDeleteMark, 0);
        if (baseMapper.selectCount(infoLambdaQueryWrapper) >= 100) {
            throw new RuntimeException("技能点数量已达到上限，请删除后再添加");
        }
        //使用分布式锁控制
        RLock lock = redissonClient.getLock(UserProvider.getUser().getTenantId() + BusinessScenarioTypeEnum.JND_PZ);
        try {
            if (!lock.tryLock(30, TimeUnit.SECONDS)) {
                throw new RuntimeException("无法获取分布式锁，请稍后再试");
            }
            //方法抽离，处理数据
            processingData(addDto, teachingSkill);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt(); // 恢复中断状态
            throw new RuntimeException(e);
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
        return Boolean.TRUE;
    }

    @Transactional(rollbackFor = Exception.class)
    public void processingData(TeachingSkillAddDto addDto, TeachingSkill teachingSkill) {
        //设置最大排序值
        LambdaQueryWrapper<TeachingSkill> infoLambdaQueryWrapper = Wrappers.lambdaQuery();
        infoLambdaQueryWrapper.eq(TeachingSkill::getDeleteMark, 0);
        infoLambdaQueryWrapper.last("limit 1");
        infoLambdaQueryWrapper.orderByDesc(TeachingSkill::getSort);
        TeachingSkill teachingSkill1Max = baseMapper.selectOne(infoLambdaQueryWrapper);
        Integer sortIndex = Objects.nonNull(teachingSkill1Max) ? teachingSkill1Max.getSort() + 1 : 1;
        TeachingSkill saveSkill = Objects.nonNull(teachingSkill) ? teachingSkill : new TeachingSkill();
        saveSkill.setId(Objects.nonNull(teachingSkill) ? teachingSkill.getId() : RandomUtil.uuId());
        saveSkill.setCategoryId(addDto.getCategoryId());
        saveSkill.setSort(sortIndex);
        saveSkill.setName(addDto.getName());
        saveSkill.setDescription(addDto.getDesc());
        saveSkill.setDeleteMark(0);
        this.saveOrUpdate(saveSkill);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteData(String id) {
        TeachingSkill teachingSkill = baseMapper.selectById(id);
        Assert.notNull(teachingSkill, "未查询到该记录");
        teachingSkill.setDeleteMark(1);
        baseMapper.updateById(teachingSkill);
        //使用分布式锁控制数据重新排序
        RLock lock = redissonClient.getLock(UserProvider.getUser().getTenantId() + BusinessScenarioTypeEnum.JND_PZ);
        try {
            if (!lock.tryLock(30, TimeUnit.SECONDS)) {
                throw new RuntimeException("无法获取分布式锁，请稍后再试");
            }
            LambdaQueryWrapper<TeachingSkill> updateWrapper = Wrappers.lambdaQuery();
            updateWrapper.eq(TeachingSkill::getDeleteMark, 0);
            updateWrapper.orderByAsc(TeachingSkill::getSort);
            List<TeachingSkill> teachingSkillList = baseMapper.selectList(updateWrapper);
            Optional.ofNullable(teachingSkillList).ifPresent(list -> {
                AtomicInteger rank = new AtomicInteger(1);
                teachingSkillList.forEach(vo -> vo.setSort(rank.getAndIncrement()));
                this.updateBatchById(teachingSkillList);
            });
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException(e);
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
        return Boolean.TRUE;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateData(TeachingSkillUpdateDto updateDto) {
        TeachingSkill teachingSkill = baseMapper.selectById(updateDto.getId());
        if (teachingSkill != null) {
            teachingSkill.setName(updateDto.getName());
            teachingSkill.setCategoryId(updateDto.getCategoryId());
            teachingSkill.setDescription(updateDto.getDesc());
            teachingSkill.setLastModifyTime(new Date());
            teachingSkill.setLastModifyUserId(UserProvider.getUser().getUserId());
        }
        return this.updateById(teachingSkill);
    }

    @Override
    public TeachingSkillVo getData(String id) {
        TeachingSkill teachingSkill = baseMapper.selectById(id);
        // 查询分类信息
        FtbCultivateLabel label = cultivateLabelMapper.selectById(teachingSkill.getCategoryId());
        return TeachingSkillVo.builder()
                .id(teachingSkill.getId())
                .name(teachingSkill.getName())
                .categoryId(teachingSkill.getCategoryId())
                .categoryName(null == label ? "" : label.getName())
                .desc(teachingSkill.getDescription())
                .sort(teachingSkill.getSort())
                .build();
    }

    @Override
    public PageInfo<TeachingSkillVo> pageList(TeachingSkillPageDto pageDto) {
        Page<TeachingSkill> page = Page.of(pageDto.getCurrentPage(), pageDto.getPageSize());
        LambdaQueryWrapper<TeachingSkill> queryWrapper = new LambdaQueryWrapper<>();
        if (StringUtil.isNotEmpty(pageDto.getCategoryId())) {
            if ("-1".equals(pageDto.getCategoryId())) {
                queryWrapper.and(w -> w.isNull(TeachingSkill::getCategoryId)
                        .or()
                        .eq(TeachingSkill::getCategoryId, ""));
            } else {
                queryWrapper.eq(TeachingSkill::getCategoryId, pageDto.getCategoryId());
            }
        }
        queryWrapper
                .like(StringUtils.isNotBlank(pageDto.getName()), TeachingSkill::getName, pageDto.getName())
                .eq(TeachingSkill::getDeleteMark, 0)
                .orderByAsc(TeachingSkill::getSort);
        Page<TeachingSkill> queryPage = baseMapper.selectPage(page, queryWrapper);
        List<TeachingSkill> records = queryPage.getRecords();
        List<String> categoryIds = records.stream().map(TeachingSkill::getCategoryId).collect(Collectors.toList());
        List<FtbCultivateLabel> categoryList;
        if (categoryIds.isEmpty()) {
            categoryList = List.of();
        } else {
            categoryList = cultivateLabelMapper.selectBatchIds(categoryIds);
        }
        Map<String, FtbCultivateLabel> categoryMap = categoryList.stream().collect(Collectors.toMap(FtbCultivateLabel::getId, Function.identity()));
        List<TeachingSkillVo> dataList = records.stream().map(item -> {
            TeachingSkillVo teachingSkillVo = JsonUtil.getJsonToBean(item, TeachingSkillVo.class);
            teachingSkillVo.setDesc(item.getDescription());
            FtbCultivateLabel label = categoryMap.get(item.getCategoryId());
            if (null != label) {
                teachingSkillVo.setCategoryName(label.getName());
            }
            return teachingSkillVo;
        }).collect(Collectors.toList());
        PageInfo<TeachingSkillVo> pageInfo = new PageInfo<>();
        pageInfo.setList(dataList);
        pageInfo.setPageSize((int) queryPage.getSize());
        pageInfo.setPageNum((int) queryPage.getCurrent());
        pageInfo.setTotal((int) queryPage.getTotal());
        return pageInfo;
    }

    @Override
    public List<TeachingSkillVo> selectDataList() {
        LambdaQueryWrapper<TeachingSkill> queryWrapper = new LambdaQueryWrapper<TeachingSkill>()
                .eq(TeachingSkill::getDeleteMark, 0)
                .orderByAsc(TeachingSkill::getSort);
        List<TeachingSkill> skillList = this.list(queryWrapper);
        if (CollUtil.isNotEmpty(skillList)) {
            return skillList.stream().map(skill -> TeachingSkillVo.builder()
                    .id(skill.getId())
                    .name(skill.getName())
                    .desc(skill.getDescription())
                    .sort(skill.getSort())
                    .creatorTime(skill.getCreatorTime())
                    .build()).collect(Collectors.toList());
        }
        return CollUtil.newArrayList();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean sort(TeachingSkillSortDto sortDto) {
        List<String> ids = sortDto.getDataList().stream().map(TeachingSkillVo::getId).distinct().collect(Collectors.toList());
        Map<String, TeachingSkillVo> teachingSkillVoMap = sortDto.getDataList().stream().collect(Collectors.toMap(TeachingSkillVo::getId, a -> a, (k1, k2) -> k1));
        //使用分布式锁控制
        RLock lock = redissonClient.getLock(UserProvider.getUser().getTenantId() + BusinessScenarioTypeEnum.JND_PZ);
        try {
            if (!lock.tryLock(30, TimeUnit.SECONDS)) {
                throw new RuntimeException("无法获取分布式锁，请稍后再试");
            }
            LambdaQueryWrapper<TeachingSkill> queryWrapper = Wrappers.lambdaQuery();
            queryWrapper.in(TeachingSkill::getId, ids);
            queryWrapper.eq(TeachingSkill::getDeleteMark, 0);
            List<TeachingSkill> teachingSkillList = baseMapper.selectList(queryWrapper);
            teachingSkillList.forEach(item -> {
                Optional.ofNullable(teachingSkillVoMap.get(item.getId())).ifPresent(dishAppManagementInfoVo -> {
                    item.setLastModifyTime(!item.getSort().equals(dishAppManagementInfoVo.getSort()) ? new Date() : item.getLastModifyTime());
                    item.setSort(dishAppManagementInfoVo.getSort());
                });
            });
            this.updateBatchById(teachingSkillList);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException(e);
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
        return Boolean.TRUE;
    }

    @Override
    public List<String> querySkillIds(String categoryId) {

        List<String> skillIds;
        if (StringUtil.isNotEmpty(categoryId)) {
            LambdaQueryWrapper<TeachingSkill> skillQuery = new LambdaQueryWrapper<TeachingSkill>()
                    .eq(!categoryId.equals("-1"), TeachingSkill::getCategoryId, categoryId)
                    .isNull(categoryId.equals("-1"), TeachingSkill::getCategoryId)
                    .eq(TeachingSkill::getDeleteMark, ConstantUtil.NUM_FALSE);
            List<TeachingSkill> skillList = this.list(skillQuery);
            skillIds = skillList.stream().map(TeachingSkill::getId).collect(Collectors.toList());
            if (skillIds.isEmpty()) {
                skillIds = null;
            }
        } else {
            // 不过滤skillIds
            skillIds = new ArrayList<>();
        }
        return skillIds;
    }

    @Override
    public List<Integer> getDistinctCountBatch(List<ImportObjectVo> list) {

        return baseMapper.getDistinctCountBatch(list);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveImportData(List<ImportObjectVo> list) {

        if (list.isEmpty()) {
            return;
        }
        // 查询分类信息
        List<String> categoryNameList = list.stream().map(ImportObjectVo::getCategoryName).filter(StringUtil::isNotEmpty).distinct().collect(Collectors.toList());
        Map<String, FtbCultivateLabel> labelMap = cultivateLabelService.getLabelMapByName(categoryNameList);
        // 查询最大sort
        LambdaQueryWrapper<TeachingSkill> sortQuery = new LambdaQueryWrapper<TeachingSkill>()
                .eq(TeachingSkill::getDeleteMark, ConstantUtil.NUM_FALSE)
                .orderByDesc(TeachingSkill::getSort)
                .last("LIMIT 1");
        TeachingSkill sk = baseMapper.selectOne(sortQuery);
        int sort = null == sk ? 1 : sk.getSort() + 1;
        List<TeachingSkill> saveList = new ArrayList<>();
        for (ImportObjectVo vo : list) {
            FtbCultivateLabel label = labelMap.get(vo.getCategoryName());
            TeachingSkill skill = new TeachingSkill(FtbUtil.getId(), null == label ? null : label.getId(), vo.getSkillName(), vo.getDescription(), sort++, UserProvider.getLoginUserId());
            saveList.add(skill);
        }
        this.saveBatch(saveList);
    }

    @Override
    public List<SkillVo> getDownSkillList(String skillId, String categoryId) {

        return baseMapper.getDownSkillList(skillId, categoryId);
    }

    @Override
    public List<SkillInfoVo> getAllSkillList() {

        List<SkillInfoVo> allList = baseMapper.getAllSkillList();
        if (allList.isEmpty()) {
            return List.of();
        }
        List<SkillInfoVo> categoryList = allList.stream().filter(v -> v.getPId().equals("0")).collect(Collectors.toList());
        allList.removeAll(categoryList);
        if (allList.isEmpty()) {
            return List.of();
        }
        Map<String, List<SkillInfoVo>> map = allList.stream().collect(Collectors.groupingBy(SkillInfoVo::getPId));
        List<String> removeIds = new ArrayList<>();
        categoryList.forEach(category -> {
            List<SkillInfoVo> childList = map.get(category.getId());
            if (childList == null || childList.isEmpty()) {
                removeIds.add(category.getId());
            } else {
                childList.sort(Comparator.comparing(SkillInfoVo::getSort));
                category.getChildren().addAll(childList);
            }
        });
        if (!removeIds.isEmpty()) {
            categoryList.removeIf(v -> removeIds.contains(v.getId()));
        }
        categoryList.sort(Comparator.comparing(SkillInfoVo::getSort));
        return categoryList;
    }

    @Override
    public List<String> getCategoryHasChild(List<String> categoryIds) {

        return baseMapper.getCategoryHasChild(categoryIds);
    }
}