package com.ytpm.question.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
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.DyzAdRecordDelayParam;
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.question.dao.AdRecordMapper;
import com.ytpm.question.dao.AppUserMapper;
import com.ytpm.question.service.AdService;
import com.ytpm.question.service.AppUserService;
import com.ytpm.question.view.AgentNetworkAgg;
import com.ytpm.util.DateUtil;
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 javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Arrays;
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;
    @Autowired
    private RiskFeign riskFeign;
    @Resource
    private AppUserService appUserService;

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

    /**
     * 保存广告记录
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result<String> saveRecord(DyzAdRecordParam param) {
        YtDyzUser user = appUserMapper.selectPrimaryKey(param.getUserId());
        if (Objects.isNull(user)) {
            return Result.resultOk(RepMessage.SAVE_SUCCESS);
        }
        if (!UserStatusEnum.NORMAL.getCode().equals(user.getUserStatus())) {
            return new Result<>(StatusCode.ACCESS_ERR, getTipsMsg());
        }
        log.debug(StrUtil.format("[saveRecord] userId:{} revenue:{} ", param.getUserId(), param.getRevenue()));
        saveRecordAndChangeUser(param, user);
        //调用风控广告校验
        if (AdSourceTypeEnum.rewarded_video.getAdSourceType() == param.getAdSourceType()) {
            appUserService.addPower(user.getUserId());
            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<String> 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 Result<String> delaySaveRecord(DyzAdRecordDelayParam param) {
        YtDyzUser user = appUserMapper.selectPrimaryKey(param.getUserId());
        if (Objects.isNull(user)) {
            return Result.resultOk(RepMessage.SAVE_SUCCESS);
        }
        if (!UserStatusEnum.NORMAL.getCode().equals(user.getUserStatus())) {
            return new Result<>(StatusCode.ACCESS_ERR, getTipsMsg());
        }
        log.info("[delaySaveRecord] userId:{}", param.getUserId());
        if (CollectionUtil.isNotEmpty(param.getAdRecords())) {
            YtDyzAdRecord insertEntity;
            List<YtDyzAdRecord> saveList = new ArrayList<>();
            BigDecimal totalRevenue = BigDecimal.ZERO;
            for (DyzAdRecordParam adParam : param.getAdRecords()) {
                handleTimeNull(adParam);
                insertEntity = new YtDyzAdRecord();
                BeanUtils.copyProperties(adParam, insertEntity);
                insertEntity.setRecordId(IdUtil.fastSimpleUUID());
                insertEntity.setUserId(user.getUserId());
                insertEntity.setNetworkName(AdPlatformTypeEnum.getDesc(Integer.parseInt(adParam.getNetworkFormId())));
                saveList.add(insertEntity);
                if (AdRecordEnum.LOGIN_BEFORE.getCode().equals(param.getLoginStatus())
                        && AdSourceTypeEnum.rewarded_video.getAdSourceType() == adParam.getAdSourceType()) {
                    continue;
                }
                if (insertEntity.getRevenue() != null) {
                    totalRevenue = totalRevenue.add(insertEntity.getRevenue());
                }
            }
            if (CollUtil.isNotEmpty(saveList)) {
                if (param.getLoginStatus() != null && AdRecordEnum.LOGIN_BEFORE.getCode().equals(param.getLoginStatus())) {
                    adRecordMapper.batchAddVisitor(saveList);
                } else {
                    adRecordMapper.batchAdd(saveList);
                }
            }
            appUserMapper.updateTotal(user.getUserId(), saveList.size(), totalRevenue);
        }
        return Result.resultOk(RepMessage.SAVE_SUCCESS);
    }

    /**
     * 处理传入广告记录 beginTime FinishTime可能为null的情况
     */
    private void handleTimeNull(DyzAdRecordParam param) {
        param.setFinishTime(verifyTimeParam("finishTime", param.getFinishTime(), null));
        param.setBeginTime(verifyTimeParam("beginTime", param.getBeginTime(), param.getFinishTime()));
    }

    private String verifyTimeParam(String fieldName, String value, String defaultValue) {
        if (StrUtil.isEmpty(value) || "null".equals(value)) {
            if ("null".equals(value)) {
                log.warn("param[{}] value '{}' is null !", fieldName, value);
            }
            return StrUtil.isEmpty(defaultValue) ? DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss") : defaultValue;
        }
        return value;
    }


    /**
     * 根据应用查询广告数量
     */
    @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;
    }

    /**
     * 查询排行榜信息
     * @param sortBy 1-当日 2-本周 3-本月
     */
    @Override
    public DashboardRankingListVO queryRankingList(Integer sortBy, Integer limit) {
        Date beginTime,endTime;
        switch (sortBy) {
            case 1:
                beginTime = DateUtil.getTodayStart();
                endTime = DateUtil.getTodayEnd();
                break;
            case 2:
                beginTime = DateUtil.getWeekStart();
                endTime = DateUtil.getWeekEnd();
                break;
            default:
                beginTime = DateUtil.getMonthStart();
                endTime = DateUtil.getMonthEnd();
        }
        //查询前五个应用名称 当日收益  用户数量
        List<AppRankingListVO> appRankingList = appUserMapper.getAppRanking(beginTime,endTime, limit);
        //查询前五个用户  所属应用  视频数 总收益 ecpm
        List<UserRankingListVO> userRankingList = appUserMapper.getUserRanking(beginTime,endTime, 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<AgentNetworkAgg> todayHourAgg = adRecordMapper.getTodayAggByNetworkHour(appIds);
        List<AgentNetworkAgg> yesterdayAgg = adRecordMapper.getYesterdayAggByNetwork(appIds);
        List<AgentNetworkAgg> monthAgg = adRecordMapper.getMonthAggByNetwork(appIds);

        Map<String, List<AgentNetworkAgg>> todayByNetwork = todayHourAgg.stream().collect(Collectors.groupingBy(AgentNetworkAgg::getNetworkId));
        Map<String, AgentNetworkAgg> yesterdayByNetwork = yesterdayAgg.stream().collect(Collectors.toMap(AgentNetworkAgg::getNetworkId, v -> v, (a, b) -> a));
        Map<String, AgentNetworkAgg> monthByNetwork = monthAgg.stream().collect(Collectors.toMap(AgentNetworkAgg::getNetworkId, v -> v, (a, b) -> a));

        List<AgentAdGroupStaticsVO> result = new ArrayList<>();
        for (Map.Entry<String, List<AgentNetworkAgg>> e : todayByNetwork.entrySet()) {
            String networkId = e.getKey();
            List<AgentNetworkAgg> hourly = e.getValue();
            AgentAdGroupStaticsVO vo = new AgentAdGroupStaticsVO();
            vo.setNetowrkId(networkId);
            vo.setNetowrkName(hourly.get(0).getNetworkName());

            BigDecimal todayRevenue = hourly.stream().map(AgentNetworkAgg::getRevenue).reduce(BigDecimal.ZERO, BigDecimal::add);
            BigDecimal todayEcpm = hourly.stream().map(AgentNetworkAgg::getEcpm).reduce(BigDecimal.ZERO, BigDecimal::add)
                    .divide(new BigDecimal(Math.max(1, hourly.size())), 2, RoundingMode.HALF_UP);

            AgentNetworkAgg yAgg = yesterdayByNetwork.get(networkId);
            AgentNetworkAgg mAgg = monthByNetwork.get(networkId);

            vo.setTodayRevenue(todayRevenue);
            vo.setYesterdayRevenue(yAgg != null ? yAgg.getRevenue() : BigDecimal.ZERO);
            vo.setMonthRevenue(mAgg != null ? mAgg.getRevenue() : BigDecimal.ZERO);

            vo.setTodayEcpm(todayEcpm);
            vo.setYesterdayEcpm(yAgg != null ? yAgg.getEcpm() : BigDecimal.ZERO);
            vo.setMonthEcpm(mAgg != null ? mAgg.getEcpm() : BigDecimal.ZERO);

            Map<String, BigDecimal> hourRev = new HashMap<>();
            Map<String, BigDecimal> hourEcpm = new HashMap<>();
            for (AgentNetworkAgg a : hourly) {
                hourRev.put(a.getTime(), a.getRevenue());
                hourEcpm.put(a.getTime(), a.getEcpm());
            }
            vo.setTodayHourRevenueMap(hourRev);
            vo.setTodayHourEcpmMap(hourEcpm);
            result.add(vo);
        }
        return result;
    }

    /**
     * 保存记录
     * 始终创建新的事务以保障子方法的独立事务
     */
    @Override
//    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
    public String saveRecordAndChangeUser(DyzAdRecordParam param, YtDyzUser user) {
        //增加广告记录
        handleTimeNull(param);
        YtDyzAdRecord adRecord = new YtDyzAdRecord();
        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
            if (param.getRevenue() == null) {
                param.setRevenue(BigDecimal.ZERO);
            }
            appUserMapper.updateTotal(user.getUserId(), 1, param.getRevenue());
        }
        return adRecord.getRecordId();
    }

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

}
