package com.duiba.tuia.youtui.web.service.impl;

import cn.com.duiba.tuia.activity.center.api.constant.Scene;
import cn.com.duiba.tuia.activity.center.api.dto.ActivityPluginDto;
import cn.com.duiba.tuia.activity.center.api.dto.IpLibraryDto;
import cn.com.duiba.tuia.activity.center.api.remoteservice.RemoteIpService;
import cn.com.duiba.tuia.activity.center.api.remoteservice.RemotePluginService;
import cn.com.duiba.tuia.ssp.center.api.constant.RedisKeyConstant;
import cn.com.duiba.tuia.ssp.center.api.dto.PlugBuoyConfigDto;
import cn.com.duiba.tuia.ssp.center.api.dto.SlotDto;
import cn.com.duiba.tuia.ssp.center.api.remote.RemoteActivityCenterService;
import cn.com.duiba.tuia.ssp.center.api.tool.SspRedisDataTool;
import cn.com.duiba.wolf.dubbo.DubboResult;
import com.duiba.tuia.youtui.web.constant.CacheKey;
import com.duiba.tuia.youtui.web.service.BaseCacheService;
import com.duiba.tuia.youtui.web.service.SlotService;
import com.duiba.tuia.youtui.web.tool.CachedKeyUtils;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.Maps;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

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

@Service
public class SlotServiceImpl extends BaseCacheService implements SlotService, InitializingBean {

    @Autowired
    RemoteActivityCenterService remoteActivityCenterService;

    @Autowired
    RemotePluginService remotePluginService;

    @Autowired
    RemoteIpService remoteIpService;

    /*广告配置信息缓存*/
    LoadingCache<Long, Optional<SlotDto>> mediaSlotCache;

    /** ip地址映射地域编码的缓存 */
    LoadingCache<String, TreeMap<Long, IpLibraryDto>> ipRegionMapCache;

    /*
     * 加载完配置后的初始化方法
     */
    @Override
    public void afterPropertiesSet() {
        mediaSlotCache = CacheBuilder.newBuilder()
                .maximumSize(5000)//最多存5000个
                .refreshAfterWrite(60, TimeUnit.SECONDS)//每60秒刷新缓存
                .build(new com.google.common.cache.CacheLoader<Long, Optional<SlotDto>>() {
                    @Override
                    public Optional<SlotDto> load(Long slotId) {
                        return getSlotBySlotId(slotId);
                    }
                });

        ipRegionMapCache = CacheBuilder.newBuilder()
                .maximumSize(3)//最多存1000个
                .expireAfterWrite(1, TimeUnit.DAYS)//每天刷新缓存
                .build(new com.google.common.cache.CacheLoader<String, TreeMap<Long, IpLibraryDto>>() {
                    @Override
                    public TreeMap<Long, IpLibraryDto> load(String key) {
                        return findIpRegionMap();
                    }
                });
    }

    @Override
    public List<ActivityPluginDto> getAvailablePlugin(List<Long> pluginIds, Long slotId) {

        if (CollectionUtils.isEmpty(pluginIds)) {
            return Collections.emptyList();
        }

        return this.advancedCacheClient.getWithCacheLoader(CachedKeyUtils.getRedisKey(CacheKey.SLOT_PLUGIN, slotId), 30, TimeUnit.SECONDS, true, () -> this.invokeSelectAvailablePluginByIds(pluginIds));
    }

    private List<ActivityPluginDto> invokeSelectAvailablePluginByIds(List<Long> pluginIds) {
        DubboResult<List<ActivityPluginDto>> dr = this.remotePluginService.selectAvailablePluginByIds(pluginIds);
        if (!dr.isSuccess()) {
            logger.warn("remotePluginService.selectAvailablePluginByIds,msg={}", dr.getMsg());
            return Collections.emptyList();
        }
        return dr.getResult();
    }

    @Override
    public Optional<SlotDto> getSlot(Long slotId) {
        try {
            if (slotId == null) {
                return Optional.empty();
            }
            return mediaSlotCache.get(slotId);
        } catch (ExecutionException e) {
            logger.warn("广告位缓存不存在，slotId=[{}]", slotId, e);
            return Optional.empty();
        }
    }

    @Override
    public List<String> getSlotIdTags(Long slotId) {
        try {
            return SspRedisDataTool.getActShieldStrategy(slotRedis.opsForHash().get(RedisKeyConstant.getSlotShieldStrategyKey(), slotId));
        } catch (Exception e) {
            logger.warn("slot redis", e);
            return Collections.emptyList();
        }
    }

    @Override
    public boolean ipFilter(SlotDto slotDto, String ip, Scene scene) {
        boolean rs = true;

        Integer regionStatus = null;//区域屏蔽状态
        List<Long> regions = null;//投放区域

        PlugBuoyConfigDto plugBuoyConfigDto = slotDto.getPlugBuoyConfigDto();

        if (plugBuoyConfigDto == null) {
            return rs;
        }

        if (Scene.PLUGIN.equalsScene(scene)) {//插件
            regionStatus = plugBuoyConfigDto.getPluginRegionStatus();
            regions = plugBuoyConfigDto.getPluginValidRegionIds();
        } else if (Scene.RETURN.equalsScene(scene)) {//返回拦截
            regionStatus = plugBuoyConfigDto.getReturnRegionStatus();
            regions = plugBuoyConfigDto.getReturnValidRegionIds();
        }

        //regionStatus == 0 时，则是区域部分投放
        if (Integer.valueOf(0).equals(regionStatus) && (CollectionUtils.isEmpty(regions) || !regions.contains(findRegionByIp(ip)))) {
                rs = false;
        }
        return rs;
    }

    /*
     * 根据ip查询区域编码
     */
    public Long findRegionByIp(String ip) {
        if (StringUtils.isBlank(ip)) {
            return 0L;
        }
        try {
            return this.findRegionByIpFormNative(ip);
        } catch (Exception e) {
            logger.warn("ip映射地域缓存错误，ip=[{}]", ip, e);
            return 0L;
        }
    }

    private Long findRegionByIpFormNative(String ip) throws Exception {
        TreeMap<Long, IpLibraryDto> ipLibraryDtoTreeMap = ipRegionMapCache.get("key");
        Map.Entry<Long, IpLibraryDto> ipLibraryDtoEntry = ipLibraryDtoTreeMap.floorEntry(IpLibraryDto.convertIpLong(ip));
        if (ipLibraryDtoEntry == null) {
            return 0L;
        }
        IpLibraryDto ipLibraryDto = ipLibraryDtoEntry.getValue();
        if (ipLibraryDto == null) {
            return 0L;//查不到默认为，其他：0
        }
        Integer areaCode = ipLibraryDto.getAreaCode();
        if (areaCode == null) {
            return 0L;
        }

        if (areaCode % 10000 == 0) {
            return Long.valueOf(areaCode / 10000);
        }
        return Long.valueOf(areaCode / 100);
    }

    private TreeMap<Long, IpLibraryDto> findIpRegionMap() {
        TreeMap<Long, IpLibraryDto> ipLibraryDtoTreeMap = Maps.newTreeMap();
        List<IpLibraryDto> ipLibraryDtoList = remoteIpService.findAll();
        if (CollectionUtils.isEmpty(ipLibraryDtoList)) {
            return ipLibraryDtoTreeMap;
        }
        ipLibraryDtoTreeMap.putAll(ipLibraryDtoList.stream().collect(Collectors.toMap(IpLibraryDto::getStartIpNum, p -> p)));
        return ipLibraryDtoTreeMap;
    }

    private Optional<SlotDto> getSlotBySlotId(Long slotId) {
        DubboResult<SlotDto> dr = this.remoteActivityCenterService.getSlotDtoFromCacheBySlotId(slotId);
        if (!dr.isSuccess()) {
            logger.warn("remoteActivityCenterService.getPlugAndBuoyConfigBySlotId({}),msg={}", slotId, dr.getMsg());
            return Optional.empty();
        }
        return Optional.of(dr.getResult());
    }
}
