package cn.com.ctop.common.module.service.impl;

import cn.com.ctop.common.module.mapper.BytedanceProjectMapper;
import cn.com.ctop.common.module.service.IBytedanceProjectService;
import cn.com.ctop.common.module.utils.Check;
import cn.com.ctop.common.module.utils.CorpWexinUtils;
import cn.com.ctop.common.module.utils.HttpUtils;
import cn.com.ctop.common.module.utils.RateLimitUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.util.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 获取项目列表
 */
@Service
@Slf4j
public class BytedanceProjectServiceImpl implements IBytedanceProjectService {


    private static String bytedanceApiUrl = "https://ad.oceanengine.com/open_api";
    @Resource
    BytedanceProjectMapper projectListMapper;

    @Autowired
    private RateLimitUtil rateLimitUtil;


    @Autowired
    private RedisUtil redisUtil;

    @Resource
    private CorpWexinUtils corpWexinUtils;


    @Override
    public void getPromotionList(Long accountId, String token, List<Long> ids, Map<Long, JSONObject> projectMap, int page) {
        String url = bytedanceApiUrl + "/v3.0/promotion/list/";
        JSONObject param = new JSONObject();
        param.put("advertiser_id", accountId);
        param.put("page", page);
        param.put("page_size", 20);
        JSONObject filtering = new JSONObject();
        filtering.put("ids", ids);
        param.put("filtering", filtering);

        JSONObject resultObject = new JSONObject();
        if (rateLimitUtil.acquire("1635316529903624", url)) {
            resultObject = HttpUtils.bytedanceGetRequest(token, url, param);
        }
        if (Check.isNull(resultObject)) {
            log.error("获取广告列表异常==》accountId:{},调用结果为null", accountId);
            return;
        }
        Integer code = resultObject.getInteger("code");
        if (null == code || !code.equals(0)) {
            log.error("获取广告列表接口异常==》accountId:{},返回信息:{}", accountId, resultObject);
            if (code == 40102 && resultObject.getString("message").equals("access_token已过期")) {
                sendAuthFailure(accountId);
            }
            return;
        }
        JSONArray data = resultObject.getJSONObject("data").getJSONArray("list");
        if (null == data || data.isEmpty()) {
            log.info("获取广告列表数据为空==》accountId:{},message:{},code{}", accountId, resultObject.getString("message"), code);
            return;
        }

        Map<Long, Long> projectIdMap = new HashMap<>();
        List<JSONObject> promotionList = new ArrayList<>();
        for (int i = 0; i < data.size(); i++) {
            JSONObject dataObject = data.getJSONObject(i);
            JSONObject promotionJson = new JSONObject();
            Long projectId = dataObject.getLong("project_id");
            promotionJson.put("projectId", projectId);
            BigDecimal deepCpabid = dataObject.getBigDecimal("deep_cpabid");
            promotionJson.put("deepCpabid", deepCpabid);
            BigDecimal cpaBid = dataObject.getBigDecimal("cpa_bid");
            promotionJson.put("cpaBid", cpaBid);
            BigDecimal bid = dataObject.getBigDecimal("bid");
            promotionJson.put("bid", bid);
            promotionList.add(promotionJson);
            projectIdMap.put(projectId, projectId);
        }
        List<Long> projectIds = new ArrayList<>();
        for (Map.Entry<Long, Long> entry : projectIdMap.entrySet()) {
            projectIds.add(entry.getValue());
        }
        Map<Long, JSONObject> projectInfo = getProjectInfo(accountId, token, projectIds, 1);
        this.check(accountId, token, promotionList, projectInfo);
    }

    @Override
    public void sendAuthFailure(Long accountId) {
        String redisKey = "sendAuthFailure_" + accountId;
        Object o = redisUtil.get(redisKey);
        if (!Check.isNull(o)) {
            log.info("此账户已进行授权失效通知，此次通知跳过。accountId：{}", accountId);
            return;
        }

        JSONObject accountInfo = projectListMapper.getAccountInfo(accountId);
        if (Check.isNull(accountInfo)) {
            log.info("此账户未授权或暂停投放，此次通知跳过。accountId：{}", accountId);
            return;
        }

        String userName = accountInfo.getString("userName");
        String projectName = accountInfo.getString("projectName");
        StringBuilder text = new StringBuilder();
        text.append("您的账户ID：").append(accountId).append("<br/>");
        text.append("所属项目：").append(projectName).append("<br/>");
        text.append("所属运营：").append(userName).append("<br/>");
        text.append("当前授权已失效，无法进行预警和拉取投放数据。").append("<br/>");
        text.append("请及时前往有腾重新进行授权！");
        List<JSONObject> list = projectListMapper.getLeaderByAccountId(accountId);
        for (JSONObject object : list) {
            corpWexinUtils.sendMessageByWeChatId(object, text.toString());
        }
        redisUtil.set(redisKey, accountId, 12 * 60 * 60);
    }


    private void check(Long accountId, String token, List<JSONObject> promotionList, Map<Long, JSONObject> projectInfo) {
        JSONObject json = projectListMapper.getProjectByAccountId(accountId);
        if (Check.isNull(promotionList) || Check.isNull(projectInfo)) {
            return;
        }

        Map<String, String> textMap = new HashMap<>();
        List<JSONObject> lists = projectListMapper.getBytedanceOcpxTypeList();
        for (JSONObject da : lists) {
            textMap.put(da.getString("ocpxActionType"), da.getString("ocpxActionName"));
        }

        //转化目标map
        Map<String, String> ocpxMap = new HashMap<>();
        String ocpxTypeStr = json.getString("ocpxActionType");
        if (!Check.isNull(ocpxTypeStr)) {
            JSONArray ocpxArr = JSONArray.parseArray(ocpxTypeStr);
            StringBuffer nameStr = new StringBuffer();
            for (int i = 0; i < ocpxArr.size(); i++) {
                JSONObject obj = ocpxArr.getJSONObject(i);
                ocpxMap.put(obj.getString("ocpxCode"), obj.getString("maxValue"));
                nameStr.append(textMap.get(obj.getString("ocpxCode"))).append(" ");
            }
            if (ocpxArr.size() > 0) {
                ocpxMap.put("nameStr", nameStr.toString());
            }
        }

        //深度转化目标map
        Map<String, String> deepMap = new HashMap<>();
        String deepTypeStr = json.getString("deepConversionType");
        if (!Check.isNull(deepTypeStr)) {
            JSONArray deepArr = JSONArray.parseArray(deepTypeStr);
            StringBuffer nameStr = new StringBuffer();
            for (int i = 0; i < deepArr.size(); i++) {
                JSONObject obj = deepArr.getJSONObject(i);
                deepMap.put(obj.getString("deepCode"), obj.getString("deepValue"));
                nameStr.append(textMap.get(obj.getString("deepCode"))).append(" ");
            }
            if (deepArr.size() > 0) {
                deepMap.put("nameStr", nameStr.toString());
            }
        }


        for (JSONObject promotionJson : promotionList) {
            //广告项目ID
            Long projectId = promotionJson.getLong("projectId");
            String redisKey = "warning_" + accountId + "_" + projectId;
            Object o = redisUtil.get(redisKey);
            if (!Check.isNull(o)) {
                log.info("该账户：{}，下的项目ID：{}，在10分钟内已经执行过预警，此次预警跳过", accountId, projectId);
                continue;
            }


            BigDecimal cpaBid = promotionJson.getBigDecimal("cpaBid");
            BigDecimal deepCpabid = promotionJson.getBigDecimal("deepCpabid");
            JSONObject projectJson = projectInfo.get(projectId);
            String optStatus = projectJson.getString("optStatus");
            if (!Check.isNull(optStatus)) {
                if ("DISABLE".equals(optStatus)) {
                    log.info("项目状态为暂停中，此次预警跳过，accountId:{}，projectId:{}", accountId, projectId);
                    continue;
                }

            }


            if (Check.isNull(cpaBid)) {
                if (!Check.isNull(projectJson.getBigDecimal("cpaBid"))) {
                    cpaBid = projectJson.getBigDecimal("cpaBid");
                }
            }
            if (Check.isNull(deepCpabid)) {
                if (!Check.isNull(projectJson.getBigDecimal("deepCpabid"))) {
                    deepCpabid = projectJson.getBigDecimal("deepCpabid");
                }
            }
            //广告项目名称
            String projectName = projectJson.getString("projectName");
            //转化目标
            String ocpxActionType = projectJson.getString("externalAction");
            //深度转化目标
            String deepExternalAction = projectJson.getString("deepExternalAction");

            //为NO_BID时，不校验出价
            String bidType = projectJson.getString("bidType");

            StringBuffer msgType = new StringBuffer();
            StringBuffer msgBid = new StringBuffer();
            //转化目标标识
            Boolean typeFalg = true;
            //出价标识
            Boolean bidFalg = true;
            //转化目标
            if (!Check.isNull(ocpxActionType)) {
                String octypeName = Check.isNull(textMap.get(ocpxActionType)) ? ocpxActionType : textMap.get(ocpxActionType);
                if (ocpxMap.isEmpty()) {
                    typeFalg = false;
                    msgType.append("未设转化，目标转化：").append(octypeName);
                } else {
                    //转化出价
                    String ocpxBid = ocpxMap.get(ocpxActionType);
                    //通过转化目标没有查询到转化出价，判断为转化目标不一致，触发关停
                    if (Check.isNull(ocpxBid)) {
                        typeFalg = false;
                        msgType.append("已设转化：").append(ocpxMap.get("nameStr")).append("<br/>").append("目标转化：").append(octypeName);
                    } else {
                        //未查询到转化出价，说明出价不一致，触发出价关停。
                        if (!Check.isNull(cpaBid)) {
//                        if (!"NO_BID".equals(bidType)) {
//                            bidFalg = false;
//                            msgBid.append("[").append(octypeName).append("]目标出价为空");
//                        }
//                    } else {
                            BigDecimal cpa = cpaBid.multiply(new BigDecimal("1000"));
                            BigDecimal ocpx = new BigDecimal(ocpxBid);//本地设置
                            if (cpa.compareTo(ocpx) == 1) {
                                //计划转化出价cpa 大于 项目设置最大转化出价，则关停
                                bidFalg = false;
                                msgBid.append("[").append(octypeName).append("]最高出价：").append(ocpx.divide(new BigDecimal("1000"))).append("<br/>");
                                msgBid.append("目标出价：").append(cpaBid);
                            }
                        }
                    }
                }
            }
            //深度转化目标
            if (typeFalg && bidFalg) {
                String depTypeName = Check.isNull(textMap.get(deepExternalAction)) ? deepExternalAction : textMap.get(deepExternalAction);
                if (!Check.isNull(deepExternalAction) && deepMap.isEmpty()) {
                    typeFalg = false;
                    msgType.append("未设深度转化，目标深度转化：").append(depTypeName);
                } else if (!Check.isNull(deepExternalAction)) {
                    String deepBid = deepMap.get(deepExternalAction);
                    if (Check.isNull(deepBid)) {
                        //通过key未获取值，说明不包含该目标，进行关停
                        typeFalg = false;
                        msgType.append("已设深度：").append(deepMap.get("nameStr")).append("<br/>").append("目标深度：").append(depTypeName);
                    } else {
                        //未查询到转化出价，说明出价不一致，触发出价关停。
                        if (!Check.isNull(deepCpabid)) {
                       /* bidFalg = false;
                        msgBid.append("[").append(depTypeName).append("]目标深度出价为空");
                    } else {*/
                            BigDecimal cpa = deepCpabid.multiply(new BigDecimal("1000"));
                            BigDecimal ocpx = new BigDecimal(deepBid);
                            if (cpa.compareTo(ocpx) == 1) {
                                //计划转化出价cpa 大于 项目设置最大转化出价，则关停
                                bidFalg = false;
                                msgBid.append("[").append(depTypeName).append("]最高深度出价：").append(ocpx.divide(new BigDecimal("1000"))).append("<br/>");
                                msgBid.append("目标深度出价：").append(deepCpabid);
                            }
                        }
                    }
                }
            }

            String status = null;
            if (!typeFalg || !bidFalg) {
                status = this.updProjectStatus(token, accountId, projectId, "DISABLE");
            }

            if (!typeFalg) { // 出价方式设置非法
                String text = getProjectBidTypeText(json.getString("projectName"), accountId, projectId, projectName, null, null, msgType.toString(), status);
                sendMsg(accountId, text);
            }

            if (!bidFalg) {
                String text = getProjectOverBidMessage(json.getString("projectName"), accountId, projectId, projectName, null, null, msgBid.toString(), status);
                sendMsg(accountId, text);
            }
        }

    }


    private Map<Long, JSONObject> getProjectInfo(Long accountId, String accessToken, List<Long> ids, int page) {
        Map<Long, JSONObject> returnMap = new HashMap<>();
        try {
            // 请求地址
            String url = bytedanceApiUrl + "/v3.0/project/list/";
            JSONObject param = new JSONObject();
            param.put("advertiser_id", accountId);
            param.put("page", page);
            param.put("page_size", 100);
            JSONObject filtering = new JSONObject();
            if (!Check.isNull(ids)) {
                filtering.put("ids", new ArrayList<>(ids));
            }
            param.put("filtering", filtering);

            JSONObject resultObject = new JSONObject();
            if (rateLimitUtil.acquire("1635316529903624", url)) {
                resultObject = HttpUtils.bytedanceGetRequest(accessToken, url, param);
            }

            if (Check.isNull(resultObject)) {
                log.error("checkAdvertiserProject异常==》accountId:{},结果为null", accountId);
                return returnMap;
            }
            Integer code = resultObject.getInteger("code");
            if (null == code || !code.equals(0)) {
                log.error("checkAdvertiserProject接口==》accountId:{},message:{},code{}", accountId, resultObject.getString("message"), code);
                return returnMap;
            }
            JSONArray data = resultObject.getJSONObject("data").getJSONArray("list");
            if (null == data || data.isEmpty()) {
                log.info("checkAdvertiserProject数据为空==》accountId:{},message:{},code:{}", accountId, resultObject.getString("message"), code);
                return returnMap;
            }
            for (int i = 0; i < data.size(); i++) {
                JSONObject dataObject = data.getJSONObject(i);
                JSONObject mapJson = new JSONObject();
                //广告项目ID
                Long projectId = dataObject.getLong("project_id");
                mapJson.put("projectId", projectId);
                //广告项目名称
                String projectName = dataObject.getString("name");
                mapJson.put("projectName", projectName);
                //转化目标
                String externalAction = dataObject.getJSONObject("optimize_goal").getString("external_action");
                mapJson.put("externalAction", externalAction);
                //深度转化目标
                String deepExternalAction = dataObject.getJSONObject("optimize_goal").getString("deep_external_action");
                mapJson.put("deepExternalAction", deepExternalAction);
                //为NO_BID时，不校验出价
                String bidType = dataObject.getJSONObject("delivery_setting").getString("bid_type");
                String deepCpabid = dataObject.getJSONObject("delivery_setting").getString("deep_cpabid");
                mapJson.put("deepCpabid", deepCpabid);
                String cpaBid = dataObject.getJSONObject("delivery_setting").getString("cpa_bid");
                mapJson.put("cpaBid", cpaBid);
                mapJson.put("bidType", bidType);
                String optStatus = dataObject.getString("opt_status");
                mapJson.put("optStatus", optStatus);
                returnMap.put(projectId, mapJson);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        return returnMap;
    }


    @Override
    public synchronized String updProjectStatus(String token, Long accountId, Long projectId, String disable) {
        JSONObject params = new JSONObject();
        params.put("advertiser_id", accountId);
        JSONObject data = new JSONObject();
        data.put("project_id", projectId);
        data.put("opt_status", disable);//ENABLE 启用项目、 DISABLE 暂停项目
        List<JSONObject> list = new ArrayList<>();
        list.add(data);
        params.put("data", list);
        String url = bytedanceApiUrl + "/v3.0/project/status/update/";

        JSONObject object = new JSONObject();
        if (rateLimitUtil.acquire("1635316529903624", url)) {
            object = HttpUtils.bytedancePostRequest(token, url, params);
        }

        if (!Check.isNull(object)) {
            Integer code = object.getInteger("code");
            if (code == 0) {
                String redisKey = "warning_" + accountId + "_" + projectId;
                redisUtil.set(redisKey, projectId, 10 * 60);
                projectListMapper.addLog(accountId, projectId, "stop");
                return "关停成功";
            } else {
                log.error("预警关停计划失败，accountId：{}，projectId：{}, result：{}", accountId, projectId, object);
            }
        }

        return "关停失败";
    }


    @Override
    public JSONObject getTokenByAccountId(Long advertiserId) {
        return projectListMapper.getTokenByAccountId(advertiserId);
    }


    //判断逻辑


    private void sendMsg(Long accountId, String text) {
        try {

            //关停计划
            List<JSONObject> list = projectListMapper.getWChatIdsByAccountId(accountId);
            for (JSONObject object : list) {
                corpWexinUtils.sendMessageByWeChatId(object, text);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    //转化方式错误消息生成
    private String getProjectBidTypeText(String projectName, Long advertiserId, Long unitId, String unit_name, Long adId, String adName, String msgType, String status) {
        StringBuilder text = new StringBuilder();
        text.append("出价方式错误预警").append("<br/>")
                .append("您本地的项目：").append(projectName).append("<br/>")
                .append("下的账户：").append(advertiserId).append("<br/>")
                .append("项目id：").append(unitId).append("<br/>")
                .append("项目名称：").append(unit_name).append("<br/>");
        if (!Check.isNull(adId)) {
            text.append("广告id：").append(adId).append("<br/>").append("广告名称：").append(adName).append("<br/>");
        }
        text.append("错误说明：").append("<br/>").append(msgType).append("<br/>").append("请您及时查看并处理！");
        text.append("关停项目说明：").append(status).append("<br/>");
        return text.toString();
    }

    //出价过高预警消息生成
    private String getProjectOverBidMessage(String projectName, Long accountId, Long planId, String planName, Long adId, String adName, String msgBid, String status) {
        StringBuilder text = new StringBuilder();
        text.append("出价过高预警").append("<br/>")
                .append("您本地的项目：").append(projectName).append("<br/>")
                .append("下的账户：").append(accountId).append("<br/>")
                .append("项目id：").append(planId).append("<br/>")
                .append("项目名称：").append(planName).append("<br/>");
        if (!Check.isNull(adId)) {
            text.append("广告id：").append(adId).append("<br/>").append("广告名称：").append(adName).append("<br/>");
        }
        if (!msgBid.contains("出价为空")) {
            text.append("出价过高说明：").append("<br/>");
        }

        text.append(msgBid).append("<br/>").append("请您及时查看并处理！");
        text.append("关停项目说明：").append(status).append("<br/>");
        return text.toString();
    }


}
