package com.ytpm.middle.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ytpm.agent.model.YtPlatformUserApp;
import com.ytpm.agent.view.AgentAppView;
import com.ytpm.agent.view.FeignServeAppView;
import com.ytpm.general.RepMessage;
import com.ytpm.general.Result;
import com.ytpm.middle.dao.ApkMapper;
import com.ytpm.middle.dao.AppMapper;
import com.ytpm.middle.dao.EnterpriseMapper;
import com.ytpm.middle.dao.RiskMapper;
import com.ytpm.middle.service.CountService;
import com.ytpm.middle.util.FeignClientInvoker;
import com.ytpm.middle.util.RedisUtil;
import com.ytpm.middle.view.AppRankingListVO;
import com.ytpm.middle.view.AppUserHourVO;
import com.ytpm.middle.view.DashboardAppRevenueVO;
import com.ytpm.middle.view.DashboardRankingListVO;
import com.ytpm.middle.view.DashboardRevenueVO;
import com.ytpm.middle.view.DashboardRiskVO;
import com.ytpm.middle.view.DashboardTopCountVo;
import com.ytpm.middle.view.UserRankingListVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

@Slf4j
@Service
@RefreshScope
public class CountServiceImpl implements CountService {

    @Resource
    private EnterpriseMapper enterpriseMapper;
    @Resource
    private ApkMapper apkMapper;
    @Autowired
    private FeignClientInvoker feignInvoker;
    @Resource
    private RiskMapper riskMapper;
    @Resource
    private RedisUtil redisUtil;
    @Value("${ranking.expire}")
    private long rankingExpire;
    @Value("${ranking.limit}")
    private int rankingLimit;
    private static final String RANKING_KEY = "yt_ranking";
    @Resource
    private AppMapper appMapper;

    /**
     * 获取顶部数据统计
     */
    @Override
    public Result<DashboardTopCountVo> getTopCount() {
        DashboardTopCountVo vo = new DashboardTopCountVo();
        vo.setAgentCount(enterpriseMapper.countEnterprise());
        vo.setApkCount(apkMapper.countApk());
        List<FeignServeAppView> appViews = apkMapper.queryServeAndApp();
        Map<String, String> serveMap = appViews.stream().filter(s->StrUtil.isNotBlank(s.getAppIds()))
                .collect(Collectors.toMap(FeignServeAppView::getServiceName, FeignServeAppView::getAppIds));
        BigDecimal resultAdCount = new BigDecimal(0);
        BigDecimal resultRevenue = new BigDecimal(0);
        for (Map.Entry<String, String> entry : serveMap.entrySet()) {
            Object o;
            try {
                o = feignInvoker.invoke(entry.getKey(), "getAdCount",entry.getValue());
            }catch (Exception e){
                log.error(e.getMessage(),e);
                continue;
            }
            JSONObject object = JSONObject.parseObject(JSON.toJSONString(o));
            resultAdCount = resultAdCount.add(object.getBigDecimal("adCount"));
            resultRevenue = resultRevenue.add(object.getBigDecimal("expectRevenue"));
        }
        vo.setAdCount(resultAdCount.intValue());
        vo.setExpectRevenue(resultRevenue);
        return Result.resultObjOk(vo);
    }

    /**
     * 按照排行榜类型、排序规则统计排行榜信息
     */
    @Override
    public Result<DashboardRankingListVO> getRankingList(Integer sortBy) {
        //判断redis中是否存在排行榜信息 不存在则查询数据进行更新，存在则直接取出
        if(Boolean.TRUE.equals(redisUtil.hasKey(RANKING_KEY+sortBy))){
            String ranking = redisUtil.getStr(RANKING_KEY+sortBy);
            long expire = redisUtil.getExpire(RANKING_KEY + sortBy);
            if(Math.subtractExact(rankingExpire,expire)<2){
                getRankingAnCache(sortBy);
            }
            return Result.resultObjOk(JSON.parseObject(ranking, DashboardRankingListVO.class));
        }
        return Result.resultObjOk(getRankingAnCache(sortBy));
    }

    /**
     * 查询排行榜并缓存
     */
    private DashboardRankingListVO getRankingAnCache(Integer sortBy) {

        List<AgentAppView> views = apkMapper.queryAll();
        List<String> serveList = views.stream().map(AgentAppView::getServiceName).distinct().collect(Collectors.toList());
        DashboardRankingListVO rankingVO = new DashboardRankingListVO();
        List<AppRankingListVO> appRankingList = new ArrayList<>();
        List<UserRankingListVO> userRankingList = new ArrayList<>();
        boolean hasError = false;
        for (String serve : serveList) {
            Object o;
            try{
                o = feignInvoker.invoke(serve,"queryRankingList",sortBy,rankingLimit);
            }catch (Exception e){
                log.error(e.getMessage(),e);
                hasError = true;
                continue;
            }
            DashboardRankingListVO vo = JSONObject.parseObject(JSON.toJSONString(o), DashboardRankingListVO.class);
            appRankingList.addAll(vo.getAppRankingList());
            userRankingList.addAll(vo.getUserRankingList());
        }
        //设置应用名称
        Map<String, String> collect = views.stream().collect(Collectors.toMap(AgentAppView::getAppId, AgentAppView::getAppName));
        for (AppRankingListVO vo : appRankingList) {
            vo.setAppName(collect.get(vo.getAppId()));
        }
        rankingVO.setUserRankingList(userRankingList.stream().sorted(Comparator.comparing(UserRankingListVO::getTotalIncome)
                .reversed()).limit(5).collect(Collectors.toList()));
        rankingVO.setAppRankingList(appRankingList.stream().sorted(Comparator.comparing(AppRankingListVO::getTotalRevenue)
                .reversed()).limit(5).collect(Collectors.toList()));
        if(!hasError){
            redisUtil.setTimeOutMinutesStr(RANKING_KEY+sortBy, JSON.toJSONString(rankingVO),rankingExpire);
        }
        return rankingVO;
    }

    /**
     * 根据应用查询各渠道包分时收益
     */
    @Override
    public Result<DashboardRevenueVO> getRevenueStatics(String appId) {
        List<AgentAppView> apkList = apkMapper.queryBySuperiorId(appId);
        String apkIds = apkList.stream().map(AgentAppView::getAppId).collect(Collectors.joining(","));
        YtPlatformUserApp appInfo = appMapper.getByPrimary(appId);
        Object o = feignInvoker.invoke(appInfo.getServiceName(), "revenueStatics", apkIds);
        DashboardRevenueVO vo = JSONObject.parseObject(JSON.toJSONString(o), DashboardRevenueVO.class);
        List<DashboardAppRevenueVO> appRevenueList = vo.getAppRevenueList();
        if (CollUtil.isNotEmpty(appRevenueList)) {
            Map<String, String> appNameMap = apkList.stream().collect(Collectors.toMap(AgentAppView::getAppId, AgentAppView::getAppName));
            for (DashboardAppRevenueVO revenueVO : appRevenueList) {
                revenueVO.setAppName(appNameMap.get(revenueVO.getAppId()));
            }
            vo.setAppRevenueList(appRevenueList);
        }
        return Result.resultObjOk(vo);
    }

    /**
     * 根据应用查询风控用户数据
     */
    @Override
    public Result<DashboardRiskVO> getUserStatics(String appId) {
        AgentAppView view = apkMapper.queryByAppId(appId);
        if(Objects.isNull(view)){
            return Result.resultErr(RepMessage.OBJECT_NOT_EXIST);
        }
        YtPlatformUserApp appInfo = appMapper.getByPrimary(view.getSuperiorId());
        Object o = feignInvoker.invoke(appInfo.getServiceName(), "userStatics", appId);
        DashboardRiskVO vo = JSONObject.parseObject(JSON.toJSONString(o), DashboardRiskVO.class);

        vo.setTodayBannedCount(riskMapper.countBanned(appId,1));
        vo.setYesterdayBannedCount(riskMapper.countBanned(appId,2));
        vo.setMonthBannedCount(riskMapper.countBanned(appId,3));
        List<AppUserHourVO> bannedHour = riskMapper.countBannedHour(appId,1);
        bannedHour = bannedHour.stream().filter(
                s-> StrUtil.isNotBlank(s.getTime())).collect(Collectors.toList());
        if(CollUtil.isNotEmpty(bannedHour)){
            Map<String, Integer> collect = bannedHour.stream().collect(
                    Collectors.toMap(AppUserHourVO::getTime, AppUserHourVO::getCount));
            vo.setTodayBannedHourMap(collect);
        }
        return Result.resultObjOk(vo);
    }
}
