package com.ningdatech.carapi.sys.helper;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.ningdatech.basic.util.CollUtils;
import com.ningdatech.basic.util.StrPool;
import com.ningdatech.carapi.datascope.model.DataScopeDto;
import com.ningdatech.carapi.sys.contants.RegionContant;
import com.ningdatech.carapi.sys.convert.RegionConverter;
import com.ningdatech.carapi.sys.entity.Region;
import com.ningdatech.carapi.sys.entity.dto.CompanyDTO;
import com.ningdatech.carapi.sys.entity.dto.RegionDTO;
import com.ningdatech.carapi.sys.service.IRegionService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
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.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * <p>
 * RegionsCacheHelper
 * </p>
 *
 * @author WendyYang
 * @since 12:12 2022/10/12
 */
@Slf4j
@Component
public class RegionsCacheHelper implements InitializingBean {

    /**
     * 当前支持最大层级
     */
    public static final int MAX_LEVEL = 3;

    public static final Long ROOT_PARENT_ID = 0L;

    public static final String ROOT_PARENT_CODE = "-1";

    private LoadingCache<Long, RegionDTO> regionsCache;

    private final Map<String, Long> regionCodeMapId = new ConcurrentHashMap<>(256);
    private final Map<Long, RegionDTO> regionsMap = new ConcurrentHashMap<>(256);

    //==================================================================================================================

    private final IRegionService regionService;

    public RegionsCacheHelper(IRegionService regionService) {
        this.regionService = regionService;
    }

    /**
     * 刷新地域信息
     *
     * @param regionId 地域编码
     **/
    public void refresh(Long regionId) {
        log.info("刷新区域信息：{}", regionId);
        regionsCache.refresh(regionId);
    }

    public Long getIdByCode(String regionCode) {
        if(StringUtils.isBlank(regionCode)){
            return 0L;
        }
        return regionCodeMapId.getOrDefault(regionCode, 0L);
    }

    public void put(Long regionId, Region region) {
        regionsCache.put(regionId, RegionConverter.toRegionDTO(region));
    }

    public RegionDTO getByRegionId(Long regionId) {
        return regionsCache.get(regionId);
    }

    @Override
    public void afterPropertiesSet() {
        regionsCache = Caffeine.newBuilder()
                .refreshAfterWrite(7, TimeUnit.DAYS)
                .build(key -> {
                    log.info("加载区域信息：regionLevel = {}", key);
                    // 查询全部
                    Region region = regionService.getById(key);
                    if (Objects.isNull(region)) {
                        return null;
                    }
                    if (!region.getRegionCode().equals(region.getParentCode())) {
                        regionCodeMapId.put(region.getRegionCode(), region.getId());
                        regionsMap.put(region.getId(), RegionConverter.toRegionDTO(region));
                    }
                    return RegionConverter.toRegionDTO(region);
                });
        warmUp();
    }

    /**
     * 或许所有区域信息
     *
     * @return 所有区域信息
     */
    public Collection<RegionDTO> getAll() {
        return regionsCache.asMap().values();
    }

    public List<Long> listChildRegionId(Long regionId) {
        return listChildRegionId(regionId, true, false);
    }

    public List<Long> listChildRegionId(Long regionId, boolean containsSelf, boolean skipThisLevel) {
        Map<Long, List<RegionDTO>> groupByPid = CollUtils.group(getAll(), RegionDTO::getParentId);
        List<Long> childIds = childrenId(regionId, groupByPid, skipThisLevel);
        if (containsSelf) {
            childIds.add(regionId);
        }
        return childIds;
    }

    private List<Long> childrenId(Long regionId, Map<Long, List<RegionDTO>> regionMap) {
        return childrenId(regionId, regionMap, false);
    }

    /**
     * 获取当前区域所有下级区域
     *
     * @param regionId      区域ID
     * @param regionMap     区域信息
     * @param skipThisLevel 是否跳过本级
     * @return 子区域ID
     * @author WendyYang
     **/
    private List<Long> childrenId(Long regionId, Map<Long, List<RegionDTO>> regionMap, boolean skipThisLevel) {
        List<Long> childIds = new ArrayList<>();
        List<RegionDTO> children = regionMap.get(regionId);
        if (children != null) {
            children.forEach(w -> {
                if (skipThisLevel && w.getRegionCode().equals(w.getParentCode())) {
                    return;
                }
                childIds.add(w.getId());
                childIds.addAll(childrenId(w.getId(), regionMap));
            });
        }
        return childIds;
    }

    /**
     * 根据传入的地区ID获取所有上层地区集合
     * 按照level升序排列
     *
     * @param regionId 地区ID
     * @return 所有父级区域集合
     * @author WendyYang
     **/
    public List<RegionDTO> listParents(Long regionId) {
        List<RegionDTO> result = new ArrayList<>();
        RegionDTO region = regionsCache.get(regionId);
        if (region != null) {
            result.add(region);
            if (region.getParentId() > 0) {
                result.addAll(0, listParents(region.getParentId()));
            }
        }
        return result;
    }


    private void warmUp() {
        List<Region> regions = regionService.list();

        for (Region region : regions) {
            if (!region.getRegionCode().equals(region.getParentCode())) {
                regionCodeMapId.put(region.getRegionCode(), region.getId());
                regionsMap.put(region.getId(), RegionConverter.toRegionDTO(region));
            }
        }

        Map<Long, RegionDTO> mapByRegionId = CollUtils.listToMap(regions, Region::getId, RegionConverter::toRegionDTO);
        regionsCache.putAll(mapByRegionId);
    }


    /**
     * 获取展示的区域名称
     */
    public String getDisplayName(Long regionId) {
        if (Objects.isNull(regionId)) {
            return StringUtils.EMPTY;
        }
        String displayRegionName = null;
        List<RegionDTO> regions = listParents(regionId);
        if (CollUtil.isNotEmpty(regions)) {
            displayRegionName = CollUtils.join(regions, RegionDTO::getRegionName, StrPool.DASH);
        }
        return displayRegionName;
    }

    /**
     * 查询区域请使用{@link #listChildRegionId(Long)}
     *
     * @param regionCode  区域编码
     * @param regionLevel 区域级别
     * @return 子区域集合
     */
    @Deprecated
    public List<String> listChildRegionCode(String regionCode, int regionLevel) {
        return Collections.emptyList();
    }

    /**
     * 获取当前区域的下级区域信息
     *
     * @param regionId      区域ID
     * @param skipThisLevel 是否包含本级
     * @return 子区域map
     */
    public Map<RegionDTO, List<Long>> getChildrenMap(Long regionId, Boolean... skipThisLevel) {
        boolean notSkip = ArrayUtil.isNotEmpty(skipThisLevel) ? skipThisLevel[0] : false;
        Map<RegionDTO, List<Long>> result = new TreeMap<>(Comparator.comparing(RegionDTO::getRegionCode));
        for (RegionDTO region : getAll()) {
            if (notSkip && region.getRegionCode().equals(region.getParentCode())) {
                continue;
            }
            if (region.getParentId().equals(regionId)) {
                result.put(region, listChildRegionId(region.getId()));
            }
        }
        result.put(getByRegionId(regionId), listChildRegionId(regionId));
        return result;
    }

    /**
     * 获取当前区域的下一级区域ID集合
     *
     * @param regionId
     * @return
     */
    public List<Long> listChild(Long regionId) {
        List<Long> result = new ArrayList<>();
        for (RegionDTO region : getAll()) {
            if (region.getParentId().equals(regionId) && region.getDeleted() == false) {
                result.add(region.getId());
            }
        }
        return result;
    }

    /**
     * 获取当前区域的下一级区域集合
     *
     * @param regionId /
     * @return /
     */
    public List<RegionDTO> listChildOnly(Long regionId) {
        return listChildOnly(regionId, false);
    }

    public List<RegionDTO> listChildOnly(Long regionId, boolean skipThisLevel) {
        List<RegionDTO> result = new ArrayList<>();
        for (RegionDTO region : getAll()) {
            if (skipThisLevel && region.getRegionCode().equals(region.getParentCode())) {
                continue;
            }
            if (Objects.nonNull(regionId) && regionId.equals(region.getParentId()) && !region.getDeleted()) {
                result.add(region);
            }
        }
        if (!result.isEmpty()) {
            result.sort(Comparator.comparing(RegionDTO::getRegionCode));
        }
        return result;
    }

    /**
     * 去获取菜单 有权限判断
     *
     * @param dataScope
     * @param companiesCacheHelper
     * @return
     */
    public Collection<RegionDTO> getAllWithRole(Optional<DataScopeDto> dataScope, CompaniesCacheHelper companiesCacheHelper) {
        if (!dataScope.isPresent() || (CollUtil.isEmpty(dataScope.get().getRegionIds()) &&
                CollUtil.isEmpty(dataScope.get().getCompanyIds()))) {
            return regionsCache.asMap().values();
        }

        List<Long> dataScopeRegionIds = dataScope.get().getRegionIds();

        //1.企业逻辑
        if (CollUtil.isNotEmpty(dataScope.get().getCompanyIds())) {
            //其实只有一个企业
            Long companyId = dataScope.get().getCompanyIds().get(0);
            CompanyDTO company = companiesCacheHelper.getById(companyId);
            dataScopeRegionIds = new ArrayList<Long>() {
                {
                    this.add(company.getRegionId());
                }
            };
        }

        Collection<@NonNull RegionDTO> regions = regionsCache.asMap().values();
        if (dataScopeRegionIds.size() == regions.size()) {
            return regions;
        }

        //2.区域逻辑
        List<Long> finalDataScopeRegionIds = dataScopeRegionIds;
        AtomicInteger index = new AtomicInteger(0);
        Collection<RegionDTO> res = regions.stream().filter(region -> finalDataScopeRegionIds.contains(region.getId()))
                .map(region -> {
                    RegionDTO resDto = BeanUtil.copyProperties(region, RegionDTO.class);
                    if (0 == index.get()) {
                        processFirst(region, resDto);
                    }
                    index.incrementAndGet();
                    return resDto;
                }).collect(Collectors.toList());
        return res;
    }

    /**
     * 角色数据权限筛选后的菜单 最顶部的 要装饰上省市 目标实体 resDto
     *
     * @param region
     * @param resDto
     */
    private void processFirst(RegionDTO region, RegionDTO resDto) {
        if (region.getRegionLevel().equals(RegionContant.SECOND_LEVEL)) {
            resDto.setRegionName(regionsCache.get(region.getParentId()).getRegionName() + region.getRegionName());
        } else if (region.getRegionLevel().equals(RegionContant.THIRD_LEVEL)) {
            RegionDTO parent = regionsCache.get(region.getParentId());
            RegionDTO grandParent = regionsCache.get(parent.getParentId());
            resDto.setRegionName(grandParent.getRegionName() + parent.getRegionName() + region.getRegionName());
        }
        resDto.setParentId(RegionContant.EMPTY_PARENT_ID);
    }

    /**
     * 获取到 此区域ID的省份
     *
     * @param regionId
     * @return
     */
    public String getProvince(Long regionId) {
        if (Objects.isNull(regionId)) {
            return StringUtils.EMPTY;
        }
        RegionDTO regionDto = this.getByRegionId(regionId);
        if (Objects.isNull(regionDto)) {
            return StringUtils.EMPTY;
        }
        if (regionDto.getRegionLevel().equals(RegionContant.THIRD_LEVEL)) {
            RegionDTO parent = getByRegionId(regionDto.getParentId());
            if (Objects.isNull(parent)) {
                return StringUtils.EMPTY;
            }
            RegionDTO parentD = getByRegionId(parent.getParentId());
            if (Objects.isNull(parentD)) {
                return StringUtils.EMPTY;
            }
            return parentD.getRegionName();
        } else if (regionDto.getRegionLevel().equals(RegionContant.SECOND_LEVEL)) {
            RegionDTO parent = getByRegionId(regionDto.getParentId());
            if (Objects.isNull(parent)) {
                return StringUtils.EMPTY;
            }
            return parent.getRegionName();
        } else if (regionDto.getRegionLevel().equals(RegionContant.FIRST_LEVEL)) {
            return regionDto.getRegionName();
        }
        return StringUtils.EMPTY;
    }

    /**
     * 获取到 此区域ID的城市
     *
     * @param regionId
     * @return
     */
    public String getCity(Long regionId) {
        if (Objects.isNull(regionId)) {
            return StringUtils.EMPTY;
        }
        RegionDTO regionDto = this.getByRegionId(regionId);
        if (Objects.isNull(regionDto)) {
            return StringUtils.EMPTY;
        }
        if (regionDto.getRegionLevel().equals(RegionContant.THIRD_LEVEL)) {
            RegionDTO parent = getByRegionId(regionDto.getParentId());
            if (Objects.isNull(parent)) {
                return StringUtils.EMPTY;
            }
            return parent.getRegionName();
        } else if (regionDto.getRegionLevel().equals(RegionContant.SECOND_LEVEL)) {
            return regionDto.getRegionName();
        }
        return StringUtils.EMPTY;
    }

    /**
     * 获取到 此区域ID的区县
     *
     * @param regionId
     * @return
     */
    public String getDistinguish(Long regionId) {
        if (Objects.isNull(regionId)) {
            return StringUtils.EMPTY;
        }
        RegionDTO regionDto = this.getByRegionId(regionId);
        if (Objects.isNull(regionDto)) {
            return StringUtils.EMPTY;
        }
        if (regionDto.getRegionLevel().equals(RegionContant.THIRD_LEVEL)) {
            return regionDto.getRegionName();
        }
        return StringUtils.EMPTY;
    }

    public List<RegionDTO> listByLevel(Integer level) {
        if (Objects.isNull(level)) {
            return Collections.emptyList();
        }
        return regionsCache.asMap().values().stream()
                .filter(r -> Objects.nonNull(r.getRegionLevel()) && level.equals(r.getRegionLevel()) && !r.getDeleted())
                .collect(Collectors.toList());
    }

}