package com.ytpm.lemonios.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
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.alibaba.fastjson.JSONObject;
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.feign.RiskFeign;
import com.ytpm.general.RepMessage;
import com.ytpm.general.Result;
import com.ytpm.general.StatusCode;
import com.ytpm.lemonios.dao.AdRecordMapper;
import com.ytpm.lemonios.dao.AppUserMapper;
import com.ytpm.lemonios.service.AdService;
import com.ytpm.lemonios.service.AppUserService;
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 lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
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.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
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 {

    @Resource
    private AdRecordMapper adRecordMapper;
    @Resource
    private AppUserMapper appUserMapper;
    @Resource
    private RiskFeign riskFeign;
    @Resource
    private AppUserService appUserService;

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

    /**
     * 保存广告记录
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result<?> saveRecord(DyzAdRecordParam param) {
        if (StrUtil.isEmpty(param.getUserId())) {
            // 登陆前保存广告(游客登陆)
            saveRecordAndChangeUser(param, null);
            return Result.resultOk(RepMessage.SAVE_SUCCESS);
        }
        // 登陆后保存广告
        YtDyzUser user = appUserMapper.selectPrimaryKey(param.getUserId());
        // 兜底处理 前端传随机userid
        if (user == null && StrUtil.isNotEmpty(param.getIosId()) && param.getDitchId() != null) {
            user = appUserMapper.getYtAppUserForIos(param.getIosId(), param.getDitchId());
        }
        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()){
            appUserService.addPower(user.getUserId());
            Result<?> result = riskFeign.checkAdRisk(user);
            if(result.getCode()!=200){
                return new Result<>(StatusCode.ACCESS_ERR, getTipsMsg());
            }
        }
        return Result.resultOk(RepMessage.SAVE_SUCCESS);
    }

    /**
     * 保存记录
     *  始终创建新的事务以保障子方法的独立事务
     */
    @Override
    public String saveRecordAndChangeUser(DyzAdRecordParam param, YtDyzUser user) {
        //增加广告记录
        handleParam(param);
        String recordId = IdUtil.fastSimpleUUID();
        YtDyzAdRecord adRecord = new YtDyzAdRecord();
        BeanUtils.copyProperties(param, adRecord);
        adRecord.setRecordId(recordId);
        adRecord.setIosId(param.getIosId());
        // 解析广告平台名称
        String netWorkName = param.getNetworkName();
        if (StrUtil.isEmpty(param.getNetworkName()) && StrUtil.isNotEmpty(param.getNetworkFormId())
                && StrUtil.isNumeric(param.getNetworkFormId().trim())) {
            netWorkName = AdPlatformTypeEnum.getDesc(Integer.parseInt(param.getNetworkFormId()));
        }
        adRecord.setNetworkName(netWorkName);
        // 兼容处理 防止前端传递重复数据 已确认loadId唯一标识广告记录
        String loadId = "";
        if (StrUtil.isNotEmpty(param.getResultJson())) {
            loadId = JSONObject.parseObject(param.getResultJson()).getString("loadId");
        }
        if (StrUtil.isNotEmpty(loadId)) {
            long count = adRecordMapper.getAdCountByLoadId(param.getIosId(), loadId);
            if (count > 0) {
                return null;
            }
        }
        if (user == null || AdRecordEnum.LOGIN_BEFORE.getCode().equals(param.getLoginStatus())) {
            if (user == null) {
                adRecord.setAppId(appId);
            }
            adRecordMapper.addOneVisitor(adRecord);
        } else {
            adRecord.setAppId(user.getAppId());
            adRecord.setUserId(user.getUserId());
            adRecordMapper.addOne(adRecord);
            if (param.getRevenue() == null) {
                param.setRevenue(BigDecimal.ZERO);
            }
            appUserMapper.updateTotal(user.getUserId(), 1, param.getRevenue());
        }
        return recordId;
    }

    /**
     * 统一处理入参时间
     */
    private void handleParam(DyzAdRecordParam param){
        String timeFormat = "yyyy-MM-dd HH:mm:ss";
        if (param.getBegintimestamp() != null && param.getFinishtimestamp() != null) {
            long beginTimestamp = param.getBegintimestamp();
            if (String.valueOf(beginTimestamp).length() == 10) { // 秒级时间戳（10位）
                beginTimestamp *= 1000; // 转为毫秒级
            }
            param.setBeginTime(DateUtil.format(new Date(beginTimestamp), timeFormat));
            long finishTimestamp = param.getFinishtimestamp();
            if (String.valueOf(finishTimestamp).length() == 10) { // 秒级时间戳（10位）
                finishTimestamp *= 1000; // 转为毫秒级
            }
            param.setFinishTime(DateUtil.format(new Date(finishTimestamp), timeFormat));
        }
        if (StrUtil.isEmpty(param.getFinishTime())) {
            param.setFinishTime(DateUtil.format(new Date(), timeFormat));
        }
        if(Objects.isNull(param.getBeginTime()) || "null".equals(param.getBeginTime())){
            if ("null".equals(param.getBeginTime())) {
                log.warn("param beginTime is null !");
            }
            param.setBeginTime(param.getFinishTime());
        }
        // ecpm 值统一解析json获取
        if (StrUtil.isNotEmpty(param.getResultJson())) {
            JSONObject resultJson = JSONObject.parseObject(param.getResultJson());
            String ecpmStr = resultJson.getString("adsource_price");
            if (StrUtil.isNotEmpty(ecpmStr)) {
                param.setEcpm(new BigDecimal(ecpmStr));
            }
        }
    }


    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;
    }

    @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;
            int count = 0;
            for (DyzAdRecordParam adParam : param.getAdRecords()) {
                handleParam(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 (param.getLoginStatus() != null && AdRecordEnum.LOGIN_BEFORE.getCode().equals(param.getLoginStatus())) {
                    continue;
                }
                if (insertEntity.getRevenue() != null) {
                    totalRevenue = totalRevenue.add(insertEntity.getRevenue());
                    count++;
                }
            }
            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(), count, totalRevenue);
        }
        return Result.resultOk(RepMessage.SAVE_SUCCESS);
    }

}
