package cn.com.ctop.kuaishou.modules.report.service.impl;

import cn.com.ctop.common.module.entity.CtopOauthToken;
import cn.com.ctop.common.module.mapper.UserAllocationMapper;
import cn.com.ctop.common.module.service.ICtopOauthTokenService;
import cn.com.ctop.common.module.service.IMessageTemplate;
import cn.com.ctop.common.module.service.ISendMessageService;
import cn.com.ctop.common.module.utils.Check;
import cn.com.ctop.common.module.utils.HttpUtils;
import cn.com.ctop.common.module.utils.KuaishouInterfaceConstant;
import cn.com.ctop.common.module.utils.PropertiesUtils;
import cn.com.ctop.kuaishou.modules.batch.entity.KuaiShouCreative;
import cn.com.ctop.kuaishou.modules.batch.mapper.KuaiShouCreativeMapper;
import cn.com.ctop.kuaishou.modules.batch.service.IKuaiShouUpdateService;
import cn.com.ctop.kuaishou.modules.report.entity.KuaishouZombieCreative;
import cn.com.ctop.kuaishou.modules.report.mapper.KuaishouReportDailyCreativeMapper;
import cn.com.ctop.kuaishou.modules.report.mapper.KuaishouZombieCreativeMapper;
import cn.com.ctop.kuaishou.modules.report.service.IKuaishouZombieCreativeService;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xxl.job.core.enums.NoEn;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.util.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 僵尸计划记录
 *
 * @author jeecg-boot
 * @version V1.0
 * @date 2020-10-14
 */
@Slf4j
@Service
public class KuaishouZombieCreativeServiceImpl extends ServiceImpl<KuaishouZombieCreativeMapper, KuaishouZombieCreative> implements IKuaishouZombieCreativeService {
    @Autowired
    private KuaishouReportDailyCreativeMapper kuaishouReportDailyCreativeMapper;
    @Autowired
    private KuaishouZombieCreativeMapper kuaishouZombieCreativeMapper;
    @Autowired
    private KuaiShouCreativeMapper kuaiShouCreativeMapper;
    @Autowired
    private ISendMessageService sendMessageService;
    @Autowired
    private IMessageTemplate messageTemplate;
    @Autowired
    private UserAllocationMapper userAllocationMapper;
    @Autowired
    private ICtopOauthTokenService oauthTokenService;
    @Autowired
    private IKuaiShouUpdateService kuaiShouUpdateService;

    /**
     * 筛选僵尸计划
     * 逻辑简述：
     * 1、以当天时间为节点，往前取五天的数据，若创意消耗（花费）都为0，则获取其数据，并insert为待确认状态的僵尸创意。
     * 2、获取前一天存储的待确认僵尸计划
     * 3、步骤二和步骤一的数据作比对筛选，若两组数据都含有该创意时，并且为了保证数据的准确性，还需调用API接口获取当天实时消耗明细，
     * 若消耗不为0，则表明为真正的僵尸创意，3.1关停创意；3.2然后发送“关停通知”消息;3.3最后更新其状态。
     * 4、步骤二存在，步骤一不存在说明创意已经活跃，更新其状态为“2”已活跃、反之则是新的待确认僵尸创意，作insert操作
     * 5、insert操作时，考虑到数据重复的问题，需先查询全部僵尸计划，作筛选比对，已有则更新，否则就新增。
     * **凡insert数据皆为状态0，待确认僵尸创意，且要发送“提醒通知”。
     *
     * @param
     * @return void
     * @throws
     * @author ZHAOXA
     */

    @Override
    public void checkZombieProject() {
        log.info("-----------------start---------------");
        long startTime = System.currentTimeMillis();
        //获取五天前的日期 yyyy-MM-dd
        String date = DateUtils.getLastDay(DateUtils.formatDate(new Date()), NoEn.NO5.valueInt());
        //查询五天内花费都为0的数据集
        List<JSONObject> list = kuaishouReportDailyCreativeMapper.checkZombieProject(date);
        //查询审核未通过且超过5天的创意集
        List<JSONObject> olist = kuaiShouCreativeMapper.checkZombieProject(date);
        if (Check.isNull(list) && Check.isNull(olist)) {
            return;
        } else {
            list.addAll(olist);
        }
        //查询数据库中前一天存储的待确认僵尸创意集
        Map<String, Object> map = new HashMap<>();
        map.put("record_time", DateUtils.getLastDay(DateUtils.formatDate(new Date()), NoEn.NO1.valueInt()));
        map.put("status", NoEn.NO0.valueStr());
        List<KuaishouZombieCreative> zombieCreatives = kuaishouZombieCreativeMapper.selectByMap(map);
        if (!Check.isNull(zombieCreatives)) {
            List<KuaishouZombieCreative> zombiePlans = new ArrayList<>();
            List<JSONObject> zombieList = new ArrayList<>();
            //通过账户和创意ID,筛选出确认的僵尸计划账户
            long tims = System.currentTimeMillis();
            log.info("-----获取数据集---------用时：{} ms", tims - startTime);
            zombieCreatives.forEach(zombie -> {
                list.forEach(obj -> {
                    if (zombie.getAccountId() - obj.getInteger("accountId") == 0 && zombie.getCreativeId() - obj.getInteger("creativeId") == 0) {
                        QueryWrapper<KuaishouZombieCreative> wrapper = new QueryWrapper<>();
                        wrapper.eq("id", zombie.getId());
                        CtopOauthToken token = oauthTokenService.getTokenByAccountId(zombie.getAccountId());
                        //校验当天是不是依旧不活跃，是则关停，否则更新激活状态
                        if (isActivation(token, zombie.getCreativeId())) {
                            //3.1关停创意
                            Map<String, Object> updateMap = kuaiShouUpdateService.updateCreativeStatus(token.getAccessToken(), zombie.getAccountId(), zombie.getCreativeId(), NoEn.NO2.valueInt(), obj.getString("userId"));
                            if (!Check.isNull(updateMap) && (boolean) updateMap.get("success")) {
                                //3.2发送创意关停通知
                                sendMsg(NoEn.NO2.valueStr(), obj);
                            }
                            zombie.setStatus(NoEn.NO1.valueStr());
                            //记录已确认的僵尸计划集
                            zombiePlans.add(zombie);
                            zombieList.add(obj);
                        } else {
                            zombie.setStatus(NoEn.NO2.valueStr());
                        }
                        //3.3更新状态
                        kuaishouZombieCreativeMapper.update(zombie, wrapper);
                        //校验数据为审核未通过数据，并将投放状态设为“暂停”
                        if (Check.isNull(obj.getInteger("dates"))) {
                            QueryWrapper<KuaiShouCreative> creativeWrapper = new QueryWrapper<>();
                            creativeWrapper.eq("id", obj.getString("id"));
                            KuaiShouCreative creative = new KuaiShouCreative();
                            //投放状态 1：投放中；2：暂停
                            creative.setPutStatus(NoEn.NO2.valueInt());
                            kuaiShouCreativeMapper.update(creative, creativeWrapper);
                        }
                    }
                });
            });
            log.info("-----筛选逻辑---------用时：{} ms", System.currentTimeMillis() - tims);
            //过滤掉已确认的僵尸数据，剩余活跃的计划
            zombieCreatives.removeAll(zombiePlans);
            //更新计划状态为已活跃
            zombieCreatives.forEach(zom -> {
                QueryWrapper<KuaishouZombieCreative> wrapper = new QueryWrapper<>();
                wrapper.eq("id", zom.getId());
                //状态 0-待确认，1-是僵尸计划，2-已活跃，不是僵尸计划
                zom.setStatus(NoEn.NO2.valueStr());
                kuaishouZombieCreativeMapper.update(zom, wrapper);
            });
            //过滤掉已确认的数据，新增新一批的僵尸计划
            list.removeAll(zombieList);
            insertorUpdate(list);
        } else {
            insertorUpdate(list);
        }
        long endTime = System.currentTimeMillis();
        log.info("---end---共用时：{} ms", endTime - startTime);
    }

    /**
     * 筛选僵尸计划数据，新增或更新操作
     *
     * @param
     * @return void
     * @throws
     * @author ZHAOXA
     */
    private void insertorUpdate(List<JSONObject> list) {
        //查询库中已有的全部数据
        List<KuaishouZombieCreative> kuaishouZombieCreatives = kuaishouZombieCreativeMapper.selectList(null);
        if (!Check.isNull(kuaishouZombieCreatives)) {
            List<KuaishouZombieCreative> updateList = new ArrayList<>();
            List<JSONObject> toBeDeleted = new ArrayList<>();
            //待新增的和库中数据对比筛选，选出相同账户和相同创意的数据
            list.forEach(obj -> {
                kuaishouZombieCreatives.forEach(zom -> {
                    if (zom.getAccountId() - obj.getInteger("accountId") == 0 && zom.getCreativeId() - obj.getInteger("creativeId") == 0) {
                        updateList.add(zom);
                        toBeDeleted.add(obj);
                    }
                });
            });
            //已有的数据作更新操作
            if (!Check.isNull(updateList)) {
                updateList.forEach(zom -> {
                    QueryWrapper<KuaishouZombieCreative> wrapper = new QueryWrapper<>();
                    wrapper.eq("id", zom.getId());
                    zom.setStatus(NoEn.NO0.valueStr());
                    zom.setRecordTime(DateUtils.getNowDate(DateUtils.WEB_FORMAT));
                    kuaishouZombieCreativeMapper.update(zom, wrapper);
                });
            }
            //去除已有的数据，新的数据作insert
            list.removeAll(toBeDeleted);
            insert(list);
        } else {
            insert(list);
        }
    }

    /**
     * 新增待确认僵尸数据,并发送消息通知
     * userAllocation
     *
     * @param
     * @return void
     * @throws
     * @author ZHAOXA
     */
    private void insert(List<JSONObject> list) {
        list.forEach(obj -> {
            KuaishouZombieCreative create = new KuaishouZombieCreative();
            create.setAccountId(obj.getLong("accountId"));
            create.setCreativeId(obj.getLong("creativeId"));
            create.setCreativeName(obj.getString("creativeName"));
            create.setRecordTime(DateUtils.getNowDate(DateUtils.WEB_FORMAT));
            //状态 0-待确认，1-是僵尸计划，2-已活跃，不是僵尸计划
            create.setStatus(NoEn.NO0.valueStr());
            kuaishouZombieCreativeMapper.insert(create);
            sendMsg(NoEn.NO1.valueStr(), obj);
        });
    }

    /**
     * 发送消息通知
     *
     * @param
     * @return void
     * @throws
     * @author ZHAOXA
     */
    private void sendMsg(String type, JSONObject obj) {
        String userId = userAllocationMapper.queryUserIdByAccountId(obj.getLong("accountId"));
        String message = "";
        if (NoEn.NO1.valueStr().equals(type)) {
            //提醒通知
            message = messageTemplate.getZombieCreativeRemindTemplate(obj);
        } else {
            //关停通知
            message = messageTemplate.getZombieCreativeShutDownTemplate(obj);
        }
        sendMessageService.sendMessage(userId, message);
    }

    /**
     * 检查创意是否激活
     *
     * @param
     * @return boolean
     * @throws
     * @author ZHAOXA
     */
    private boolean isActivation(CtopOauthToken oauthToken, long creativeId) {
        boolean flag = true;
        try {
            JSONArray creativeIds = new JSONArray();
            creativeIds.add(creativeId);
            JSONObject info = getAdvertiserCreativeReportDailyInfo(oauthToken, new Date(), new Date(), creativeIds);
            if (!Check.isNull(info) && !Check.isNull(info.getBigDecimal("charge"))) {
                flag = false;
            }
        } catch (Exception e) {
            log.error("校验创意是否激活，异常", e);
            //异常时，默认为激活状态
            flag = false;
        }
        return flag;
    }

    private JSONObject getAdvertiserCreativeReportDailyInfo(CtopOauthToken token, Date startDate, Date endDate, JSONArray creativeIds) throws Exception {
        String url = PropertiesUtils.getConfig("kuaishou_api_url") + KuaishouInterfaceConstant.CREATIVE_REPORT;
        Map<String, String> headers = new HashMap<String, String>();
        headers.put("Content-Type", "application/json");
        headers.put("Access-Token", token.getAccessToken());
        Map<String, Object> param = new HashMap<String, Object>();
        if (!Check.isNull(startDate)) {
            param.put("start_date", DateUtils.formatDate(startDate));
        }
        if (!Check.isNull(endDate)) {
            param.put("end_date", DateUtils.formatDate(endDate));
        }
        if (!Check.isNull(creativeIds) && creativeIds.size() > 0) {
            param.put("creative_ids", creativeIds);

        }
        param.put("advertiser_id", token.getAccountId());
        param.put("temporal_granularity", "DAILY");

        String result = HttpUtils.httpPostRequest(url, param, headers);
        JSONObject resultJson = JSONObject.parseObject(result);
        Integer code = resultJson.getInteger("code");
        String message = resultJson.getString("message");
        if (null == code || code != 0) {
            throw new Exception("获取快手广告创意分天报表异常:" + message + ",accountId:" + token.getAccountId());
        }
        JSONArray details = resultJson.getJSONObject("data").getJSONArray("details");
        if (null == details || details.size() <= 0) {
            return null;
        }
        return (JSONObject) details.get(0);
    }


}
