package com.xxfc.platform.universal.biz;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.github.wxiaoqi.security.common.biz.BaseBiz;
import com.google.common.collect.Lists;
import com.xxfc.platform.universal.constant.RedisKey;
import com.xxfc.platform.universal.constant.RegionType;
import com.xxfc.platform.universal.dto.RegionDTO;
import com.xxfc.platform.universal.entity.SysRegion;
import com.xxfc.platform.universal.mapper.SysRegionMapper;
import com.xxfc.platform.universal.msg.CustomIllegalParamException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.task.TaskExecutor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import tk.mybatis.mapper.entity.Example;
import tk.mybatis.mapper.weekend.WeekendSqls;

import java.util.List;

@Service
@Slf4j
public class SysRegionBiz extends BaseBiz<SysRegionMapper, SysRegion> {

    @Autowired
    private RedisTemplate customRedisTemplate;//todo redis需要改为多实例

    @Autowired
    @Qualifier(value = "applicationTaskExecutor")
    private TaskExecutor customTaskExecutor;
    /**
     * 国家没有父地区，其parentId为0
     */
    private static final Long PARENT_ID_NONE = 0l;



    public List<SysRegion> getRegionsByCodes(List<Long> ids){
        List<String> redisCacheKeys = Lists.newArrayList();
        if(CollectionUtils.isEmpty(ids)){
            throw new CustomIllegalParamException("empty id list");
        }
        for(Long id:ids){
            redisCacheKeys.add(RedisKey.SYS_REGION_CACHE_PREFIX+id);
        }
        List<String> cachedRegionStrList = customRedisTemplate.opsForValue().multiGet(redisCacheKeys);
        List<SysRegion> rs = Lists.newArrayList();
        Boolean hasCache = Boolean.TRUE;
        for(String cachedRegionStr:cachedRegionStrList){
            if(StringUtils.isBlank(cachedRegionStr)){
                hasCache = Boolean.FALSE;
                continue;
            }else{
                rs.add(JSONObject.parseObject(cachedRegionStr,SysRegion.class));
            }

        }
        if(!hasCache){//从db读
            rs = mapper.getByIdList(ids);
        }
        return rs;
    }


    //获取相应地区类型对应在id的位数,作为对应parent查找其下地区的redis key组成部分
    private String getPrefixOfAgencyId(Integer type,Long parentId){
        return String.valueOf(parentId);

    }


    /**
     * 获取对应redis中的hash的key
     * @param parentId
     * @return
     */
    private String getCacheRedisKey(Long parentId){
        //获取相应地区类型对应在agencyId的位数
        return RedisKey.SYS_REGION_SONS_CACHE_PREFIX +parentId;
    }

    /**
     * 查询对应地区之下的地区
     * @param id
     * @return
     */
    public List<SysRegion> getSonRegion(Long id){
        //从缓存中查询
        String redisKey = getCacheRedisKey(id);
        String sysRegionListJson = String.valueOf(customRedisTemplate.opsForValue().get(redisKey));
        List<SysRegion> sysRegions = null;
        if(StringUtils.isNotBlank(sysRegionListJson)){
            sysRegions = JSONObject.parseArray(sysRegionListJson,SysRegion.class);
        }
        if(CollectionUtils.isNotEmpty(sysRegions)){
            return sysRegions;
        }
        //不存在则异步开启刷新缓存任务
        refreshCacheAsync();
        //从db中查询
        return getSonRegionNoCache(id);
    }

    public List<SysRegion> getSonRegionNoCache(Long parentId){
        //从db中查询
        SysRegion param = new SysRegion();
        param.setParentId(parentId);
        return mapper.select(param);
    }

    public void refreshCacheAsync(){
        customTaskExecutor.execute(new Runnable() {
            @Override
            public void run() {
                refreshCache();
            }
        });

    }

    /**
     * 刷新其孩子节点到缓存，并对其孩子迭代执行此操作
     * @param parentId
     */
    private void refreshCacheRegionAndSon(Long parentId){
        List<SysRegion> sonRegions = getSonRegionNoCache(parentId);
        //把当前节点的子节点数据缓存
        String redisKey = getCacheRedisKey(parentId);
        customRedisTemplate.opsForValue().set(redisKey,JSON.toJSON(sonRegions).toString());
        log.info("完成地区【"+parentId+"】的子地区数据缓存");
        if(CollectionUtils.isEmpty(sonRegions) || sonRegions.get(0).getType().equals(RegionType.CITY.getCode())){
            return;
        }
        for(SysRegion sonRegion:sonRegions){
            //缓存每个地区
            customRedisTemplate.opsForValue().set(RedisKey.SYS_REGION_CACHE_PREFIX+sonRegion.getId(),JSON.toJSON(sonRegion).toString());
            refreshCacheRegionAndSon(sonRegion.getId());
        }
    }

    /**
     * 5分钟内刷新数据到缓存
     */
    //@Scheduled(cron = "0 */5 * * * *")//每5分钟刷新一次数据
    public void  refreshCache(){

        String redisLockKey = RedisKey.SYS_REGION_REFRESH_LOCK +(DateTime.now().getMinuteOfDay()/5);//同一日每5分钟只刷新一次
        Boolean suc = customRedisTemplate.opsForValue().setIfAbsent(redisLockKey, String.valueOf(DateTime.now().getMillis()));
        if(!suc){
            log.info("刷新地区数据：获取乐观锁失败，不执行任务");
            return;
        }
        log.info("刷新地区数据任务开始");
        refreshCacheRegionAndSon(PARENT_ID_NONE);
        log.info("刷新常量数据任务成功");
    }

    /**
     * 5分钟内刷新数据到缓存
     */
    public List<SysRegion> selectCity(){
        List<SysRegion> selectByWeekendSql = mapper.selectByExample(new Example.Builder(SysRegion.class)
                .where(WeekendSqls.<SysRegion>custom().andLike(SysRegion::getId, "%00")
                        .andNotLike(SysRegion::getId, "%0000"))
                .build());
        return selectByWeekendSql;
    }

    /**
     * 通过属性获取省
     * @param type
     * @return
     */
    public List<SysRegion> getSysRegionByType(Integer type){
        SysRegion sysRegion = new SysRegion();
        sysRegion.setType(type);
       return mapper.select(sysRegion);
    }

    public RegionDTO findRegionByCityName(String city) {
        return mapper.findByCityName(city);
    }
}
