package com.xxfc.platform.vehicle.biz;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.github.wxiaoqi.security.common.biz.BaseBiz;
import com.github.wxiaoqi.security.common.exception.BaseException;
import com.google.common.collect.Lists;
import com.xxfc.platform.vehicle.constant.RedisKey;
import com.xxfc.platform.vehicle.constant.RegionType;
import com.xxfc.platform.vehicle.entity.SysRegion;
import com.xxfc.platform.vehicle.mapper.SysRegionMapper;
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.core.task.TaskExecutor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
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
    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 BaseException("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){
//        switch(type){
//            case -1:
//                return String.valueOf(0);
//            case 0:
//                return String.valueOf(1);
//            case 1:
//                return String.valueOf(parentId);
//            case 2:
//                return String.valueOf(parentId).substring(0,2);
//            case 3:
//                return String.valueOf(parentId).substring(0,4);
//            case 4:
//                return String.valueOf(parentId);
//            default:
//                throw new CustomIllegalParamException("not valid region");
//        }

        return String.valueOf(parentId);

    }

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

    /**
     * 根据id获得所属类型
     * @param parentId
     * @return
     */
//    private Integer getTypeFromId(Long parentId){
//        if(parentId == 0) {//国家type-1
//            return RegionType.COUNTRY.getCode();
//        }if (parentId<2){//地区type 0
//            return RegionType.REGION.getCode();
//        }if(parentId < 20){//省type 1
//            return RegionType.PROVINCE.getCode();
//        }else if(parentId%100>0){//第1、2非零，即3级地区 镇一级type 4
//            return RegionType.TOWN.getCode();
//        }else if(parentId%10000>0){//第1、2为零，第3、4位非0，即2级地区  县type 3
//            return RegionType.PREFECTUR.getCode();
//        }else{//第1、2、3、4为零，第5、6位非0，即2级地区    市type 2
//            return RegionType.CITY.getCode();
//        }
//    }

    /**
     * 获取对应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);
    }
}
