package com.reyun.saas.mob.user.service.impl;

import com.reyun.saas.common.model.NoSqlTableName;
import com.reyun.saas.common.restResult.RestResult;
import com.reyun.saas.common.restResult.ResultCode;
import com.reyun.saas.common.restResult.ResultGenerator;
import com.reyun.saas.common.util.EquipmentStatusUtils;
import com.reyun.saas.common.util.RealTimeRedisUtils;
import com.reyun.saas.common.util.RedisUtils;
import com.reyun.saas.mob.user.dao.AppMapper;
import com.reyun.saas.mob.user.dao.ChannelMapper;
import com.reyun.saas.mob.user.dao.UserMapper;
import com.reyun.saas.mob.user.domain.App;
import com.reyun.saas.mob.user.domain.Channel;
import com.reyun.saas.mob.user.domain.User;
import com.reyun.saas.mob.user.service.AuthService;
import com.reyun.saas.mob.user.service.MetaDataService;
import com.reyun.saas.mob.user.service.NoSqlService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import tk.mybatis.mapper.entity.Example;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author: hanzepeng
 * @Date: 2021/10/11/16:07
 * @Description: 数据中心非报表类业务层实现类
 */
@Slf4j
@Service
@Transactional
public class MetaDataServiceImpl implements MetaDataService {

    /**
     * 设备状态轨迹信息表的主键  值的格式为  "appkey:设备id"  (设备id可以是 rdid imei idfa oaid)
     */
    private static final String EQUIPMENT_TRACK_KEY = "yy_rtcid";

    /**
     * yy_app_deviceinfo表的主键  目前去这个表中获取新增时间
     */
    private static final String  APP_DEVICE_KEY = "yy_appdeviceid";

    /**
     * 实时redis中的日志流主键开头
     */
    private static final String LOGFLOW_REDISKEY_HEAD = "rtllog:";


    @Resource
    private UserMapper userMapper;

    @Resource
    private AppMapper appMapper;

    @Resource
    private AuthService authService;

    @Resource
    private NoSqlService noSqlService;

    @Resource
    private ChannelMapper channelMapper;

    @Resource
    private RealTimeRedisUtils realTimeRedisUtils;

    @Resource
    private RedisUtils redisUtils;

    /**
     * 查询7日设备状态信息
     * 目前查找设备状态7日轨迹的逻辑 :
     *   根据app的系统类型判断ddb的表名。主键格式为 "appkey:设备id"  (设备id可以是 rdid imei idfa oaid)。
     *   根据主键查询对应的设备信息。
     *
     * 字段 "新增时间"获取逻辑 :
     *   根据  "appkey" + ":" + "rdid"去ddb的yy_app_deviceinfo表中查询新增时间
     *
     * @param map appkey 和 设备id
     * @param loginUserId 当前登录用户id
     * @return
     */
    @Override
    public RestResult equipmentStatus(Map map, Long loginUserId) {

        //获取参数
        String appkey  = (String)map.get("appkey");
        String equipmentId  = (String)map.get("equipmentId");

        //校验参数
        if(StringUtils.isEmpty(appkey)
            || StringUtils.isEmpty(equipmentId)){
            return ResultGenerator.genFailResult("参数错误");
        }

        //去除设备id前后空格
        equipmentId = equipmentId.trim();

        User user = userMapper.selectByPrimaryKey(loginUserId);
        Example example = new Example(App.class);
        example.and().andEqualTo("appkey",appkey);
        example.and().andEqualTo("delFlag",false);
        App app = appMapper.selectOneByExample(example);

        //校验权限
        if(!authService.checkUserAppAuth(user, appkey)){
            return ResultGenerator.genResult(ResultCode.FORBID_OPERATION);
        }

        //根据app的os获取ddb表名
        String DDBTableName = app.getOs().equals("iOS")? NoSqlTableName.YY_TCTINFO_IOS:NoSqlTableName.YY_TCTINFO_ANDROID;

        Map<String,Object> item = noSqlService.getItem2Map(DDBTableName, EQUIPMENT_TRACK_KEY, appkey+":"+equipmentId);

        Map<String,Object> result = new HashMap<>();

        if (item != null) {
            //查询渠道信息
            String channelAlias = null;
            if (app.getOs().equals("iOS")) {
                //ios
                channelAlias = "appleStore";
            }else{
                //其他系统
                String cid = !StringUtils.isEmpty(item.get("cid"))?(String) item.get("cid"):"-1";
                Example channelExample = new Example(Channel.class);
                channelExample.and().andEqualTo("appkey",appkey);
                channelExample.and().andEqualTo("cid",cid);
                Channel channel = channelMapper.selectOneByExample(channelExample);
                channelAlias = channel!=null?channel.getChannelAlias():"-";
            }

            result.put("channelAlias",channelAlias);//渠道名称
            result.put("appVersion",item.get("app_version")); //应用版本
            result.put("sdkVersion",item.get("sdk_version"));//SDK版本
            result.put("appName",app.getAppName());//app名称
            result.put("ip",item.get("ip")); //IP
            result.put("idfa",item.get("idfa"));//IDFA
            result.put("idfv",item.get("idfv"));//IDFV
            result.put("oaid",item.get("oaid"));//OAID
            result.put("android",item.get("androidid")); //Android
            result.put("imei",item.get("imei"));//IMEI

            List<Map<String, String>> rctInfo = splitEquipmentStatusTrack((List<String>)item.get("rct_info"));//设备状态轨迹
            result.put("rctInfo",rctInfo);
            result.put("rdid",item.get("rdid"));//rdid

            //获取rdid和新增时间
            String appRdidKey = appkey + ":" + item.get("rdid");
            Map<String,Object> itemTwo = noSqlService.getItem2Map(NoSqlTableName.YY_APP_DEVICEINFO, APP_DEVICE_KEY, appRdidKey);
            result.put("initTime",itemTwo.get("init_time"));//新增时间

        }
        return ResultGenerator.genSuccessResult(result);
    }


    /**
     * 实时日志流
     * @param appkey appkey
     * @param loginUserId 档期登录用户id
     * @return
     */
    @Override
    public RestResult logFlow(String appkey, Long loginUserId) {

        //查询用户
        User user = userMapper.selectByPrimaryKey(loginUserId);

        //减少实时redis集群的压力  防止频繁点击
        if (!redisUtils.tryLock(user.getId()+":"+appkey,2)) {
            return new RestResult(ResultCode.SERVER_BUSY);
        }

        //查询app
        Example example = new Example(App.class);
        example.and().andEqualTo("appkey",appkey);
        example.and().andEqualTo("delFlag",false);
        App app = appMapper.selectOneByExample(example);

        //校验权限
        if(!authService.checkUserAppAuth(user, appkey)){
            return ResultGenerator.genResult(ResultCode.FORBID_OPERATION);
        }

        //查询redis
        List<String> stringList = realTimeRedisUtils.getStringList(LOGFLOW_REDISKEY_HEAD + appkey);

        //封装结果集
        List<Map<String, String>> result = splitLogFlow(stringList, app);

        return ResultGenerator.genSuccessResult(result);
    }

    /**
     * 拆分实时日志流
     * @param source 原始数据
     * @return
     */
    private List<Map<String,String>> splitLogFlow(List<String> source,App app){

        if (CollectionUtils.isEmpty(source)) {
            return null;
        }

        List<Map<String,String>> result = new ArrayList<>();
        Set<String> cids = new HashSet<>();

        //如果数据超出20条 只取最新20条 实时多线程处理集合的size有几率为21  22
        int length = Math.min(source.size(), 20);

        //原始数据 使用"\t"分割  顺序:1.时间 2.RDID 3.IP 4.应用版本 5.SDK版本 6.作弊状态 7.CID 8.设备状态 9.是否更新
        for (int i = 0; i < length; i++) {

            String[] split = source.get(i).split("\t");

            if (split.length < 9) {
                continue;
            }
            Map<String,String> temp = new HashMap<>();
            temp.put("date",split[0]); //时间
            temp.put("appName",app.getAppName());//应用名称
            temp.put("rdid",split[1]);//rdid
            temp.put("ip",split[2]); //ip
            temp.put("appVersion",split[3]);//app版本
            temp.put("sdkVersion",split[4]);//sdk版本
            temp.put("cheatingBehavior",EquipmentStatusUtils.getCheatingBehavior(split[5]));//作弊状态
            temp.put("equipmentStatus",EquipmentStatusUtils.getEquipmentStatus(split[7],split[8]));//设备状态
            temp.put("cid",split[6]);//渠道id  用于获取渠道别名
            cids.add(split[6]);
            result.add(temp);
        }

        //批量设置渠道名称
        if("iOS".equals(app.getOs())){
            result.forEach(v -> v.put("channelAlias","appleStore"));
        }else{

            Example example = new Example(Channel.class);
            example.and().andEqualTo("appkey",app.getAppkey());
            example.and().andIn("cid",cids);
            example.and().andEqualTo("delFlag",false);
            List<Channel> channels = channelMapper.selectByExample(example);

            if (!CollectionUtils.isEmpty(channels)) {

                //cid为键  渠道对象为值的map
                Map<String, Channel> collect = channels.stream().collect(Collectors.toMap(v -> v.getCid(), v -> v, (v1, v2) -> v1));

                //批量插入渠道名称
                result.forEach(v -> {
                    String cid = v.get("cid")!=null?v.get("cid"):"-1";
                    if (collect.containsKey(cid)) {
                        v.put("channelAlias",collect.get(cid).getChannelAlias());
                    }else{
                        v.put("channelAlias","");
                    }
                });
            }
        }
        return result;

    }


    /**
     * 拆分设备状态轨迹
     * @param source 原始数据
     * @return 拆分之后的数据
     */
    private List<Map<String,String>>  splitEquipmentStatusTrack(List<String> source){

        if (source == null) {
            return null;
        }
        List<Map<String,String>> result = new ArrayList<>();

        //实时那边是按照时间正序排列  这里需要倒序展示给前端
        for (int i = source.size()-1; i >=0 ; i--) {
            Map<String,String> temp = new HashMap<>();

            //原始数据格式为 : 时间|活跃或新增|设备是否更新|设备状态
            String[] split = source.get(i).split("\\|");

            String tags = "-";
            if("0".equals(split[3])){
                tags = "padding"; //处理中
            } else if("1".equals(split[3])){
                tags = "success"; //设备正常
            }else if("2".equals(split[3])){
                tags = "danger"; //设备异常
            } else if("5".equals(split[3])){
                tags = "warning"; //潜在风险
            }

            temp.put("date",split[0]);//时间
            temp.put("status",EquipmentStatusUtils.getEquipmentStatus(split[1],split[2]));//状态
            temp.put("cheatingBehavior",EquipmentStatusUtils.getCheatingBehavior(split[3]));//作弊状态
            temp.put("tags",tags);//前端要加的  用于标识左侧轨迹的图标
            result.add(temp);
        }
        return result;

    }

}
