package ctop.bytedance.ad.report.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.jeecg.common.api.vo.Result;
import ctop.bytedance.ad.advertise.entity.AiBytedanceAdvertiserStrategy;
import ctop.bytedance.ad.advertise.entity.ByteDanceAdvertisePlan;
import ctop.bytedance.ad.advertise.mapper.AiBytedanceAdvertiserStrategyMapper;
import ctop.bytedance.ad.advertise.mapper.ByteDanceAdvertisePlanMapper;
import ctop.bytedance.ad.advertise.vo.IndustryVo;
import ctop.bytedance.ad.common.utils.Check;
import ctop.bytedance.ad.report.entity.BytedanceAdvertiserHourlyReport;
import ctop.bytedance.ad.report.entity.vo.ReportCostVo;
import ctop.bytedance.ad.report.mapper.BytedanceAdvertiserHourlyReportMapper;
import ctop.bytedance.ad.report.service.IBytedanceAdvertiserHourlyReportService;
import ctop.bytedance.ad.report.utils.TimeStartAndEndUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Description: 广告主小时级别报表信息
 * @Author: jeecg-boot
 * @Date: 2021-05-18
 * @Version: V1.0
 */
@Service
@Primary

public class BytedanceAdvertiserHourlyReportServiceImpl extends ServiceImpl<BytedanceAdvertiserHourlyReportMapper, BytedanceAdvertiserHourlyReport> implements IBytedanceAdvertiserHourlyReportService {


    //时段数组
    private final static List<String> TIMES = Arrays.asList("0","1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19","20","21","22","23");
    public static final String PATTEN_DEFAULT_YMD = "yyyy-MM-dd";


    @Resource
    private BytedanceAdvertiserHourlyReportMapper bytedanceAdvertiserHourlyReportMapper;

    @Resource
    private AiBytedanceAdvertiserStrategyMapper aiBytedanceAdvertiserStrategyMapper;

    @Resource
    private ByteDanceAdvertisePlanMapper byteDanceAdvertisePlanMapper;


    /**
     *
     * @description:
     *
     * @param accountId 账户id
     * @param startTime 开始时间
     * @param endTime 结束时间
     * @return: org.jeecg.common.api.vo.Result
     * @author: zianY
     * @time: 2021/5/19 16:56
     */
    @Override
    public Result getReportYearOnYearCost(String accountId, String startTime, String endTime) throws Exception{
        DecimalFormat df = new DecimalFormat("0.00%");
        Map<String,Object> resultMap = new HashMap<>();
        double compare = 0;
        double lastCost = 0;
        double nowCost = 0;
        List<ReportCostVo> nowCostList = new ArrayList<>();
        List<ReportCostVo> lastCostList = new ArrayList<>();


        //根据本阶段时间 获取 上阶段 开始-截至 时间
        Map<String,String> lastTimeMap = TimeStartAndEndUtil.getStartEndTime(startTime,endTime);

        /**
         * 日期相差不为 0 则 展示 日报（每天数据总和）数据
         * 日期相差天数为0  则 展示 24小时 数据
         */
        if (!"0".equals(lastTimeMap.get("daysBetween"))){
            // 账户 日报 阶段 数据
            nowCostList = bytedanceAdvertiserHourlyReportMapper.selectPlanCostInfoCount(accountId,null,startTime,endTime);
            lastCostList = bytedanceAdvertiserHourlyReportMapper.selectPlanCostInfoCount(accountId,null,lastTimeMap.get("lastTimeStart"),lastTimeMap.get("lastTimeEnd"));
            //当前阶段 总花费
            nowCost = nowCostList.stream().mapToDouble(ReportCostVo::getCost).sum();
            resultMap.put("nowCost", String.format("%.2f", nowCost));
            //上阶段花费
            lastCost = lastCostList.stream().mapToDouble(ReportCostVo::getCost).sum();
            resultMap.put("lastCost", String.format("%.2f", lastCost));
            //环比 =（当前花费 - 上阶段 同时段 花费 ）/ 上阶段花费
            compare = (nowCost - lastCost) / lastCost;
            //resultMap.put("compare", Double.isNaN(compare) ? "0" : df.format(compare));
            resultMap.put("compare", new Double(lastCost).intValue() == 0 ? "0" : String.format("%.2f", compare*100));

        }else{
            // 账户 当前时间段数据
            nowCostList = bytedanceAdvertiserHourlyReportMapper.getReportCostInfoStage(accountId,startTime,endTime);
            //上阶段数据
            lastCostList = bytedanceAdvertiserHourlyReportMapper.getReportCostInfoStageLast(accountId,lastTimeMap.get("lastTimeStart"),lastTimeMap.get("lastTimeEnd"),null);
            //计算环比 =（当前花费 - 上阶段 同时段 花费 ）/ 上阶段花费
            //获取 同时段 的花费
            ReportCostVo statHourVo = new ReportCostVo();
            if (nowCostList.size() > 0){
                statHourVo = nowCostList.get(nowCostList.size()-1);
            }
            List<ReportCostVo> compareCost = bytedanceAdvertiserHourlyReportMapper.getReportCostInfoStageLast(accountId,lastTimeMap.get("lastTimeStart"),lastTimeMap.get("lastTimeEnd"),statHourVo.getTime());
            double lastCompareCost = compareCost.stream().mapToDouble(ReportCostVo::getCost).sum();
            //当前阶段 总花费
            nowCost = nowCostList.stream().mapToDouble(ReportCostVo::getCost).sum();
            resultMap.put("nowCost", String.format("%.2f", nowCost));
            //上阶段花费
            lastCost = lastCostList.stream().mapToDouble(ReportCostVo::getCost).sum();
            resultMap.put("lastCost", String.format("%.2f", lastCost));
            //环比 =（当前花费 - 上阶段 同时段 花费 ）/ 上阶段花费
            compare = (nowCost - lastCompareCost) / lastCompareCost;
            //resultMap.put("compare", Double.isNaN(compare) ? "0" : df.format(compare));
            resultMap.put("compare", new Double(lastCompareCost).intValue() == 0 ? "0" : String.format("%.2f", compare*100));
        }

        //更新时间
        List<ReportCostVo> updTimeList = nowCostList.stream().sorted(Comparator.comparing(ReportCostVo::getCreateTime)).collect(Collectors.toList());
        resultMap.put("updTime",Check.isNull(updTimeList) ? new Date() : updTimeList.get(0).getCreateTime());

        //花费占比 = 花费 / 账户预算
        //获取账户预算
        QueryWrapper<AiBytedanceAdvertiserStrategy> strategyQueryWrapper = new QueryWrapper<>();
        strategyQueryWrapper.eq("account_id",accountId);
        strategyQueryWrapper.eq("status",0);
        strategyQueryWrapper.last("limit 1");
        AiBytedanceAdvertiserStrategy strategy = aiBytedanceAdvertiserStrategyMapper.selectOne(strategyQueryWrapper);
        //没有预算则使用上阶段花费
        double accountBudget = Check.isNull(strategy) || strategy.getAccountBudget().intValue() == 0 ? lastCost : strategy.getAccountBudget().doubleValue();
        //花费占比
        double costShare = nowCost / accountBudget;
        resultMap.put("costShare",new Double(accountBudget).intValue() == 0 ? "0" : df.format(costShare));

        //获取 本阶段  转化成本；转化数；转化率；平均千次展现费用。数据展示
        Map nowMap = getCost(nowCostList);
        // 上阶段数据
        Map lastMap = getCost(lastCostList);

        if (nowCostList.size() > 0){
            //当前阶段 每个时间 的 转化成本；转化数；转化率；平均千次展现费用。数据展示
            nowMap.put("costList", getCostList(nowCostList,startTime,endTime,lastTimeMap.get("daysBetween")));
        }else {
            nowMap.put("costList",Arrays.asList());
        }

        if (lastCostList.size() > 0){
            //上个阶段 每个时间 的 转化成本；转化数；转化率；平均千次展现费用。数据展示
            lastMap.put("costList", getCostList(lastCostList,lastTimeMap.get("lastTimeStart"),lastTimeMap.get("lastTimeEnd"),lastTimeMap.get("daysBetween")));
        }else {
            lastMap.put("costList",Arrays.asList());
        }

        resultMap.put("nowMap", nowMap);
        resultMap.put("lastMap", lastMap);
        return Result.successMsg("成功。",resultMap);
    }


    /**
     * 获取 数据 （总）
     * @param list
     * @return
     */
    public Map<String,Object> getCost(List<ReportCostVo> list){
        Map<String,Object> map = new HashMap<>();
        //花费
        double cost = list.stream().mapToDouble(ReportCostVo::getCost).sum();
        //转化成本 = 总花费 / 转化数
        int converNum = list.stream().mapToInt(ReportCostVo::getConvertNum).sum();
        double conver = cost / new Double(converNum);
        map.put("conver",converNum == 0 ? "0" : String.format("%.2f", conver));
        map.put("converNum", converNum);
        //转化率 = 转化数 / 点击数 * 100
        int clickNum = list.stream().mapToInt(ReportCostVo::getClick).sum();
        double click = Double.valueOf(converNum) /  Double.valueOf(clickNum) * 100;
        map.put("converRate", clickNum == 0 ? "0" : String.format("%.2f", click));
        // 平均千次展显费用 = 总花费/展示数 * 1000
        int showNum = list.stream().mapToInt(ReportCostVo::getShowNum).sum();
        double show = (cost / showNum) * 1000 ;
        map.put("averageShow", showNum == 0 ? "0" : String.format("%.2f", show));
        return map;
    }

    //获取 数据 （时间段）
    public List<ReportCostVo> getCostList(List<ReportCostVo> list,String startTime,String endTime,String daysBetween){
        list.forEach(reportCostVo -> {
            //花费
            double cost = reportCostVo.getCost();
            //转化成本 = 总花费 / 转化数
            int converNum = reportCostVo.getConvertNum();
            double conver = cost / converNum;
            reportCostVo.setConver(converNum == 0 ? "0" : String.format("%.2f", conver));
            reportCostVo.setConvertNum(converNum);

            //转化率 = 转化数 / 点击数 * 100
            int clickNum = reportCostVo.getClick();
            double click = Double.valueOf(converNum) /  Double.valueOf(clickNum) * 100;
            reportCostVo.setConverRate(clickNum == 0 ? "0" : String.format("%.2f", click));

            // 平均千次展显费用 = 总花费/展示数 * 1000
            int showNum = reportCostVo.getShowNum();
            double show = (cost / showNum) * 1000 ;
            reportCostVo.setAverageShow( showNum == 0 ? "0" : String.format("%.2f", show));
        });

        /**
         * 时间间隔为0 则表示获取当天数据 0-23时段
         * 不为0 则表示 获取 开始-截至 时间 阶段数据
         */
        List<String> reduceList = new ArrayList<>();
        if ("0".equals(daysBetween)){
            //返回数据中 time 的集合
            List<String> voTimes = list.stream().map(ReportCostVo::getTime).collect(Collectors.toList());
            // 获取0-23时段 和 已有时段的 差集 （times - voTimes）
            reduceList = TIMES.stream().filter(item -> !voTimes.contains(item)).collect(Collectors.toList());
        }else {
            // 获取两个日期之间 所有的 时间 年-月-日
            List<String> betweenDaysList = TimeStartAndEndUtil.getAllDatesOfTwoTimes(startTime,endTime);
            ////返回数据中 time 的集合
            List<String> dayTimes = list.stream().map(ReportCostVo::getTime).collect(Collectors.toList());
            //差集
            reduceList = betweenDaysList.stream().filter(item -> !dayTimes.contains(item)).collect(Collectors.toList());
        }
        //补充差集时段数据
        if (!Check.isNull(reduceList)){
            reduceList.forEach(reduce ->{
                ReportCostVo vo = new ReportCostVo();
                vo.setStatDatetime(startTime);
                vo.setTime(reduce);
                vo.setConver("0");//转化成本
                vo.setConvertNum(0);//转化数
                vo.setConverRate("0");//转化率
                vo.setAverageShow("0");//平均前次展现费用
                list.add(vo);
            });
        }
        //排序
        if ("0".equals(daysBetween)){
            list.sort((ReportCostVo vo1, ReportCostVo vo2) -> Integer.valueOf(vo1.getTime()).compareTo(Integer.valueOf(vo2.getTime())));
        }else {
            Collections.sort(list,Comparator.comparing(ReportCostVo::getTime));
        }
        return list;
    }



    /**
     *
     * @description:
     *
     * @param userId 当前登录用户id
     * @param type 类型 kuaishou bytedance
     * @return: org.jeecg.common.api.vo.Result
     * @author: zianY
     */
    @Override
    public Result initAccountIdAndTime(String userId, String type) throws Exception {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
        Map<String,Object> resultMap = new HashMap<>();
        Map<String,Object> map = bytedanceAdvertiserHourlyReportMapper.selAcountIdCostMax(userId,type);
        resultMap.put("startTime", formatter.format(new Date()));
        resultMap.put("endTime", formatter.format(new Date()));
        resultMap.put("account", map);
        return Result.successMsg("成功",resultMap);
    }


    /**
     *
     * @description: 查询账户余额；花费
     *
     * @param accountId
     * @return: org.jeecg.common.api.vo.Result
     * @author: zianY
     */
    @Override
    public Result getAccountCost(String accountId) throws Exception {

        Map<String,Object> resultMap = new HashMap<>();
        Map<String,Object> accountMap = bytedanceAdvertiserHourlyReportMapper.selectAccountCost(accountId);
        //获取账户预算
        QueryWrapper<AiBytedanceAdvertiserStrategy> strategyQueryWrapper = new QueryWrapper<>();
        strategyQueryWrapper.eq("account_id",accountId);
        strategyQueryWrapper.eq("status",0);
        strategyQueryWrapper.last("limit 1");
        AiBytedanceAdvertiserStrategy strategy = aiBytedanceAdvertiserStrategyMapper.selectOne(strategyQueryWrapper);
        double accountBudget = Check.isNull(strategy) ? 0 : strategy.getAccountBudget().doubleValue();
        resultMap.put("accountMap", accountMap);
        resultMap.put("accountBudget", accountBudget);

        return Result.successMsg("成功。", resultMap);
    }



    /**
     *
     * @description:查询计划数据
     *
     * @param accountId 账户id
     * @param startTime 开始时间
     * @param endTime 结束时间
     * @return: org.jeecg.common.api.vo.Result
     * @author: zianY
     */
    @Override
    public Result getReportPlanCost(String accountId, String startTime, String endTime,Integer pageNum,Integer pageSize) throws Exception {
        SimpleDateFormat sf = new SimpleDateFormat(PATTEN_DEFAULT_YMD);
        DecimalFormat df = new DecimalFormat("0.00%");
        DecimalFormat df1 = new DecimalFormat("0.00");
        List<ReportCostVo> reportCostVoList = new ArrayList<ReportCostVo>();
        List<ReportCostVo> lastReportCostVoList = new ArrayList<ReportCostVo>();
        //根据本阶段时间 获取 上阶段 开始-截至 时间
        Map<String,String> lastTimeMap = TimeStartAndEndUtil.getStartEndTime(startTime,endTime);
        String nowTime = sf.format(new Date());

        if (!startTime.equals(nowTime)){
            //日报
            PageHelper.startPage(pageNum,pageSize);
            reportCostVoList = bytedanceAdvertiserHourlyReportMapper.selectReportPlanCostInfoDay(accountId, null, startTime,endTime);
        }else {
            PageHelper.startPage(pageNum,pageSize);
            //时报
           reportCostVoList = bytedanceAdvertiserHourlyReportMapper.selectReportPlanCostInfo(accountId, null, startTime,endTime);

        }


        for (ReportCostVo reportCostVo : reportCostVoList) {
            reportCostVo.setCostStr(String.format("%.2f",reportCostVo.getCost()));
            //出价  ctop_bytedance_advertise_plan 广告计划信息表
            QueryWrapper<ByteDanceAdvertisePlan> planQueryWrapper = new QueryWrapper<>();
            planQueryWrapper.eq("account_id",accountId);
            planQueryWrapper.eq("id",reportCostVo.getAdId());
            planQueryWrapper.last("limit 1");
            ByteDanceAdvertisePlan planInfo = byteDanceAdvertisePlanMapper.selectOne(planQueryWrapper);
            reportCostVo.setCpaBid(Check.isNull(planInfo) ? "0" : String.format("%.2f",planInfo.getCpaBid()));
            reportCostVo.setBid(Check.isNull(planInfo) ? "0" : String.format("%.2f",planInfo.getBid()));
            //广告预算 （修改出价不能大于广告计划预算）
            reportCostVo.setAdBudget(Check.isNull(planInfo) ? new BigDecimal(0) : planInfo.getBudget());

            // 平均千次展显费用 = 总花费/展示数 * 1000
            double show = 0;
            if (reportCostVo.getShowNum() != 0){
                show = (reportCostVo.getCost() / reportCostVo.getShowNum()) * 1000 ;
            }
            reportCostVo.setAverageShow(String.format("%.2f", show));

            double avgClick = 0;
            double clickRate = 0;
            double convetRate = 0;
            if (reportCostVo.getClick() != 0){
                //平均点击单价 = 花费 / 点击数
                avgClick = reportCostVo.getCost() / reportCostVo.getClick().doubleValue();
                //点击率 = 点击数 / 展示数
                clickRate = reportCostVo.getClick().doubleValue() / reportCostVo.getShowNum().doubleValue();
                //转化率 = 转化数 / 点击数 * 100
                convetRate = Double.valueOf(reportCostVo.getConvertNum()) /  Double.valueOf(reportCostVo.getClick()) * 100;
            }

            reportCostVo.setAverageClick(String.format("%.2f", avgClick));
            reportCostVo.setClickRate(df.format(clickRate));
            reportCostVo.setConverRate(String.format("%.2f", convetRate));

            //转化成本 = 总花费 / 转化数
            double conver = 0;
            if (reportCostVo.getConvertNum() != 0){
                conver = reportCostVo.getCost() / reportCostVo.getConvertNum();
            }
            reportCostVo.setConver(String.format("%.2f", conver));

            //日报
            if (!startTime.equals(nowTime)){
                // 计划id 和 上阶段 时间 查询 上阶段数据
                lastReportCostVoList = bytedanceAdvertiserHourlyReportMapper.selectReportPlanCostInfoDay(accountId, reportCostVo.getAdId(), lastTimeMap.get("lastTimeStart"),lastTimeMap.get("lastTimeEnd"));
            }else {
                //时报
                //查询上阶段 同时间点 之前的 时报 数据信息
                lastReportCostVoList = bytedanceAdvertiserHourlyReportMapper.selectReportPlanCostHourLast(accountId,reportCostVo.getAdId(),lastTimeMap.get("lastTimeStart"),lastTimeMap.get("lastTimeEnd"),reportCostVo.getStatHour());
            }
            double compare = 0;
            if (lastReportCostVoList.size() > 0){
                if (lastReportCostVoList.get(0).getCost() !=0){
                    //环比 =（当前花费 - 上阶段花费 ）/ 上阶段花费
                    compare = (reportCostVo.getCost() - lastReportCostVoList.get(0).getCost()) / lastReportCostVoList.get(0).getCost();
                }
            }
            reportCostVo.setCompare(df.format(compare));

            //操作状态
            QueryWrapper<ByteDanceAdvertisePlan> advertisePlanQueryWrapper = new QueryWrapper<>();
            advertisePlanQueryWrapper.eq("account_id",accountId);
            advertisePlanQueryWrapper.eq("id",reportCostVo.getAdId());
            advertisePlanQueryWrapper.last("limit 1");
            ByteDanceAdvertisePlan advertisePlan = byteDanceAdvertisePlanMapper.selectOne(advertisePlanQueryWrapper);
            reportCostVo.setOptStatus(Check.isNull(advertisePlan) ? "0" : advertisePlan.getOptStatus());
        }
        PageInfo<ReportCostVo> pageInfo = new PageInfo<ReportCostVo>(reportCostVoList);
        return Result.successMsg("成功。", pageInfo);
    }


}
