package com.hz.pm.api.user.helper.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.IterUtil;
import com.github.benmanes.caffeine.cache.CacheLoader;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.google.common.collect.Lists;
import com.hz.pm.api.meta.assembler.MetaTagAssembler;
import com.hz.pm.api.meta.model.dto.TagDTO;
import com.hz.pm.api.meta.model.dto.TagTreeDTO;
import com.hz.pm.api.user.helper.MhUnitCache;
import com.hz.pm.api.user.model.dto.MhUnitDTO;
import com.hz.pm.api.user.model.entity.MhUnit;
import com.hz.pm.api.user.service.IMhUnitService;
import com.ningdatech.basic.util.CollUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * <p>
 * MhUnitCacheImpl
 * </p>
 *
 * @author WendyYang
 * @since 11:20 2024/1/11
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class MhUnitCacheImpl implements MhUnitCache, InitializingBean {

    private static final Long ALL = -1L;

    private final IMhUnitService mhUnitService;
    private LoadingCache<Long, MhUnitDTO> cache;

    private final Map<Long, List<Long>> childIdMap = new ConcurrentHashMap<>();

    private void putToChildIdMap(Long childId, Long parentId) {
        List<Long> childIds = Lists.newArrayList(childId);
        childIdMap.merge(parentId, childIds, (v1, v2) -> {
            v1.addAll(v2);
            return v1;
        });
    }

    @Override
    public void afterPropertiesSet() {
        cache = Caffeine.newBuilder()
                .refreshAfterWrite(7, TimeUnit.DAYS)
                .build(new CacheLoader<Long, MhUnitDTO>() {

                    @Override
                    public MhUnitDTO load(@NonNull Long key) {
                        log.info("刷新当前标签树：{}", key);
                        // 查询全部
                        MhUnit unit = mhUnitService.getById(key);
                        putToChildIdMap(unit.getId(), unit.getParentId());
                        return BeanUtil.copyProperties(unit, MhUnitDTO.class);
                    }

                    @Override
                    public @NonNull Map<Long, MhUnitDTO> loadAll(@NonNull Iterable<? extends @NonNull Long> keys) {
                        List<MhUnit> allUnits;
                        if (CollUtil.isEmpty(keys)) {
                            allUnits = mhUnitService.list();
                        } else {
                            List<? extends @NonNull Long> keysList = IterUtil.toList(keys);
                            if (keysList.size() == 1 && keysList.get(0).equals(ALL)) {
                                allUnits = mhUnitService.list();
                            } else {
                                allUnits = mhUnitService.listByIds(keysList);
                            }
                        }
                        return CollUtils.listToMap(allUnits, MhUnit::getId, w -> {
                            putToChildIdMap(w.getId(), w.getParentId());
                            return BeanUtil.copyProperties(w, MhUnitDTO.class);
                        });
                    }
                });
        // 初始化所有单位
        cache.getAll(Collections.singletonList(ALL));
    }

    @Override
    public List<MhUnitDTO> all() {
        return new ArrayList<>(cache.asMap().values());
    }

    @Override
    public MhUnitDTO getById(Long id) {
        return cache.get(id);
    }

    @Override
    public List<MhUnitDTO> getChildren(Long id) {
        return CollUtils.convert(childIdMap.get(id), cache::get);
    }

    @Override
    public List<Long> getChildrenIds(Long id) {
        return childIdMap.get(id);
    }

    @Override
    public List<MhUnitDTO> getChildrenRecursion(Long id) {
        return CollUtils.convert(getChildrenIdsRecursion(id), cache::get);
    }

    @Override
    public List<Long> getChildrenIdsRecursion(Long id) {
        List<Long> childIds = new ArrayList<>();
        collectChildId(childIds, id);
        return childIds;
    }

    private void collectChildId(List<Long> collect, Long parentId) {
        List<Long> childIds = childIdMap.get(parentId);
        if (childIds != null) {
            collect.addAll(childIds);
            childIds.forEach(child -> collectChildId(collect, child));
        }
    }


}
