package com.hdkj.lt.core.sys.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.StopWatch;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.hdkj.lt.base.constants.RedisKeyConstants;
import com.hdkj.lt.base.constants.SystemGlobalConstant;
import com.hdkj.lt.base.enums.OrganTypeEnum;
import com.hdkj.lt.core.sys.dao.SysStruMapper;
import com.hdkj.lt.core.sys.model.dto.OrgInfoDto;
import com.hdkj.lt.core.sys.model.dto.StruTreeCacheDTO;
import com.hdkj.lt.core.sys.model.dto.UnitQueryDTO;
import com.hdkj.lt.core.sys.model.po.OrganizationBo;
import com.hdkj.lt.core.sys.model.po.SysStru;
import com.hdkj.lt.core.sys.service.ISysDeptService;
import com.hdkj.lt.core.sys.service.ISysStruService;
import com.hdkj.lt.utils.cache.RedisRepository;
import com.hdkj.lt.utils.tree.TreeBuilder;
import com.ruoyi.system.api.domain.SysDept;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Nonnull;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Description:
 * @Author: wenhao
 * @Create: 2023/12/07 17:09
 * @Version: 1.0
 */
@Slf4j
@Service
@AllArgsConstructor
public class SysStruServiceImpl extends ServiceImpl<SysStruMapper, SysStru> implements ISysStruService {

    private final RedisRepository redisRepository;

    private final ISysDeptService sysDeptService;

//    private final ISysDictService sysDictService;

    @Override
    public void putAllToCache() {
        redisRepository.del(RedisKeyConstants.CACHE_KEY_STRU_HASH);
        Map<String, Object> dbMap = super.list(new LambdaQueryWrapper<SysStru>().ne(SysStru::getStruType, OrganTypeEnum.USER.getCode()))
                .stream().collect(Collectors.toMap(e -> String.valueOf(e.getStruId()), e -> e));
        redisRepository.putHashValues(RedisKeyConstants.CACHE_KEY_STRU_HASH, dbMap);
    }

    @Override
    public SysStru getCacheById(@Nonnull Long struId) {
        Map<String, Object> cacheMap = redisRepository.getHashValue(RedisKeyConstants.CACHE_KEY_STRU_HASH);
        SysStru sysStru = (SysStru) cacheMap.get(String.valueOf(struId));
        if (Objects.isNull(sysStru)) {
            sysStru = super.getById(struId);
            cacheMap.put(String.valueOf(struId), sysStru);
            redisRepository.putHashValues(RedisKeyConstants.CACHE_KEY_STRU_HASH, cacheMap);
        }
        return sysStru;
    }

    /**
     * @author luorui
     * @date 2025/7/17 11:26
     * @discription 从缓存中取，如果缓存中没有取到则查询数据库刷新全量组织树，并返回查询结果
     **/
    @Override
    public StruTreeCacheDTO getStruTreeByCache(@Nonnull Long struId) {
        if (!redisRepository.exists(RedisKeyConstants.CACHE_KEY_STRU_TREE_HASH)) {
            //如果缓存没有加载
            Map<Long, StruTreeCacheDTO> treeMap = refreshStruTreeByCache();
            return treeMap.get(struId);
        } else {
            //缓存存在。
            Map<String, Object> cacheMap = redisRepository.getHashValue(RedisKeyConstants.CACHE_KEY_STRU_TREE_HASH);
            StruTreeCacheDTO struTreeCacheDTO = (StruTreeCacheDTO) cacheMap.get(String.valueOf(struId));
            return struTreeCacheDTO;
        }
    }

    //刷新组织机构树
    @Override
    public Map<Long, StruTreeCacheDTO> refreshStruTreeByCache() {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start("refreshStruTreeCache");
        Map<Long, StruTreeCacheDTO> treeMap = getStruFullTree();
        try {
            Map<String, Object> cacheMap = new HashMap<>();
            cacheMap.putAll(treeMap.entrySet().stream().collect(Collectors.toMap(e -> String.valueOf(e.getKey()), Map.Entry::getValue)));
            if (redisRepository.exists(RedisKeyConstants.CACHE_KEY_STRU_TREE_HASH)) {
                redisRepository.del(RedisKeyConstants.CACHE_KEY_STRU_TREE_HASH);
            }
            redisRepository.putHashValues(RedisKeyConstants.CACHE_KEY_STRU_TREE_HASH, cacheMap);
        } catch (RuntimeException ex) {
            throw new RuntimeException(String.format("调用组织机构树信息缓存刷新时发生异常：%s", ex.getMessage()));
        } finally {
            stopWatch.stop();
            log.info("组织机构树信息缓存刷新耗时{}秒", stopWatch.getLastTaskInfo().getTimeSeconds());
        }
        return treeMap;
    }

    //查询全量的构建全量组织树
    @Override
    public Map<Long, StruTreeCacheDTO> getStruFullTree() {
        List<SysDept> allDeptList = sysDeptService.getAllDeptList();
        List<StruTreeCacheDTO> treeList = Lists.newArrayList();
        if (CollUtil.isNotEmpty(allDeptList)) {
            allDeptList.forEach(e -> {
                StruTreeCacheDTO struTreeCacheDTO = new StruTreeCacheDTO();
                struTreeCacheDTO.setParentId(e.getParentId());
                struTreeCacheDTO.setName(e.getShortName());
                struTreeCacheDTO.setStruId(e.getDeptId());
                struTreeCacheDTO.setStruLevel(e.getDeptLv() == null ? null : Integer.valueOf(e.getStruLevel()));
                struTreeCacheDTO.setOrganAlias(e.getDeptName());
                struTreeCacheDTO.setStruOrder(e.getOrderNum());
                struTreeCacheDTO.setStruType(e.getStruType());
                struTreeCacheDTO.setIscOrgId(e.getIscOrgId());
                treeList.add(struTreeCacheDTO);
            });
        }
        Map<Long, StruTreeCacheDTO> result = TreeBuilder.buildFullTree(treeList);
        return result;
    }


    @Override
    public Map<Long, SysStru> getCacheMapByIds(Collection<Long> struIds) {
        Map<Long, SysStru> resultMap = Maps.newHashMap();
        if (CollectionUtils.isEmpty(struIds)) {
            return resultMap;
        }
        Map<String, Object> cacheMap = redisRepository.getHashValue(RedisKeyConstants.CACHE_KEY_STRU_HASH);
        struIds = struIds.stream().distinct().collect(Collectors.toList());
        struIds.forEach(i -> Optional.ofNullable(cacheMap.get(String.valueOf(i))).ifPresent(e -> resultMap.put(i, (SysStru) e)));
        Set<Long> queriedIds = resultMap.keySet();
        if (CollectionUtils.size(struIds) != CollectionUtils.size(queriedIds)) {
            List<Long> notFoundIds = (List<Long>) CollectionUtils.removeAll(struIds, queriedIds);
            Map<Long, SysStru> dbMap = super.listByIds(notFoundIds).stream().collect(Collectors.toMap(SysStru::getStruId, e -> e));
            resultMap.putAll(dbMap);
            cacheMap.putAll(dbMap.entrySet().stream().collect(Collectors.toMap(e -> String.valueOf(e.getKey()), Map.Entry::getValue)));
            redisRepository.putHashValues(RedisKeyConstants.CACHE_KEY_STRU_HASH, cacheMap);
        }
        return resultMap;
    }

    @Override
    public Set<SysStru> getCacheSetByIds(Collection<Long> struIds) {
        Set<SysStru> resultSet = Sets.newHashSet();
        if (CollectionUtils.isEmpty(struIds)) {
            return resultSet;
        }
        Map<String, Object> cacheMap = redisRepository.getHashValue(RedisKeyConstants.CACHE_KEY_STRU_HASH);
        struIds = struIds.stream().distinct().collect(Collectors.toList());
        struIds.forEach(i -> Optional.ofNullable(cacheMap.get(String.valueOf(i))).ifPresent(e -> resultSet.add((SysStru) e)));
        List<Long> queriedIds = resultSet.stream().map(SysStru::getStruId).collect(Collectors.toList());
        if (CollectionUtils.size(struIds) != CollectionUtils.size(queriedIds)) {
            List<Long> notFoundIds = (List<Long>) CollectionUtils.removeAll(struIds, queriedIds);
            List<SysStru> dbList = super.listByIds(notFoundIds);
            resultSet.addAll(dbList);
            Map<Long, SysStru> dbMap = dbList.stream().collect(Collectors.toMap(SysStru::getStruId, e -> e));
            cacheMap.putAll(dbMap.entrySet().stream().collect(Collectors.toMap(e -> String.valueOf(e.getKey()), Map.Entry::getValue)));
            redisRepository.putHashValues(RedisKeyConstants.CACHE_KEY_STRU_HASH, cacheMap);
        }
        return resultSet;
    }

    @Override
    public List<OrgInfoDto> listByCondition(UnitQueryDTO queryDTO) {
        return this.baseMapper.listByCondition(queryDTO);
    }

    @Override
    public List<OrganizationBo> findOrganizationsByParentId(Long parentId) {
        return this.getBaseMapper().selectOrganByIds(parentId);
    }

    @Override
    public List<Long> getTeamChildrenStru(Long struId) {
        return this.getBaseMapper().getTeamChildrenStru(struId);
    }

    @Override
    public List<SysStru> getNodesByLastNodes(List<Long> unitIds) {
        List<SysStru> countyIds = Lists.newArrayList();
        List<SysStru> cityIds = Lists.newArrayList();
        List<SysStru> finalStru = Lists.newArrayList();
        //获取所有单位
        List<SysStru> struList = this.getBaseMapper().queryProvinceStruList();
        Set<SysStru> sysStrus = this.getCacheSetByIds(unitIds);
        if (CollectionUtils.size(sysStrus) > 0) {
            Map<Long, SysStru> collectMap = struList.stream().collect(Collectors.toMap(SysStru::getStruId, o -> o, (k1, k2) -> k1));
            Map<Long, List<SysStru>> collect = sysStrus.stream().collect(Collectors.groupingBy(SysStru::getParentId));//根据parentId分组
            for (Map.Entry<Long, List<SysStru>> entity : collect.entrySet()) {
                Long key = entity.getKey();//上级单位id
                SysStru sysStru = collectMap.get(key);
                String struType = sysStru.getStruType();
                List<SysStru> lists = entity.getValue();
                if (StringUtils.equals(SystemGlobalConstant.ORGAN_TYPE_COUNTY, struType)) {
                    //上级单位是区县，1.获取所有该区县的班组，2.比较是否相等，相等：加入区县；不相等：加入班组到最终list
                    long thisTeamCount = struList.stream().filter(f -> Objects.equals(f.getParentId(), key)).count();
                    if (lists.size() == thisTeamCount) {
                        countyIds.add(sysStru);
                    } else {
                        //没有选区县，保存班组的id
                        finalStru.addAll(lists);
                    }
                } else if (StringUtils.equals(SystemGlobalConstant.ORGAN_TYPE_CITY, struType)) {
                    //上级单位是地市，说明有部分是没有班组的区县，加入区县
                    countyIds.addAll(lists);
                } else {
                    //上级是省公司，说最末级是地市，加入地市
                    cityIds.addAll(lists);
                }
            }
            //对比区县
            if (CollectionUtils.size(countyIds) > 0) {
                Map<Long, List<SysStru>> cityMaps = countyIds.stream().collect(Collectors.groupingBy(SysStru::getParentId));
                for (Map.Entry<Long, List<SysStru>> entity : cityMaps.entrySet()) {
                    Long key = entity.getKey();//上级单位id
                    SysStru sysStru = collectMap.get(key);
                    String struType = sysStru.getStruType();
                    List<SysStru> lists = entity.getValue();
                    if (StringUtils.equals(SystemGlobalConstant.ORGAN_TYPE_CITY, struType)) {
                        //上级单位是地市，1.获取所有该地市的区县，2.比较是否相等，相等：加入地市；不相等：加入区县到最终list
                        long thisCountyCount = struList.stream().filter(f -> Objects.equals(f.getParentId(), key)).count();
                        if (lists.size() == thisCountyCount) {
                            cityIds.add(sysStru);
                        } else {
                            finalStru.addAll(lists);
                        }
                    }
                }
            }
            //比较地市
            if (CollectionUtils.size(cityIds) > 0) {
                //判断是否是省公司，1.获取所有地市公司数量，2.比较是否相等，相等：省公司，不相等：加入地市到最终list
                long cityCount = struList.stream().filter(f -> Objects.equals(SystemGlobalConstant.ORGAN_TYPE_CITY, f.getStruType())).count();
                if (cityIds.size() == cityCount) {
                    finalStru.addAll(struList.stream().filter(f -> Objects.equals(SystemGlobalConstant.ORGAN_TYPE_PROVINCE, f.getStruType())).collect(Collectors.toList()));
                } else {
                    finalStru.addAll(cityIds);
                }
            }
            return finalStru;
        }
        return finalStru;
    }

    @Override
    public List<OrgInfoDto> getCityByPsrId(String psrId) {
        return this.baseMapper.getCityByPsrId(psrId);
    }


    @Override
    public List<OrgInfoDto> getCountyByPsrId(String psrId) {
        return this.baseMapper.getCountyByPsrId(psrId);
    }
}
