package com.ningdatech.carapi.sys.helper;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

import javax.annotation.Resource;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
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.Maps;
import com.ningdatech.carapi.sys.entity.CompanyJoin;
import com.ningdatech.carapi.sys.entity.dto.CompanyDTO;
import com.ningdatech.carapi.sys.entity.dto.RegionDTO;
import com.ningdatech.carapi.sys.service.CompanyJoinService;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import lombok.extern.slf4j.Slf4j;

/**
 * <p>
 * CompaniesCacheHelper
 * </p>
 *
 * @author PoffyZhang
 * @since 09:01 2022/10/18
 */
@Slf4j
@Component
public class CompaniesCacheHelper implements InitializingBean {

    private LoadingCache<Long, CompanyDTO> companyCache;

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

    private final CompanyJoinService companyService;

    @Resource
    private RegionsCacheHelper regionsCacheHelper;

    private Map<String, CompanyDTO> companyNameMap = Maps.newHashMap();

    public CompaniesCacheHelper(CompanyJoinService companyService) {
        this.companyService = companyService;
    }

    @Override
    public void afterPropertiesSet() {
        companyCache = Caffeine.newBuilder()
            // 需避免刷新过于频繁
            .refreshAfterWrite(10, TimeUnit.MINUTES).build(new CacheLoader<Long, CompanyDTO>() {

                @Override
                public CompanyDTO load(@NonNull Long companyId) {
                    CompanyJoin company = companyService
                        .oneJoin(Wrappers.lambdaQuery(CompanyJoin.class).eq(CompanyJoin::getId, companyId));
                    if (Objects.isNull(company)) {
                        log.info("加载企业信息失败: 企业{} 信息为空为空", companyId);
                        return null;
                    }
                    // 查询区域
                    searchRegion(company);
                    return BeanUtil.copyProperties(company, CompanyDTO.class);
                }

                @Override
                public @NonNull Map<Long, CompanyDTO> loadAll(@NonNull Iterable<? extends Long> keys) {
                    List<CompanyJoin> companies =
                        companyService.listJoin(Wrappers.lambdaQuery(CompanyJoin.class).in(CompanyJoin::getId, keys));
                    if (CollUtil.isEmpty(companies)) {
                        log.info("加载企业信息失败: 企业信息为空为空");
                        return Collections.emptyMap();
                    }
                    // 查询区域
                    searchRegions(companies);
                    return companies.stream().map(c -> BeanUtil.copyProperties(c, CompanyDTO.class))
                        .collect(Collectors.toMap(CompanyDTO::getId, Function.identity()));
                }

            });

        List<CompanyJoin> companies = companyService.listJoin(Wrappers.lambdaQuery(CompanyJoin.class));
        if (CollUtil.isEmpty(companies)) {
            log.info("加载企业信息失败: 企业为空");
            return;
        }
        // 查询区域
        searchRegions(companies);
        companies.forEach(c -> {
            CompanyDTO company = BeanUtil.copyProperties(c, CompanyDTO.class);
            companyCache.put(company.getId(), company);
            companyNameMap.put(company.getName(), company);
        });
    }

    public Collection<CompanyDTO> all() {
        return companyCache.asMap().values();
    }

    private void searchRegions(List<CompanyJoin> companyJoins) {
        if (CollUtil.isEmpty(companyJoins)) {
            return;
        }
        companyJoins.forEach(this::searchRegion);
    }

    private void searchRegion(CompanyJoin company) {
        if (Objects.isNull(company)) {
            return;
        }
        if (Objects.nonNull(company.getRegionId())) {
            List<RegionDTO> regions =
                regionsCacheHelper.listParents(Long.valueOf(String.valueOf(company.getRegionId())));
            if (CollUtil.isNotEmpty(regions)) {
                company.setRegionName(regions.stream().map(RegionDTO::getRegionName).collect(Collectors.joining("-")));
            }
        }
    }

    /**
     * 获取当前企业信息
     * 
     * @param companyId
     *            企业ID
     * @return
     */
    public CompanyDTO getById(Long companyId) {
        CompanyDTO companyDTO = companyCache.get(companyId);
        return Optional.ofNullable(companyDTO).orElse(new CompanyDTO());
    }

    public String getNameById(Long companyId) {
        CompanyDTO dto = companyCache.get(companyId);
        if (Objects.nonNull(dto)) {
            return dto.getName();
        }
        return StringUtils.EMPTY;
    }

    public String getNameById(Long companyId, String oldName) {
        CompanyDTO dto = companyCache.get(companyId);
        if (Objects.nonNull(dto)) {
            return dto.getName();
        }
        return oldName;
    }

    /**
     * 获取当前企业信息
     * 
     * @param companyName
     *            企业ID
     * @return
     */
    public CompanyDTO getByName(String companyName) {
        return companyNameMap.get(companyName);
    }

    /**
     * 获取当前企业信息
     * 
     * @return
     */
    public Map<Long, CompanyDTO> asMap() {
        return companyCache.asMap();
    }

    public void refresh(Long id) {
        companyCache.refresh(id);
        CompanyDTO data = companyCache.get(id);
        if (Objects.nonNull(data)) {
            companyNameMap.put(data.getName(), data);
        }
    }

    public void refresh(List<Long> ids) {
        if (CollUtil.isNotEmpty(ids)) {
            ids.forEach(id -> {
                companyCache.refresh(id);
                CompanyDTO data = companyCache.get(id);
                if (Objects.nonNull(data)) {
                    companyNameMap.put(data.getName(), data);
                }
            });
        }
    }
}
