package com.ytpm.walking.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.ytpm.advertise.enums.AdPlatformTypeEnum;
import com.ytpm.advertise.enums.AdSourceTypeEnum;
import com.ytpm.agent.enums.AdRecordEnum;
import com.ytpm.agent.enums.UserStatusEnum;
import com.ytpm.agent.view.AgentAdGroupStaticsVO;
import com.ytpm.agent.view.AgentTopCountView;
import com.ytpm.app.model.YtDyzAdRecord;
import com.ytpm.app.model.YtDyzUser;
import com.ytpm.app.param.DyzAdRecordParam;
import com.ytpm.app.view.WxDefaultConfig;
import com.ytpm.feign.RiskFeign;
import com.ytpm.general.RepMessage;
import com.ytpm.general.Result;
import com.ytpm.general.StatusCode;
import com.ytpm.middle.view.AppRankingListVO;
import com.ytpm.middle.view.AppRevenueHourVO;
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.UserRankingListVO;
import com.ytpm.walking.dao.AdRecordMapper;
import com.ytpm.walking.dao.AppUserMapper;
import com.ytpm.walking.service.AdService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
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 org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.stream.Collectors;

@Slf4j
@Service
@RefreshScope
public class AdServiceImpl implements AdService {

    @Autowired
    private AdRecordMapper adRecordMapper;
    @Autowired
    private AppUserMapper appUserMapper;

    @Value(" ${risk.config.banned.tips}")
    private String tips;
    @Autowired
    private RiskFeign riskFeign;

    /**
     * 保存广告记录
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result<?> saveRecord(DyzAdRecordParam param) {
        YtDyzUser user = appUserMapper.selectPrimaryKey(param.getUserId());
        log.error(StrUtil.format("[saveRecord]:{} ", param.getBeginTime()));
        if(Objects.isNull(user)){
            return Result.resultOk(RepMessage.SAVE_SUCCESS);
        }
        if(!UserStatusEnum.NORMAL.getCode().equals(user.getUserStatus())){
            return new Result<>(StatusCode.ACCESS_ERR,getTipsMsg());
        }
        saveRecordAndChangeUser(param, user);
        //调用风控广告校验
        if(AdSourceTypeEnum.rewarded_video.getAdSourceType() == param.getAdSourceType()){
            WxDefaultConfig config = appUserMapper.getDefaultConfigByAppId(user.getAppId());
            String format = StrUtil.emptyToDefault(config.getTaskLimitTip(), "当前设备完成任务次数过多，请{}后重试！");
            user.setPowerLimitTip(format);
            Result<?> result = riskFeign.checkAdRisk(user);
            if (result.getCode() != 200) {
                Result<Object> resultObj = new Result<>(StatusCode.ACCESS_ERR, getTipsMsg());
                // 766处理
                if("766".contains(result.getMessage())){
                    resultObj.setData(result.getMessage());
                }
                else if (result.getCode() == StatusCode.ACCESS_RISK_ERROR && StrUtil.isNotBlank(result.getMessage())) {
                    // 体力不再获取处理
                    resultObj.setMessage(result.getMessage());
                    resultObj.setData("767");
                }
                return resultObj;
            }
        }
        return Result.resultOk(RepMessage.SAVE_SUCCESS);
    }

    private String getTipsMsg(){
        String[] split = tips.split(",");
        return split[RandomUtil.randomInt(split.length)];
    }
    /**
     * 根据应用查询广告数量
     */
    @Override
    public Map<String,BigDecimal> getAdCount(String appIds) {
        Map<String, BigDecimal> map = new HashMap<>();
        //查询这些app的用户统计广告数量相加
        int adCount = appUserMapper.getAdCount(appIds);
        BigDecimal total = appUserMapper.getRevenueCount(appIds);
        map.put("adCount", BigDecimal.valueOf(adCount));
        map.put("expectRevenue", total);
        return map;
    }

    /**
     * 查询排行榜信息
     */
    @Override
    public DashboardRankingListVO queryRankingList(Integer sortBy,Integer limit) {
        //查询前五个应用名称 当日收益  用户数量
        List<AppRankingListVO> appRankingList = appUserMapper.getAppRanking(sortBy,limit);
        //查询前五个用户  所属应用  视频数 总收益 ecpm
        List<UserRankingListVO> userRankingList = appUserMapper.getUserRanking(sortBy,limit);
        return new DashboardRankingListVO(appRankingList,userRankingList);
    }

    /**
     *  查询各渠道应用收益分时统计
     */
    @Override
    public DashboardRevenueVO revenueStatics(String apkIds) {
        DashboardRevenueVO vo = new DashboardRevenueVO();
        List<DashboardAppRevenueVO> appRevenueList = new ArrayList<>();
        //查询出各应用的用户及收益数据
        List<String> appIdList = Arrays.asList(apkIds.split(","));
        int index = appIdList.size();
        // 由于子表数据庞大导致range查询低于const
        CountDownLatch countDownLatch = new CountDownLatch(index);
        DashboardAppRevenueVO appRevenueVO;
        String appId;
        do {
            --index;
            appId = appIdList.get(index);
            //根据应用ID查询收益数据及个小时数据  1-今日
            List<AppRevenueHourVO> todayRevenues =  adRecordMapper.getHourRevenue(appId,1);
            Map<String, BigDecimal> todayHourMap = todayRevenues.stream().collect(Collectors.toMap(AppRevenueHourVO::getHour, AppRevenueHourVO::getRevenue));
            appRevenueVO = new DashboardAppRevenueVO();
            appRevenueVO.setAppId(appId);
            //统计今日数据
            BigDecimal todayRevenue = todayHourMap.values().stream().reduce(BigDecimal.ZERO, BigDecimal::add);
            appRevenueVO.setTodayHourRevenueMap(todayHourMap);
            appRevenueVO.setTodayRevenue(todayRevenue);
            //统计昨日数据
            List<AppRevenueHourVO> yesterdayRevenues =  adRecordMapper.getHourRevenue(appId,2);
            Map<String, BigDecimal> yesterdayHourMap = yesterdayRevenues.stream().collect(Collectors.toMap(AppRevenueHourVO::getHour, AppRevenueHourVO::getRevenue));
            appRevenueVO.setYesterdayHourRevenueMap(yesterdayHourMap);
            BigDecimal yesterdayRevenue = yesterdayHourMap.values().stream().reduce(BigDecimal.ZERO, BigDecimal::add);
            appRevenueVO.setYesterdayRevenue(yesterdayRevenue);
            //统计本月数据
            List<AppRevenueHourVO> monthRevenues =  adRecordMapper.getHourRevenue(appId,3);
            BigDecimal monthRevenue = monthRevenues.stream().map(AppRevenueHourVO::getRevenue).reduce(BigDecimal.ZERO, BigDecimal::add);
            appRevenueVO.setMonthRevenue(monthRevenue);
            appRevenueList.add(appRevenueVO);
            countDownLatch.countDown();
        }while (index>0);
        vo.setAppRevenueList(appRevenueList);
        //直接统计各应用的收益
        vo.setTodayTotalRevenue(appRevenueList.stream().map(DashboardAppRevenueVO::getTodayRevenue).reduce(BigDecimal.ZERO, BigDecimal::add));
        vo.setYesterdayTotalRevenue(appRevenueList.stream().map(DashboardAppRevenueVO::getYesterdayRevenue).reduce(BigDecimal.ZERO, BigDecimal::add));
        vo.setMonthTotalRevenue(appRevenueList.stream().map(DashboardAppRevenueVO::getMonthRevenue).reduce(BigDecimal.ZERO, BigDecimal::add));
        return vo;
    }

    /**
     * 查询代理商顶部数据
     */
    @Override
    public AgentTopCountView getAppTopCount(String appIds) {
        AgentTopCountView view = new AgentTopCountView();
        int dyzAdCount = adRecordMapper.countByAppIds(appIds);
        view.setAdCount(dyzAdCount);
        int dyzUserCount = appUserMapper.countUserByAppIds(appIds);
        view.setUserCount(dyzUserCount);
        BigDecimal totalRevenue = new BigDecimal(0);
        BigDecimal dyzRevenue = appUserMapper.countRevenueByAppIds(appIds);
        if(Objects.nonNull(dyzRevenue)){totalRevenue = totalRevenue.add(dyzRevenue);}
        view.setRevenue(totalRevenue);
        int bannedCount = appUserMapper.countBannedByAppIds(appIds);
        view.setRiskCount(bannedCount);
        return view;
    }

    /**
     * 查询用户风控分时数据统计
     */
    @Override
    public DashboardRiskVO userStatics(String appId) {
        DashboardRiskVO vo = new DashboardRiskVO();
        vo.setTodayRegistryCount(appUserMapper.countRegistryUser(appId,1));
        vo.setYesterdayRegistryCount(appUserMapper.countRegistryUser(appId,2));
        vo.setMonthRegistryCount(appUserMapper.countRegistryUser(appId,3));

        vo.setTodayLoginCount(appUserMapper.countLoginUser(appId,1));
        vo.setYesterdayLoginCount(appUserMapper.countLoginUser(appId,2));
        vo.setMonthLoginCount(appUserMapper.countLoginUser(appId,3));

        List<AppUserHourVO> registryHour = appUserMapper.countRegistryHour(appId,1);
        Map<String, Integer> registryMap = registryHour.stream().collect(Collectors.toMap(AppUserHourVO::getTime, AppUserHourVO::getCount));
        vo.setTodayRegistryHourMap(registryMap);

        List<AppUserHourVO> loginHour = appUserMapper.countLoginHour(appId,1);
        Map<String, Integer> loginMap = loginHour.stream().collect(Collectors.toMap(AppUserHourVO::getTime, AppUserHourVO::getCount));
        vo.setTodayLoginHourMap(loginMap);
        return vo;
    }

    /**
     * 查询代理商首页广告数据统计
     */
    @Override
    public List<AgentAdGroupStaticsVO> getAgentProfit(String appIds) {
        //查询今日的 按小时 按广告平台分组数据
        List<AgentAdGroupStaticsVO> vos = new ArrayList<>();
        // 需要查询各 app 今日的广告记录 然后在内存中按照  广告平台分组 计算平均ecpm值， 并且设置每小时的
        List<YtDyzAdRecord> records;
        //昨日所有
        List<YtDyzAdRecord> yestRecords;
        //本月所有
        List<YtDyzAdRecord> monthRecords = new ArrayList<>();

        Calendar cal = Calendar.getInstance(); // 获取当前日期时间
        cal.add(Calendar.MONTH,0);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        //获取到本月起始日
        int actualMinimum = cal.getActualMinimum(Calendar.DAY_OF_MONTH);
        //获取到本月结束日
        int actualMaximum = cal.getActualMaximum(Calendar.DAY_OF_MONTH);
        cal.set(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH),actualMinimum,00,00,00);
        String firstDay = sdf.format(cal.getTime());
        cal.set(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH),actualMaximum,23,59,59);
        String lastDay = sdf.format(cal.getTime());
        List<YtDyzAdRecord> dyzMonthRecords = adRecordMapper.getTodayRecord(appIds,firstDay,lastDay);
        if(CollUtil.isNotEmpty(dyzMonthRecords)){monthRecords.addAll(dyzMonthRecords);}
        records = monthRecords.stream().filter(
                s->DateUtil.isSameDay(DateUtil.parse(s.getFinishTime()),new Date())).collect(Collectors.toList());

        cal = Calendar.getInstance(); // 获取当前日期时间
        cal.add(Calendar.DATE, -1); // 昨天的日期时间
        Date yest = cal.getTime();
        yestRecords = monthRecords.stream().filter(
                s->DateUtil.isSameDay(DateUtil.parse(s.getFinishTime()),yest)).collect(Collectors.toList());
        //广告平台分组
        Map<String, List<YtDyzAdRecord>> networkMap = records.stream().collect(
                Collectors.groupingBy(YtDyzAdRecord::getNetworkFormId));
        //按广告平台分组统计昨天和本月的收益&ecpm
        BigDecimal yestRevenue = yestRecords.stream().map(YtDyzAdRecord::getRevenue).reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal yestEcpm = yestRecords.stream().map(YtDyzAdRecord::getEcpm).reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal monthRevenue = monthRecords.stream().map(YtDyzAdRecord::getRevenue).reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal monthEcpm = monthRecords.stream().map(YtDyzAdRecord::getEcpm).reduce(BigDecimal.ZERO, BigDecimal::add);
        AgentAdGroupStaticsVO vo;
        for (Map.Entry<String, List<YtDyzAdRecord>> adEntry : networkMap.entrySet()) {
            vo = new AgentAdGroupStaticsVO();
            vo.setNetowrkId(adEntry.getKey());
            vo.setNetowrkName(networkMap.get(adEntry.getKey()).get(0).getNetworkName());
            //设置收益
            vo.setTodayRevenue(adEntry.getValue().stream().map(YtDyzAdRecord::getRevenue).reduce(BigDecimal.ZERO, BigDecimal::add));
            vo.setYesterdayRevenue(yestRevenue);
            vo.setMonthRevenue(monthRevenue);
            //设置ecpm
            BigDecimal totalEcpm = adEntry.getValue().stream().map(YtDyzAdRecord::getEcpm).reduce(BigDecimal.ZERO, BigDecimal::add);
            vo.setTodayEcpm(totalEcpm.divide(new BigDecimal(adEntry.getValue().size()), 2, RoundingMode.HALF_UP));
            vo.setYesterdayEcpm(yestEcpm.divide(new BigDecimal(yestRecords.size()),2,RoundingMode.HALF_UP));
            vo.setMonthEcpm(monthEcpm.divide(new BigDecimal(monthRecords.size()),2,RoundingMode.HALF_UP));
            //设置分小时报表
            Map<LocalDateTime, List<YtDyzAdRecord>> hourMap = adEntry.getValue().stream().collect(Collectors.groupingBy(s ->
                    DateUtil.parse(s.getFinishTime(),"yyyy-MM-dd HH").toLocalDateTime()));
            Map<String, BigDecimal> ecpmMap = new HashMap<>();
            Map<String, BigDecimal> revenueMap = new HashMap<>();
            for (Map.Entry<LocalDateTime, List<YtDyzAdRecord>> entry : hourMap.entrySet()) {
                BigDecimal reduce = entry.getValue().stream().map(YtDyzAdRecord::getEcpm).reduce(BigDecimal.ZERO, BigDecimal::add);
                ecpmMap.put(entry.getKey().toString(),reduce.divide(new BigDecimal(entry.getValue().size()), RoundingMode.HALF_UP));
                revenueMap.put(entry.getKey().toString(),hourMap.get(entry.getKey()).stream().map(YtDyzAdRecord::getRevenue).reduce(BigDecimal.ZERO, BigDecimal::add));
            }
            vo.setTodayHourEcpmMap(ecpmMap);
            vo.setTodayHourRevenueMap(revenueMap);
            vos.add(vo);
        }
        return vos;
    }

    /**
     * 保存记录
     *  始终创建新的事务以保障子方法的独立事务
     */
//    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
    @Override
    public String saveRecordAndChangeUser(DyzAdRecordParam param, YtDyzUser user) {
        //增加广告记录
        YtDyzAdRecord adRecord = new YtDyzAdRecord();
        if(Objects.isNull(param.getBeginTime())){
            param.setBeginTime(param.getFinishTime());
        }
        BeanUtils.copyProperties(param, adRecord);
        adRecord.setRecordId(IdUtil.fastSimpleUUID());
        adRecord.setNetworkName(AdPlatformTypeEnum.getDesc(Integer.parseInt(param.getNetworkFormId())));
        if (param.getLoginStatus() != null && AdRecordEnum.LOGIN_BEFORE.getCode().equals(param.getLoginStatus())) {
            adRecordMapper.addOneVisitor(adRecord);
        } else {
            adRecordMapper.addOne(adRecord);
        }
        boolean updateUserTotal = true;
        // 游客登陆下 激励视频广告记录不算入用户统计
        if (AdRecordEnum.LOGIN_BEFORE.getCode().equals(param.getLoginStatus())
                && AdSourceTypeEnum.rewarded_video.getAdSourceType() == param.getAdSourceType()) {
            updateUserTotal = false;
        }
        if (updateUserTotal) {
            //修改用户信息， 广告次数+1  总收益 + revenue
            YtDyzUser dyzUser = new YtDyzUser();
            dyzUser.setUserId(user.getUserId());
            dyzUser.setTotalVideo(Objects.isNull(user.getTotalVideo()) ? 1 : (user.getTotalVideo() + 1));
            dyzUser.setTotalIncome(user.getTotalIncome().add(param.getRevenue()));
            appUserMapper.updateUser(dyzUser);
        }
        return adRecord.getRecordId();
    }

    @Override
    public List<YtDyzAdRecord> queryRecordByIds(List<String> adRecordIds) {
        return adRecordMapper.selectRecordByIds(AdRecordEnum.LOGIN_BEFORE.getCode(), adRecordIds);
    }

}
