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

import cn.com.ctop.common.module.entity.CtopOauthToken;
import cn.com.ctop.common.module.service.ICtopOauthTokenService;
import cn.com.ctop.common.module.service.IMaterialCutFrameService;
import cn.com.ctop.common.module.utils.Check;
import cn.com.ctop.kuaishou.modules.ai.entity.AiKuaishouAccountAutoTarget;
import cn.com.ctop.kuaishou.modules.ai.entity.AiKuaishouAccountAutoVideo;
import cn.com.ctop.kuaishou.modules.ai.enums.KuaishouAutoTypeEnum;
import cn.com.ctop.kuaishou.modules.ai.enums.KuaishouCreativeAdsenceTypeEnum;
import cn.com.ctop.kuaishou.modules.ai.service.IAiKuaiShouAppInfoService;
import cn.com.ctop.kuaishou.modules.ai.service.IAiKuaishouAccountAutoTargetService;
import cn.com.ctop.kuaishou.modules.ai.service.IAiKuaishouAccountAutoVideoService;
import cn.com.ctop.kuaishou.modules.ai.service.IAiKuaishouCampaignLevelOperationRecordService;
import cn.com.ctop.kuaishou.modules.ai.service.IAiKuaishouCreativeLevelOperationRecordService;
import cn.com.ctop.kuaishou.modules.ai.service.IAiKuaishouProgramCreativeLevelOperationRecordService;
import cn.com.ctop.kuaishou.modules.ai.service.IAiKuaishouUnitLevelOperationRecordService;
import cn.com.ctop.kuaishou.modules.ai.service.IKuaiShouAppMultipleBidsService;
import cn.com.ctop.kuaishou.modules.ai.service.IKuaishouAccountCreativeOverrunInfoService;
import cn.com.ctop.kuaishou.modules.ai.service.IKuaishouLandpagePackageService;
import cn.com.ctop.kuaishou.modules.batch.entity.KuaiShouAppList;
import cn.com.ctop.kuaishou.modules.batch.service.IKuaiShouAppListService;
import cn.com.ctop.kuaishou.modules.batch.service.IKuaiShouCampaignService;
import cn.com.ctop.kuaishou.modules.batch.service.IKuaiShouGroupService;
import cn.com.ctop.kuaishou.modules.batch.service.IKuaiShouGroupTemplateService;
import cn.com.ctop.kuaishou.modules.batch.service.IKuaiShouImageGetService;
import cn.com.ctop.kuaishou.modules.batch.service.IKuaiShouMaterialUploadService;
import cn.com.ctop.kuaishou.modules.batch.service.IKuaiShouUpdateService;
import cn.com.ctop.kuaishou.modules.batch.service.IKuaiShouVideoGetService;
import cn.com.ctop.kuaishou.modules.batch.service.IKuaishouInterfaceService;
import cn.com.ctop.kuaishou.modules.channel.service.IKuaishouChannelService;
import cn.com.ctop.kuaishou.modules.v3.entity.V3KuaishouAccountAutoStrategy;
import cn.com.ctop.kuaishou.modules.v3.entity.V3KuaishouAccountAutoTarget;
import cn.com.ctop.kuaishou.modules.v3.mapper.V3KuaishouAccountAutoStrategyMapper;
import cn.com.ctop.kuaishou.modules.v3.service.V3KuaishouAccountAutoDoService;
import cn.com.ctop.kuaishou.modules.v3.service.V3KuaishouAccountAutoTargetService;
import cn.com.ctop.kuaishou.modules.v3.service.V3KuaishouAccountDetailsService;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.util.DateUtils;
import org.jeecg.common.util.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.UUID;

@Slf4j
@Service
public class V3KuaishouAccountAutoDoServiceImpl implements V3KuaishouAccountAutoDoService {

    @Autowired
    private V3KuaishouAccountAutoStrategyMapper mapper;
    @Autowired
    private IKuaiShouGroupService groupService;
    @Autowired
    private V3KuaishouAccountDetailsService v3detailsService;
    @Autowired
    private IAiKuaishouAccountAutoTargetService autoTargetService;
    @Autowired
    private V3KuaishouAccountAutoTargetService v3autoTargetService;
    @Autowired
    private IKuaiShouGroupTemplateService groupTemplateService;
    @Autowired
    private ICtopOauthTokenService tokenService;
    @Autowired
    private IKuaishouInterfaceService kuaishouInterfaceService;
    @Autowired
    private IKuaiShouVideoGetService videoGetService;
    @Autowired
    private IAiKuaishouAccountAutoVideoService autoVideoService;
    @Autowired
    private IMaterialCutFrameService materialCutFrameService;
    @Autowired
    private IKuaiShouImageGetService imageGetService;
    @Autowired
    private IKuaiShouMaterialUploadService materialUploadService;
    @Autowired
    private IKuaishouAccountCreativeOverrunInfoService overrunInfoService;
    @Autowired
    private IAiKuaishouCampaignLevelOperationRecordService aiKuaishouCampaignLevelOperationRecordService;

    @Autowired
    private IAiKuaishouProgramCreativeLevelOperationRecordService kuaishouProgramCreativeLevelOperationRecordService;

    @Autowired
    private IAiKuaiShouAppInfoService appInfoService;
    @Autowired
    private IAiKuaishouUnitLevelOperationRecordService unitLevelOperationRecordService;
    @Autowired
    private IAiKuaishouCreativeLevelOperationRecordService creativeLevelOperationRecordService;
    @Autowired
    private IKuaishouLandpagePackageService landpagePackageService;
    @Autowired
    private IKuaiShouAppMultipleBidsService kuaiShouAppMultipleBidsService;
    @Autowired
    private IKuaishouChannelService channelService;
    @Autowired
    private IKuaiShouUpdateService updateService;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private IKuaiShouAppListService appListService;

    @Autowired
    private IKuaiShouCampaignService campaignService;

    /**
     * 创建自定义上新、高质量、打捞、遗漏广告创意
     * <p>
     * 创建频率：次数/小时；时间范围 9~21共计13次，触发时间每小时55分钟执行
     */
    @Override
    public void createCustomCreative(V3KuaishouAccountAutoStrategy strategy) {
        //当天小时数
        Integer hour = 0;
        try {
            hour = DateUtils.getHour(DateUtils.formatDateTime());
        } catch (ParseException e) {
            e.printStackTrace();
        }
        if (hour <= 9 || hour >= 22) {
            return;
        }
        Long accountId = strategy.getAccountId();
        //==========账户{}执行自定义（高质量、打捞、遗漏）自动创建==========start==========
        String date = DateUtils.formatDate(new Date());
        //当前需要创建广告组数量
        if (strategy.getCustomPlanCnt() == null || strategy.getCustomUnitCnt() == null) {
            return;
        }
        int unitNum = strategy.getCustomPlanCnt() * strategy.getCustomUnitCnt();
        //获取自定义已创建广告组数量
        String customkey = date + "_v3_custom_cnt_" + accountId;
        Integer customUnitCnt = (Integer) redisUtil.get(customkey);
        if (Check.isNull(customUnitCnt)) {
            if (unitNum > 600) {
                unitNum = 600;
            }
        } else {
            unitNum = unitNum - customUnitCnt;
        }
        if (unitNum <= 0) {
            log.info("今日创建已达标");
            return;
        }
        log.info("----- （自定义）账户：{}，已建组数：{}；剩余待建组数：{}", strategy.getAccountId(), customUnitCnt, unitNum);
        //剩余创建次数
        Integer hours = 22 - hour;

        //上新
        Integer upNewCnt = 0;
        //高质量待建数
        int highCnt = 0;
        //遗漏
        int omitCnt = 0;
        //打捞
        int salvageCnt = 0;
        //1-上新素材，2-高质量素材，3-遗漏素材，4-历史打捞
        String material = strategy.getSourceMaterial();
        if (hours > 0) {
            JSONObject cnt = getCnt(material, unitNum, hours);
            upNewCnt = cnt.getInteger("upNewCnt");
            highCnt = cnt.getInteger("highCnt");
            omitCnt = cnt.getInteger("omitCnt");
            salvageCnt = cnt.getInteger("salvageCnt");
        }
        if (upNewCnt > 0) {
            // 1上新
            log.info("----- （自定义上新）账户：{}，本次可建组数：{}", strategy.getAccountId(), upNewCnt);
            this.autoCreateCreative(strategy, 1, upNewCnt);
        }
        if (omitCnt > 0 && material.contains("3")) {
            //3历史遗漏
            log.info("----- （自定义历史遗漏）账户：{}，本次可建组数：{}", strategy.getAccountId(), omitCnt);
            unitNum = this.autoCreateCreative(strategy, 3, omitCnt);
        }
        if (highCnt > 0 && material.contains("2")) {
            //2高质量素材
            log.info("----- （自定义高质量）账户：{}，本次可建组数：{}", strategy.getAccountId(), highCnt);
            unitNum = this.autoCreateCreative(strategy, 2, highCnt);
        }
        if (salvageCnt > 0 && material.contains("4")) {
            //4历史打捞素材
            log.info("----- （自定义历史打捞）账户：{}，本次可建组数：{}", strategy.getAccountId(), salvageCnt);
            this.autoCreateCreative(strategy, 4, salvageCnt);
        }
    }


    /**
     * 执行创建自定义创意
     *
     * @param strategy  账户配置信息
     * @param videoType 创意创建类型 1:素材上新 2：历史高质量 3:历史遗漏素材 4:历史打捞
     * @param videoCnt  可创建组数
     */
    private Integer autoCreateCreative(V3KuaishouAccountAutoStrategy strategy, Integer videoType, Integer unitCnt) {
        //4-自定义
        strategy.setUnitType(4);
        strategy.setToBeBuiltCnt(unitCnt);
        Long accountId = strategy.getAccountId();
        CtopOauthToken token = tokenService.getTokenByAccountId(accountId);
        if (null == token) {
            log.error("****** accountId:{}获取token失败", accountId);
            return unitCnt;
        }
        //创意创建类型
        String typeName = KuaishouAutoTypeEnum.getNameByType(strategy.getUnitType().toString() + videoType.toString());
        String statDate = DateUtils.formatDate(new Date());

        String strategyUuid = UUID.randomUUID().toString();
        log.info("--------------------账户（{}）执行自定义自动投放搭建:{}-----------------", accountId, typeName);
        /*获取应用*/
        Long appId = strategy.getAppId();
        Integer campaignType = strategy.getCampaignType();
        KuaiShouAppList appInfo = appListService.getAppInfo(accountId, appId);
        if (Check.isNull(appInfo) && (campaignType == 2 || campaignType == 7)) {
            log.error("****** 获取应用失败，AppId={}", appId);
            return unitCnt;
        }

        try {

            /*获取素材*/
            List<AiKuaishouAccountAutoVideo> newVideos = getVideosByParams(strategy, statDate, videoType, "custom", unitCnt);
            if (null == newVideos || newVideos.isEmpty()) {
                log.error("****** {}账户（{}）执行自定义创建，未获取到素材({})", statDate, accountId, typeName);
            }

            //待建素材集 redis key值
            String videoKey = accountId + statDate + "_v3_list_" + videoType;
            /*根据匹配逻辑，定向包和素材进行匹配，获取匹配后的素材*/
            List<List<AiKuaishouAccountAutoVideo>> targetsAndVideos = getTargetsAndVideos(strategy, newVideos, videoKey, unitCnt);
            if (Check.isNull(targetsAndVideos) || targetsAndVideos.isEmpty()) {
                return unitCnt;
            }
            //执行创建
            checkPlanIsCreated(strategy, token, videoType, targetsAndVideos, videoKey);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("(单应用)创建自定义广告组异常", e);
        }

        return unitCnt;
    }

    /**
     * 更新广告组数
     */
    private void updateRedisCnt(String key) {
        Integer unitCnt = (Integer) redisUtil.get(key);
        if (Check.isNull(unitCnt)) {
            redisUtil.set(key, 1, 100000L);
        } else {
            redisUtil.set(key, unitCnt + 1, 100000L);
        }
    }


    /**
     * 创建程序化广告创意
     *
     * @param strategy 账户配置信息
     */
    @Override
    public void createProgramCreative(V3KuaishouAccountAutoStrategy strategy) {
        //==========账户{}执行自定义（高质量、打捞、遗漏）自动创建==========start==========
        //当天小时数
        Integer hour = 0;
        try {
            hour = DateUtils.getHour(DateUtils.formatDateTime());
        } catch (ParseException e) {
            e.printStackTrace();
        }
        Long accountId = strategy.getAccountId();
        String date = DateUtils.formatDate(new Date());
        //当前需要创建广告组数量
        if (strategy.getProgramPlanCnt() == null || strategy.getProgramUnitCnt() == null) {
            return;
        }
        int unitNum = strategy.getProgramPlanCnt() * strategy.getProgramUnitCnt();
        //获取自定义已创建广告组数量
        String programkey = date + "_v3_program_cnt_" + accountId;
        Integer programUnitCnt = (Integer) redisUtil.get(programkey);
        if (Check.isNull(programUnitCnt)) {
            if (unitNum > 300) {
                unitNum = 300;
            }
        } else {
            unitNum = unitNum - programUnitCnt;
        }
        if (unitNum == 0) {
            log.info("今日创建已达标");
            return;
        }
        log.info("----- （程序化）账户：{}，已建组数：{}；剩余待建组数：{}", strategy.getAccountId(), programUnitCnt, unitNum);
        //剩余创建次数
        Integer hours = 22 - hour;
        int highCnt = 0;
        int upNewCnt = unitNum;
        String matchType = strategy.getSourceMaterial();
        //包含高质量，则按照 上新：高质量 = 75%:25% 分配
        if (matchType.contains("2")) {
            highCnt = (int) (unitNum * 0.25);
            upNewCnt = (int) (unitNum * 0.75);
        }
        if (hours > upNewCnt) {
            upNewCnt = 1;
        } else {
            highCnt = highCnt / hours;
            upNewCnt = upNewCnt / hours;
        }
        if (upNewCnt > 0) {
            //1上新
            log.info("----- （程序化上新）账户：{}，本次可建组数：{}", strategy.getAccountId(), upNewCnt);
            unitNum = this.autoCreateProgramCreative(strategy, 1, upNewCnt);
        }
        if (highCnt > 0 && matchType.contains("2")) {
            log.info("----- （程序化高质量）账户：{}，本次可建组数：{}", strategy.getAccountId(), highCnt);
            //2高质量素材
            unitNum = this.autoCreateProgramCreative(strategy, 2, highCnt);
        }
    }

    /**
     * 执行创建程序化创意
     *
     * @param strategy  策略对象
     * @param videoType 创意创建类型 1:素材上新 2：历史高质量 3:历史遗漏素材 4:历史打捞
     * @param videoCnt  视频数量
     */
    private Integer autoCreateProgramCreative(V3KuaishouAccountAutoStrategy strategy, Integer videoType, Integer unitCnt) {
        try {
            String uuid = UUID.randomUUID().toString();
            //4-自定义
            strategy.setUnitType(7);
            strategy.setToBeBuiltCnt(unitCnt);
            Long accountId = strategy.getAccountId();
            CtopOauthToken token = tokenService.getTokenByAccountId(accountId);
            if (null == token) {
                log.error("****** accountId:{}获取token失败", accountId);
                return unitCnt;
            }

            //创意创建类型
            String typeName = KuaishouAutoTypeEnum.getNameByType(strategy.getUnitType().toString() + videoType.toString());
            String statDate = DateUtils.formatDate(new Date());

            /*获取应用*/
            Long appId = strategy.getAppId();
            KuaiShouAppList appInfo = appListService.getAppInfo(accountId, appId);
            if (Check.isNull(appInfo)) {
                log.error("****** 获取应用失败，AppId={}", appId);
                return unitCnt;
            }

            /*获取素材*/
            List<List<AiKuaishouAccountAutoVideo>> proVideos = new ArrayList<>();
            List<AiKuaishouAccountAutoVideo> allVideos = getVideosByParams(strategy, statDate, videoType, "program", unitCnt * 10);
            if (null == allVideos || allVideos.isEmpty()) {
                log.error("****** {}账户（{}）执行程序化创建，未获取到素材({})", statDate, accountId, typeName);
            } else {
                if (allVideos.size() < 3) {
                    log.error("****** {}账户（{}）执行程序化创建，获取素材小于3({})", statDate, accountId, typeName);
                } else {
                    List<List<AiKuaishouAccountAutoVideo>> newVideos = Lists.newArrayList(Lists.partition(allVideos, 10));
                    int size = newVideos.size() - 1;
                    if (size > 0) {
                        //判断最后一个集合小于3
                        List<AiKuaishouAccountAutoVideo> lastVideos = newVideos.get(size);
                        if (lastVideos.size() < 3) {
                            //小于3 则去除
                            for (int i = 0; i < size; i++) {
                                proVideos.add(newVideos.get(i));
                            }
                        } else {
                            proVideos = newVideos;
                        }
                    } else {
                        proVideos = newVideos;
                    }
                }
            }
            //待建素材集 redis 程序化单应用 key值
            String videoKey = accountId + statDate + "_v3_op_plist_" + videoType;
            /*根据匹配逻辑，定向包和素材进行匹配，获取匹配后的素材*/
            List<List<AiKuaishouAccountAutoVideo>> targetsAndVideos = getProTargetsAndVideos(strategy, proVideos, videoKey, unitCnt, null, null, null);
            if (Check.isNull(targetsAndVideos) || targetsAndVideos.isEmpty()) {
                return unitCnt;
            }
            //执行创建
            checkPlanIsCreated(strategy, token, videoType, targetsAndVideos, videoKey);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("(单应用)创建自定义广告组异常", e);
        }
        return unitCnt;
    }


    /**
     * 添加素材使用记录
     */
    private void updateVideoUsed(Integer videoType, List<List<AiKuaishouAccountAutoVideo>> videos, Integer unitType) {
        try {
            for (List<AiKuaishouAccountAutoVideo> videoList : videos) {
                for (AiKuaishouAccountAutoVideo video : videoList) {
                    String type = "";
                    if (videoType == 1 || videoType == 3) {
                        AiKuaishouAccountAutoVideo v = new AiKuaishouAccountAutoVideo();
                        v.setId(video.getId());
                        if (unitType == 4) {
                            type = "custom";
                            v.setCustomStatus(0);//无效
                        } else {
                            type = "program";
                            v.setProgramStatus(0);//无效
                        }
                        autoVideoService.updateById(v);
                    } else {
                        autoVideoService.saveOrUpdateAutoVideoUsedCount(video.getAccountId(), video.getSignature(), videoType, type);
                    }
                    if (unitType == 4) {
                        //更新自定义创建组数量
                        updateRedisCnt(DateUtils.formatDate(new Date()) + "_v3_custom_cnt_" + video.getAccountId());
                    } else {
                        updateRedisCnt(DateUtils.formatDate(new Date()) + "_v3_program_cnt_" + video.getAccountId());
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("添加素材使用记录异常", e);
        }
    }

    /**
     * 删除redis中创建成功的匹配素材（程序化）
     */
    private void removeProRedisVideos(List<List<AiKuaishouAccountAutoVideo>> successVideoList, String key) {
        //redis key值
        try {
            if (!Check.isNull(successVideoList) && !successVideoList.isEmpty()) {
                List<List<AiKuaishouAccountAutoVideo>> redisVideos = (List) redisUtil.get(key);
                if (!Check.isNull(redisVideos) && !redisVideos.isEmpty()) {
                    redisVideos.removeAll(successVideoList);
                    redisUtil.set(key, redisVideos, 100000L);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("****** 删除redis中创建成功的匹配素材失败（程序化）");
        }
    }


    /**
     * 根据匹配逻辑，定向包和素材进行匹配，获取匹配后的素材(自定义)
     */
    private List<List<AiKuaishouAccountAutoVideo>> getTargetsAndVideos(V3KuaishouAccountAutoStrategy strategy, List<AiKuaishouAccountAutoVideo> newVideos, String key, Integer unitCnt) {
        List<List<AiKuaishouAccountAutoVideo>> redisVideos = (List) redisUtil.get(key);
        if (Check.isNull(redisVideos) || redisVideos.isEmpty()) {
            if (Check.isNull(newVideos) || newVideos.isEmpty()) {
                log.error("****** 缺少素材，不执行创建操作");
                return null;
            }
            redisVideos = new ArrayList<>();
        } else {
            if (redisVideos.size() >= unitCnt) {
                return new ArrayList<>(redisVideos.subList(0, unitCnt));
            }
        }
        /*
        定向匹配：用定向包匹配创意； 根据数量来进行匹配，谁多创建多少个
        素材匹配：用素材匹配定向包； 定向包与创意数量进行叉乘
        * */
        Integer matchType = strategy.getMatchType();//1定向匹配。2素材匹配
        Integer creativeCnt = strategy.getCustomCreativeCnt();
        List<Long> targets = JSONArray.parseArray(strategy.getAutoTargetArray(), Long.class);
        if (Check.isNull(targets) || targets.isEmpty()) {
            log.error("****** 获取策略中本地定向数据为空");
            return null;
        }
        //多个或者一个
        creativeCnt = Check.isNull(creativeCnt) ? 1 : creativeCnt;
        List<List<AiKuaishouAccountAutoVideo>> creativeGroup = new ArrayList<>();
        List<List<AiKuaishouAccountAutoVideo>> creativeGroupList = Lists.newArrayList(Lists.partition(newVideos, creativeCnt));
        List<List<AiKuaishouAccountAutoVideo>> arrayList = new ArrayList<>();
        for (List<AiKuaishouAccountAutoVideo> splitVideo : creativeGroupList) {
            arrayList.add(Lists.newArrayList(splitVideo));
        }
        int length = arrayList.size() - 1;
        if (length > 0) {
            //判断最后一个集合小于 组创建数（creativeCnt）
            List<AiKuaishouAccountAutoVideo> endVideos = arrayList.get(length);
            if (endVideos.size() < creativeCnt) {
                //若小于 则去除
                for (int i = 0; i < length; i++) {
                    creativeGroup.add(arrayList.get(i));
                }
            } else {
                creativeGroup = arrayList;
            }
        } else {
            creativeGroup = arrayList;
        }

        if (matchType == 1) {
            directionalMatch(targets, creativeGroup, redisVideos);
        } else {
            videoMatch(targets, creativeGroup, redisVideos);
        }
        List<List<AiKuaishouAccountAutoVideo>> autoVideos = new ArrayList<>();
        if (!Check.isNull(redisVideos) && !redisVideos.isEmpty()) {
            redisUtil.set(key, redisVideos, 100000L);
            if (redisVideos.size() > unitCnt) {
                autoVideos = new ArrayList<>(redisVideos.subList(0, unitCnt));
            } else {
                autoVideos = redisVideos;
            }
        }
        return autoVideos;
    }

    /**
     * 定向匹配（自定义）
     * 定向匹配：用定向包匹配创意； 根据数量来进行匹配，谁多创建多少个
     * <p>
     * 程序化创意---定向包 X 创意数量（17个素材，在程序化会分为2个创意）---5个--广告组数量；定1-创意1，定2-创意2，定3-创1，定4-创2，定5-创1
     * 自定义创意（设定创意数量）---定向包 X 创意数量（17个素材，设定为5个素材一组创意，则会分为3个创意组，剩余2个素材）---5个--广告组数量；
     * 自定义创意---定向包 X 创意数量（17个素材）---17个--广告组数量；定1-创意1、6、11、16，定2-创意2、7、12、17，定3-创3、8、13，定4-创4、9、14，定5-创5、10、15
     */
    private void directionalMatch(List<Long> targets, List<List<AiKuaishouAccountAutoVideo>> creativeGroup, List<List<AiKuaishouAccountAutoVideo>> redisVideos) {
        //定向包数量：
        int targetCnt = targets.size();
        List<List<AiKuaishouAccountAutoVideo>> havaList = new ArrayList<>();

        int videoCnt = creativeGroup.size();
        if (targetCnt > videoCnt) {
            List<List<Long>> splits = Lists.newArrayList(Lists.partition(targets, videoCnt));
            for (List<Long> target : splits) {
                for (int i = 0; i < target.size(); i++) {
                    List<AiKuaishouAccountAutoVideo> videoGroup = Lists.newArrayList(creativeGroup.get(i));
                    if (!Check.isNull(videoGroup)) {
                        for (AiKuaishouAccountAutoVideo video : videoGroup) {
                            video.setAutoTargetId(target.get(i));
                        }
                        for (List<AiKuaishouAccountAutoVideo> redisVideo : redisVideos) {
                            if (redisVideo.size() == videoGroup.size() && redisVideo.containsAll(videoGroup)) {
                                havaList.add(videoGroup);
                            }
                        }
                    }
                }
            }
        } else if (targetCnt < videoCnt) {
            List<List<List<AiKuaishouAccountAutoVideo>>> splitVideos = Lists.newArrayList(Lists.partition(creativeGroup, targetCnt));
            for (List<List<AiKuaishouAccountAutoVideo>> videoList : splitVideos) {
                for (int i = 0; i < videoList.size(); i++) {
                    Long targetId = targets.get(i);
                    if (!Check.isNull(targetId)) {
                        List<AiKuaishouAccountAutoVideo> videoGroup = Lists.newArrayList(videoList.get(i));
                        for (AiKuaishouAccountAutoVideo video : videoGroup) {
                            video.setAutoTargetId(targetId);
                        }
                        for (List<AiKuaishouAccountAutoVideo> redisVideo : redisVideos) {
                            if (redisVideo.size() == videoGroup.size() && redisVideo.containsAll(videoGroup)) {
                                havaList.add(videoGroup);
                            }
                        }
                    }
                }
            }
        } else {
            for (int i = 0; i < targets.size(); i++) {
                List<AiKuaishouAccountAutoVideo> videoGroup = Lists.newArrayList(creativeGroup.get(i));
                if (!Check.isNull(videoGroup)) {
                    for (AiKuaishouAccountAutoVideo video : videoGroup) {
                        video.setAutoTargetId(targets.get(i));
                    }
                    for (List<AiKuaishouAccountAutoVideo> redisVideo : redisVideos) {
                        if (redisVideo.size() == videoGroup.size() && redisVideo.containsAll(videoGroup)) {
                            havaList.add(videoGroup);
                        }
                    }
                }
            }
        }
        if (havaList.size() > 0) {
            creativeGroup.removeAll(havaList);
        }
        redisVideos.addAll(creativeGroup);
    }

    /**
     * 素材匹配(自定义）
     * 素材匹配：用素材匹配定向包； 定向包与创意数量进行叉乘
     * <p>
     * 程序化创意---定向包 X 创意数量（17个素材，在程序化会分为2个创意）---5 x 2--广告组数量；
     * 自定义创意（设定创意数量）---定向包 X 创意数量（17个素材，设定为5个素材一组创意，则会分为3个创意组，剩余2个素材）---5 x 3--广告组数量；
     * 自定义创意---定向包 X 创意数量（17个素材）---5 x 17--广告组数量；
     */
    private void videoMatch(List<Long> targets, List<List<AiKuaishouAccountAutoVideo>> creativeGroup, List<List<AiKuaishouAccountAutoVideo>> redisVideos) {
        List<List<AiKuaishouAccountAutoVideo>> havaList = new ArrayList<>();
        for (Long targetId : targets) {
            for (List<AiKuaishouAccountAutoVideo> videoGroup : creativeGroup) {
                videoGroup = Lists.newArrayList(videoGroup);
                for (AiKuaishouAccountAutoVideo video : videoGroup) {
                    video.setAutoTargetId(targetId);
                }
                for (List<AiKuaishouAccountAutoVideo> redisVideo : redisVideos) {
                    if (redisVideo.size() == videoGroup.size() && redisVideo.containsAll(videoGroup)) {
                        havaList.add(videoGroup);
                    }
                }
            }
        }
        if (havaList.size() > 0) {
            creativeGroup.removeAll(havaList);
        }
        redisVideos.addAll(creativeGroup);
    }

    /**
     * 根据匹配逻辑，定向包和素材进行匹配，获取匹配后的素材(程序化）
     */
    private List<List<AiKuaishouAccountAutoVideo>> getProTargetsAndVideos(V3KuaishouAccountAutoStrategy strategy, List<List<AiKuaishouAccountAutoVideo>> proVideos, String key, Integer videoCnt, Long appId, Long campaignId, String appVersion) {
        List<List<AiKuaishouAccountAutoVideo>> redisVideos = (List) redisUtil.get(key);
        if (Check.isNull(redisVideos) || redisVideos.isEmpty()) {
            if (Check.isNull(proVideos) || proVideos.isEmpty()) {
                log.error("****** 缺少素材，不执行创建操作");
                return null;
            }
            redisVideos = new ArrayList<>();
        } else {
            if (redisVideos.size() >= 300) {
                if (videoCnt == 0) {
                    return null;
                }
                return new ArrayList<>(redisVideos.subList(0, videoCnt));
            }
        }
        Integer matchType = strategy.getMatchType();//1定向匹配。2素材匹配
        List<Long> targets = JSONArray.parseArray(strategy.getAutoTargetArray(), Long.class);
        if (Check.isNull(targets) || targets.isEmpty()) {
            log.error("****** 获取策略中本地定向数据失败");
            return null;
        }
        if (matchType == 1) {
            directionalMatchPro(targets, proVideos, redisVideos, appId, campaignId, appVersion);
        } else {
            videoMatchPro(targets, proVideos, redisVideos, appId, campaignId, appVersion);
        }

        List<List<AiKuaishouAccountAutoVideo>> autoVideos = new ArrayList<>();
        if (!Check.isNull(redisVideos) && !redisVideos.isEmpty()) {
            redisUtil.set(key, redisVideos, 100000L);
            if (redisVideos.size() > videoCnt) {
                autoVideos = new ArrayList<>(redisVideos.subList(0, videoCnt));
            } else {
                autoVideos = redisVideos;
            }
        }
        return autoVideos;

    }

    /**
     * 素材匹配(程序化）
     */
    private void videoMatchPro(List<Long> targets, List<List<AiKuaishouAccountAutoVideo>> proVideos, List<List<AiKuaishouAccountAutoVideo>> redisVideos, Long appId, Long campaignId, String appVersion) {
        List<List<AiKuaishouAccountAutoVideo>> havaList = new ArrayList<>();
        for (Long targetId : targets) {
            for (List<AiKuaishouAccountAutoVideo> newVideo : proVideos) {
                for (AiKuaishouAccountAutoVideo video : newVideo) {
                    if (!Check.isNull(appId)) {
                        video.setAppId(appId);
                        video.setCampaignId(campaignId);
                        video.setAppVersion(appVersion);
                    }
                    video.setAutoTargetId(targetId);
                }
                for (List<AiKuaishouAccountAutoVideo> redisVideo : redisVideos) {
                    if (redisVideo.size() == newVideo.size() && redisVideo.containsAll(newVideo)) {
                        havaList.add(newVideo);
                    }
                }
            }
        }
        if (havaList.size() > 0) {
            proVideos.removeAll(havaList);
        }
        redisVideos.addAll(proVideos);
    }

    /**
     * 定向匹配（程序化）
     */
    private void directionalMatchPro(List<Long> targets, List<List<AiKuaishouAccountAutoVideo>> proVideos, List<List<AiKuaishouAccountAutoVideo>> redisVideos, Long appId, Long campaignId, String appVersion) {
        List<List<AiKuaishouAccountAutoVideo>> havaList = new ArrayList<>();
        int size = targets.size();
        if (proVideos.size() > size) {
            List<List<List<AiKuaishouAccountAutoVideo>>> lists = Lists.newArrayList(Lists.partition(proVideos, size));
            for (List<List<AiKuaishouAccountAutoVideo>> videos : lists) {
                for (int i = 0; i < videos.size(); i++) {
                    List<AiKuaishouAccountAutoVideo> video = videos.get(i);
                    for (AiKuaishouAccountAutoVideo vide : video) {
                        vide.setAutoTargetId(targets.get(i));
                        if (!Check.isNull(appId)) {
                            vide.setAppId(appId);
                            vide.setCampaignId(campaignId);
                            vide.setAppVersion(appVersion);
                        }
                    }
                    for (List<AiKuaishouAccountAutoVideo> redisVideo : redisVideos) {
                        if (redisVideo.size() == video.size() && redisVideo.containsAll(video)) {
                            havaList.add(video);
                        }
                    }
                }
            }
        } else {
            for (int i = 0; i < proVideos.size(); i++) {
                List<AiKuaishouAccountAutoVideo> video = proVideos.get(i);
                for (AiKuaishouAccountAutoVideo vide : video) {
                    vide.setAutoTargetId(targets.get(i));
                    if (!Check.isNull(appId)) {
                        vide.setAppId(appId);
                        vide.setCampaignId(campaignId);
                        vide.setAppVersion(appVersion);
                    }
                }
                for (List<AiKuaishouAccountAutoVideo> redisVideo : redisVideos) {
                    if (redisVideo.size() == video.size() && redisVideo.containsAll(video)) {
                        havaList.add(video);
                    }
                }
            }
        }
        if (havaList.size() > 0) {
            proVideos.removeAll(havaList);
        }
        redisVideos.addAll(proVideos);
    }

    /**
     * 整理程序化创意参数
     */
    private JSONObject getProgramCreativeParams(V3KuaishouAccountAutoStrategy strategy, CtopOauthToken token, List<AiKuaishouAccountAutoVideo> videos) {
        JSONObject programJson = new JSONObject();
        try {
            Integer singleAppid = strategy.getSingleAppid();
            programJson.put("package_name", "程序化创意1");

            //素材列表
            JSONArray photoList = new JSONArray();
            for (AiKuaishouAccountAutoVideo video : videos) {
                Integer type = video.getMaterialType();
                String photoId = video.getPhotoId();
                JSONObject photo = new JSONObject();
                photo.put("photo_id", photoId);//视频 ID
                photo.put("photo_orientation", type);//1：竖版视频 2：横版视频
                photoList.add(photo);
            }
            programJson.put("photo_infos", photoList);
            //创意播放方式；0=默认，1=轮播，2=优选
            if (!Check.isNull(strategy.getShowMode())) {
                programJson.put("show_mode", strategy.getShowMode());
            } else {
                programJson.put("show_mode", 0);
            }
            // 广告标签
            if (!Check.isNull(strategy.getExposeTag())) {
                programJson.put("expose_tag_str", JSONArray.parseArray(strategy.getExposeTag()));
            }
            //是否开启智能抽帧
            if (!Check.isNull(strategy.getSmartCover())) {
                programJson.put("smart_cover", strategy.getSmartCover() == 1);
            }
            //行动号召按钮
            if (!Check.isNull(strategy.getActionBarText())) {
                programJson.put("action_bar", getActionBar(strategy.getActionBarText()));
            }

            //第三方点击检测链接
            if (!Check.isNull(strategy.getClickTrackUrl())) {
                programJson.put("click_url", strategy.getClickTrackUrl());
            }

            //第三方 ActionBar 点击监控链接
            if (!Check.isNull(strategy.getActionbarClickUrl())) {
                programJson.put("actionbar_click_url", strategy.getActionbarClickUrl());
            }

            //作品广告语
            programJson.put("captions", getCaptions(strategy.getDescription()));

            if (!Check.isNull(strategy.getCreativeCategory())) {
                //创意分类 与创意标签同时传或同时不传
                programJson.put("creative_category", strategy.getCreativeCategory());
                //创意标签
                programJson.put("creative_tag", strategy.getCreativeTag());
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("整理程序化创意参数异常", e);
        }
        return programJson;
    }

    /**
     * 获取程序化广告语
     */
    private JSONArray getCaptions(String description) {
        JSONArray jsonArray = JSONArray.parseArray(description);
        if (jsonArray.size() <= 3) {
            return jsonArray;
        }
        //重置顺序
        Collections.shuffle(jsonArray);
        JSONArray captions = new JSONArray();
        for (int i = 0; i < jsonArray.size(); i++) {
            captions.add(jsonArray.getString(i));
            if (captions.size() == 3) {
                break;
            }
        }
        return captions;
    }

    /**
     * 获取素材
     *
     * @param strategy   策略对象
     * @param statDate   获取当前时间的素材
     * @param appInfo    应用，只针对对应用
     * @param videoType  创意创建类型 1:素材上新 2：历史高质量 3:历史遗漏素材 4：历史素材打捞
     * @param createType 创建类型 custom：自定义，program:程序化
     * @param videoCnt   获取素材数
     */
    private List<AiKuaishouAccountAutoVideo> getVideosByParams(V3KuaishouAccountAutoStrategy strategy, String statDate, Integer videoType, String createType, Integer videoCnt) {
        //素材关键词
        List<String> keyword = new ArrayList<>();
        String materialKeyword = strategy.getMaterialKeyword();
        if (!Check.isNull(materialKeyword) && materialKeyword.contains("-")) {
            keyword = Arrays.asList(materialKeyword.split("-"));
        }
        Long accountId = strategy.getAccountId();
        Integer channelType = strategy.getChannelType();
        if (videoType == 1) {
            //获取上新素材
            return autoVideoService.getUpNewVideos(accountId, channelType, keyword, videoCnt, statDate, createType);
        } else if (videoType == 2) {
            //历史高质量
            return autoVideoService.getHistoryTopVideos(accountId, channelType, keyword, videoCnt, statDate, createType);
        } else if (videoType == 3) {
            //获取遗漏素材：7天内未被创建过的素材（自动投放账户级重构）
            return autoVideoService.getMissVideoByParams(accountId, channelType, keyword, videoCnt, statDate);
        } else {
            //历史素材打捞
            return autoVideoService.getHistoryCatchVideos(accountId, channelType, keyword, videoCnt, statDate);
        }
    }

    /**
     * 检查计划是否创建
     */
    private void checkPlanIsCreated(V3KuaishouAccountAutoStrategy strategy, CtopOauthToken token, Integer videoType, List<List<AiKuaishouAccountAutoVideo>> videoGroup, String videoKey) {
        String nowDate = DateUtils.getNowDate("yyyy-MM-dd") + " 00:00:01";
        String typeNa = "自动程序化";
        //计划数
        Integer planCnt = strategy.getProgramPlanCnt();
        //组数
        Integer unitCnt = strategy.getProgramUnitCnt();
        if (strategy.getUnitType() == 4) {
            planCnt = strategy.getCustomPlanCnt();
            unitCnt = strategy.getCustomUnitCnt();
            typeNa = "自动自定义";
        }
        List<JSONObject> datas = campaignService.getCampaignIdsByParameters(strategy.getAccountId(), typeNa, nowDate);
        int toBeBuiltCnt = 0;
        Long campaignId = null;
        if (!Check.isNull(datas)) {
            for (JSONObject data : datas) {
                //计划下已建广告组数
                Integer count = data.getInteger("count");
                if (count >= unitCnt) {
                    continue;
                }
                //计划下还可建的广告组数
                toBeBuiltCnt = unitCnt - count;
                //若比本次待建数少，则用本计划建满，剩余不做创建，下次执行
                if (toBeBuiltCnt > 0 && toBeBuiltCnt < strategy.getToBeBuiltCnt()) {
                    strategy.setToBeBuiltCnt(toBeBuiltCnt);
                }
                campaignId = data.getLong("campaignId");
                break;
            }
            //判断是否仍可创建计划
            if (planCnt > datas.size()) {
                createCampaign(strategy, token, videoType, videoGroup, campaignId, videoKey);
            } else {
                log.info("当日创建量已达标");
            }
        } else {
            createCampaign(strategy, token, videoType, videoGroup, campaignId, videoKey);
        }
    }

    private void createCampaign(V3KuaishouAccountAutoStrategy strategy, CtopOauthToken token, Integer videoType, List<List<AiKuaishouAccountAutoVideo>> videoGroup, Long campaignId, String videoKey) {
        try {
            // 当日时间 yyyy-MM-dd
            String statDate = DateUtils.formatDate(new Date());
            //创建记录，通过uuid判断是同一组创建
            String uuid = UUID.randomUUID().toString();
            //账户ID
            Long accountId = token.getAccountId();
            if (getUnitOverrun(accountId)) {
                log.error("****** 账户（{}）创建广告组超限", accountId);
                return;
            }

            if (strategy.getUnitType() == 4) {
                if (!Check.isNull(overrunInfoService.getByParams(accountId, statDate))) {
                    log.error("****** 账户（{}）创建广告创意超限", accountId);
                    return;
                }
            } else {
                //程序化创建创意超限标识，redis key值
                String programKey = accountId + "_pcreativecount_" + statDate;
                Object redisObj = redisUtil.get(programKey);
                if (!Check.isNull(redisObj)) {
                    Integer value = (Integer) redisObj;
                    if (value == 1) {
                        log.error("****** 账户（{}）创建程序化创意超限", strategy.getAccountId());
                        return;
                    }
                }
            }

            //记录创建成功的素材，待最后执行删除redis
            List<List<AiKuaishouAccountAutoVideo>> successVideoList = new ArrayList<>();
            for (List<AiKuaishouAccountAutoVideo> videos : videoGroup) {
                JSONObject result = null;
                //计划ID为空->重新创建； 非空->补建
                if (Check.isNull(campaignId)) {
                    JSONObject newAdParams = getNewAdParams(strategy, token, videoType, videos);
                    result = v3detailsService.createCascadeAll(token, newAdParams);
                    createRecord(newAdParams, result.toJSONString());
                } else {
                    JSONObject hadAdParams = getHadAdParams(strategy, token, videoType, videos, campaignId);
                    result = v3detailsService.createUnitAndCreative(token, hadAdParams);
                    createRecord(hadAdParams, result.toJSONString());
                }
                if (!Check.isNull(result) && result.getInteger("code") == 0) {
                    successVideoList.add(videos);
                }
            }
            if (!Check.isNull(successVideoList) && successVideoList.isEmpty()) {
                //添加素材使用记录
                updateVideoUsed(videoType, successVideoList, strategy.getUnitType());
                removeProRedisVideos(successVideoList, videoKey);
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("创建计划异常", e);
        }
    }

    /**
     * V3创建记录
     */
    private void createRecord(JSONObject params, String result) {
        JSONObject record = new JSONObject();
        record.put("accountId", params.getLong("advertiser_id"));
        record.put("params", params.toJSONString());
        record.put("message", result);
        mapper.createRecord(record);
    }

    private JSONObject getNewAdParams(V3KuaishouAccountAutoStrategy strategy, CtopOauthToken token, Integer videoType, List<AiKuaishouAccountAutoVideo> videos) {
        JSONObject allParams = new JSONObject();
        allParams.put("advertiser_id", strategy.getAccountId());

        //计划参数
        JSONObject campaignParams = new JSONObject();
        String campaignName = getName(strategy.getCampaignName(), strategy, videoType, null, "plan", null, null);
        campaignParams.put("name", campaignName);
        campaignParams.put("type", strategy.getCampaignType());
        campaignParams.put("bid_type", strategy.getCampaignBidType());
        campaignParams.put("ad_type", strategy.getAdType());
        campaignParams.put("day_budget", strategy.getCampaignDayBudget());
        allParams.put("campaign", campaignParams);
        //广告组参数
        allParams.put("unit", createUnitParams(strategy, token, videos, videoType, null));
        //广告创意参数
        if (strategy.getUnitType() == 4) {
            allParams.put("customized_creative", getCreativeParams(strategy, token, videos));
        } else {
            allParams.put("advanced_creative", getProgramCreativeParams(strategy, token, videos));
        }
        return allParams;
    }

    private JSONObject getHadAdParams(V3KuaishouAccountAutoStrategy strategy, CtopOauthToken token, Integer videoType, List<AiKuaishouAccountAutoVideo> videos, Long campaignId) {
        JSONObject allParams = new JSONObject();
        allParams.put("advertiser_id", strategy.getAccountId());
        allParams.put("campaign_id", campaignId);
        //广告组参数
        allParams.put("unit", createUnitParams(strategy, token, videos, videoType, campaignId));
        //广告创意参数
        if (strategy.getUnitType() == 4) {
            allParams.put("customized_creative", getCreativeParams(strategy, token, videos));
        } else {
            allParams.put("advanced_creative", getProgramCreativeParams(strategy, token, videos));
        }
        return allParams;
    }


    /**
     * 整理广告组参数
     */
    private JSONObject createUnitParams(V3KuaishouAccountAutoStrategy strategy, CtopOauthToken token, List<AiKuaishouAccountAutoVideo> videos, Integer videoType, Long campaignId) {
        JSONObject unitParams = new JSONObject();
        // 拼装创建组的入参 json
        try {
            String unitName = getName(strategy.getGroupName(), strategy, videoType, videos.get(0).getPhotoName(), "unit", null, videos.get(0).getAutoTargetId());

            unitParams.put("name", unitName);

            Long appId = strategy.getAppId();
            //出价类型
            unitParams.put("bid_type", strategy.getUnitBidType());

            //组单日预算
            if (Check.isNull(strategy.getUnitDayBudget())) {
                unitParams.put("day_budget", 0);
            } else {
                unitParams.put("day_budget", strategy.getUnitDayBudget());
            }
            //应用商店列表
            unitParams.put("ocpx_action_type", strategy.getOcpxActionType());
            //出价
            if (null != strategy.getBid() && !"0".equals(strategy.getBid())) {
                unitParams.put("bid", strategy.getBid());
            }
            if (!Check.isNull(strategy.getCpaBid()) && !"0".equals((strategy.getCpaBid()))) {
                Long cpdBid = getBidByString(strategy.getCpaBid());
                unitParams.put("cpa_bid", cpdBid);
            }
            //优化目标
            if (!Check.isNull(strategy.getOcpxActionType())) {
                unitParams.put("ocpx_action_type", strategy.getOcpxActionType());
            }
            //深度转化目标
            if (!Check.isNull(strategy.getDeepConversionType())) {
                unitParams.put("deep_conversion_type", strategy.getDeepConversionType());
            }
            //深度转化目标出价
            if (!Check.isNull(strategy.getDeepConversionBid())) {
                Long cpdBid = getBidByString(strategy.getDeepConversionBid());
                unitParams.put("deep_conversion_bid", cpdBid);
            }
            //投放开始时间
            if (!Check.isNull(strategy.getBeginTime())) {
                unitParams.put("begin_time", DateUtils.timeToStamp(strategy.getBeginTime()));
            } else {
                unitParams.put("begin_time", DateUtils.getTimestamp());
            }
            //投放结束时间
            if (!Check.isNull(strategy.getEndTime())) {
                unitParams.put("end_time", DateUtils.timeToStamp(strategy.getEndTime()));
            }
            //投放排期
            if (!Check.isNull(strategy.getScheduleTime())) {
                unitParams.put("schedule", strategy.getScheduleTime());
            }
            //付费ROI系数
            if (!Check.isNull(strategy.getRoiRatio())) {
                unitParams.put("roi_ratio", strategy.getRoiRatio());
            }

            Integer sceneId = null;
            String scenceIdString = strategy.getSceneIds();

            if (!Check.isNull(scenceIdString) && !Check.isNull(JSONArray.parseArray(scenceIdString))) {
                JSONArray scenceArray = JSONArray.parseArray(scenceIdString);
                sceneId = scenceArray.getInteger(0);
                unitParams.put("scene_ids", scenceArray);
            }
            Integer campaignType = strategy.getCampaignType();
            if (campaignType == 2) { // 提升应用安装
                //应用ID
                if (!Check.isNull(appId)) {
                    unitParams.put("app_id", appId);
                }
                // 直达链接
                if (!Check.isNull(strategy.getSchemaUri())) {
                    unitParams.put("schema_uri", strategy.getSchemaUri());
                }
                // 优先从系统应用商店下载
                if (null != strategy.getUseAppMarket()) {
                    unitParams.put("use_app_market", strategy.getUseAppMarket());
                }
                if (null != strategy.getAppStore() && !"".equals(strategy.getAppStore())) {
                    unitParams.put("app_store", JSONArray.parseArray(strategy.getAppStore()));
                }
            } else if (campaignType == 3) { //  获取电商下单
                //url类型
                if (!Check.isNull(strategy.getUrlType())) {
                    unitParams.put("url_type", strategy.getUrlType());
                }
                if (!Check.isNull(strategy.getUrl())) {
                    unitParams.put("url", strategy.getUrl());
                }
            } else if (campaignType == 5) { //收集销售线索
                //url类型
                if (!Check.isNull(strategy.getWebUriType())) {
                    unitParams.put("web_uri_type", strategy.getWebUriType());
                }
                //程序化落地页
                if (!Check.isNull(strategy.getGroupId())) {
                    unitParams.put("group_id", strategy.getGroupId());
                }
                //url
                if (!Check.isNull(strategy.getUrl())) {
                    unitParams.put("url", strategy.getUrl());
                }
            } else if (campaignType == 7) { //  提高应用活跃
                //  appId
                if (!Check.isNull(appId)) {
                    unitParams.put("app_id", appId);
                }
                //  url
                if (!Check.isNull(strategy.getUrl())) {
                    unitParams.put("url", strategy.getUrl());
                }
                //调起链接
                if (!Check.isNull(strategy.getSchemaUri())) {
                    unitParams.put("schema_uri", strategy.getSchemaUri());
                }

            }

            //转化目标ID
            if (!Check.isNull(strategy.getConvertId())) {
                unitParams.put("convert_id", strategy.getConvertId());
            }
            //投放方式
            if (!Check.isNull(strategy.getSpeed())) {
                unitParams.put("speed", strategy.getSpeed());
            }
            //预约广告
            if (!Check.isNull(strategy.getSiteType())) {
                unitParams.put("site_type", strategy.getSiteType());
            }
            //智能定向
            if (!Check.isNull(strategy.getTargetType())) {
                unitParams.put("target_type", strategy.getTargetType());
            }
            //行为意向-系统优选
            if (!Check.isNull(strategy.getIntentionTarget()) && strategy.getIntentionTarget() != 0) {
                unitParams.put("intention_target", true);
            }
            //试玩 ID
            if (!Check.isNull(strategy.getPlayableIds())) {
                unitParams.put("playable_id", getPlayId(strategy.getPlayableIds(), strategy.getId(), campaignId));
                unitParams.put("play_button", strategy.getPlayButton());
            }
            //是否开启自动生成视频
            if (!Check.isNull(strategy.getAutoCreatePhoto())) {
                unitParams.put("auto_create_photo", strategy.getAutoCreatePhoto());
            }

            //是否开启智能抽帧
            if (!Check.isNull(strategy.getSmartCover())) {
                unitParams.put("smart_cover", strategy.getSmartCover() == 1);
            }
            //程序化创意2.0素材挖掘
            if (!Check.isNull(strategy.getAssetMining())) {
                unitParams.put("asset_mining", strategy.getAssetMining() == 1);
            }
            // 定向数据
            unitParams.put("target_info", getTarget(sceneId, videos.get(0).getAutoTargetId()));
            return unitParams;
        } catch (Exception e) {
            log.error("整理广告组数据异常", e);
            e.printStackTrace();
            return null;
        }
    }


    /**
     * 获取出价参数
     */
    private Long getBidByString(String cpaBid) {
        if (Check.isNull(cpaBid)) {
            return null;
        }
        String bidString = null;
        if (cpaBid.contains("/")) { // 阶梯出价
            String[] split = cpaBid.split("/");
            int random = new Random().nextInt(split.length);
            bidString = split[random];

        } else if (cpaBid.contains("-")) { // 随机出价
            String[] split = cpaBid.split("-");
            Double min = Double.valueOf(split[0]) / 1000;
            Double max = Double.valueOf(split[1]) / 1000;
            Double randomNumber = new BigDecimal(min + Math.random() * (max - min)).setScale(1, BigDecimal.ROUND_HALF_UP).doubleValue();
            bidString = String.valueOf(new Double(randomNumber * 1000).longValue());

        } else {
            bidString = cpaBid;
        }

        return Long.valueOf(bidString);
    }

    /**
     * 整理本地定向参数
     */
    private JSONObject getTarget(Integer sceneId, Long autoTargetId) {
        JSONObject target = new JSONObject();
        try {
            V3KuaishouAccountAutoTarget templateTarget = v3autoTargetService.getById(autoTargetId);
            if (Check.isNull(templateTarget)) {
                log.error("****** 未曾查到本地定向，id={}", autoTargetId);
                throw new Exception("未曾查到本地定向，id=" + autoTargetId);
            }
            // 地域
            List<String> region = JSONArray.parseArray(templateTarget.getRegionCategoryIds(), String.class);
            if (!Check.isNull(region) && !region.isEmpty()) {
                target.put("region_category_ids", region);
            }
            // 地域
            if (!Check.isNull(templateTarget.getRegionType())) {
                target.put("region_type", templateTarget.getRegionType());
            }
            //用户类型	0：实时；1：常驻；2：不限
            if (!Check.isNull(templateTarget.getUserType())) {
                target.put("user_type", templateTarget.getUserType());
            }
            //自定义年龄段
            if (!Check.isNull(templateTarget.getMax()) && !Check.isNull(templateTarget.getMin())) {
                JSONObject ageJson = new JSONObject();
                Integer max = templateTarget.getMax();
                Integer min = templateTarget.getMin();
                if (!Check.isNull(max) && max != -1) {
                    ageJson.put("max", max);
                }
                if (!Check.isNull(min) && min != -1) {
                    ageJson.put("min", min);
                }
                if (!ageJson.isEmpty()) {
                    target.put("age", ageJson);
                }
            }
            // 固定年龄段
            JSONArray agesRange = JSONArray.parseArray(templateTarget.getAgesRange());
            if (!Check.isNull(agesRange) && !agesRange.isEmpty()) {
                JSONArray array = new JSONArray();
                for (int i = 0; i < agesRange.size(); i++) {
                    JSONObject ages = new JSONObject();
                    Integer number = agesRange.getInteger(i);
                    switch (number) {
                        case 18:
                            ages.put("min", 18);
                            ages.put("max", 23);
                            break;
                        case 24:
                            ages.put("min",24);
                            ages.put("max",30);
                            break;
                        case 31:
                            ages.put("min",31);
                            ages.put("max",40);
                            break;
                        case 41:
                            ages.put("min",41);
                            ages.put("max",49);
                            break;
                        case 50:
                            ages.put("min",50);
                            ages.put("max",100);
                            break;
                        default:
                            break;
                    }
                    array.add(ages);
                }
                target.put("age", array);
            }
            // 性别
            if (!Check.isNull(templateTarget.getGender())) {
                target.put("gender", templateTarget.getGender());
            }
            // 操作系统
            String platform = templateTarget.getPlatform();
            if (!Check.isNull(platform)) {
                JSONObject obj = new JSONObject();
                JSONObject ob2 = new JSONObject();
                if ("1".equals(platform)) {
                    ob2.put("min", templateTarget.getAndroid());
                    obj.put("android", ob2);
                } else {
                    ob2.put("min", templateTarget.getIos());
                    obj.put("ios", ob2);
                }
                target.put("platform", obj);
            }
            // 网络环境
            if (!Check.isNull(templateTarget.getNetwork())) {
                target.put("network", JSONArray.parseArray(templateTarget.getNetwork()));
            }
            // 设备品牌
            JSONArray deviceBrand = JSONArray.parseArray(templateTarget.getDeviceBrandIds());
            if (!Check.isNull(deviceBrand) && !deviceBrand.isEmpty()) {
                target.put("device_brand_ids", deviceBrand);
            }
            // 设备价格
            JSONArray devicePrice = JSONArray.parseArray(templateTarget.getDevicePrice());
            if (!Check.isNull(devicePrice) && !devicePrice.isEmpty()) {
                JSONArray array = new JSONArray();
                for (int i = 0; i < devicePrice.size(); i++) {
                    JSONObject price = new JSONObject();
                    Integer number = devicePrice.getInteger(i);
                    switch (number) {
                        case 0:
                            price.put("min", 0);
                            price.put("max", 1000);
                            break;
                        case 1:
                            price.put("min", 1001);
                            price.put("max", 1500);
                            break;
                        case 2:
                            price.put("min", 1501);
                            price.put("max", 2000);
                            break;
                        case 3:
                            price.put("min", 2001);
                            price.put("max", 2500);
                            break;
                        case 5:
                            price.put("min", 2501);
                            price.put("max", 3000);
                            break;
                        case 6:
                            price.put("min", 3001);
                            price.put("max", 3500);
                            break;
                        case 7:
                            price.put("min", 3501);
                            price.put("max", 4000);
                            break;
                        case 8:
                            price.put("min", 4001);
                            price.put("max", 4500);
                            break;
                        case 9:
                            price.put("min", 4501);
                            price.put("max", 5000);
                            break;
                        case 10:
                            price.put("min", 5001);
                            price.put("max", 5500);
                            break;
                        case 11:
                            price.put("min", 5501);
                            price.put("max", 20000);
                            break;
                        default:
                            break;
                    }
                    array.add(price);
                }
                target.put("device_price", array);
            }
            // APP行为-按分类
            JSONArray appInterest = JSONArray.parseArray(templateTarget.getAppInterestIds());
            if (!Check.isNull(appInterest) && !appInterest.isEmpty()) {
                target.put("app_interest_ids", appInterest);
            }
            // APP行为-按APP名称
            JSONArray packageName = JSONArray.parseArray(templateTarget.getPackageName());
            if (!Check.isNull(packageName) && !packageName.isEmpty()) {
                target.put("package_name", packageName);
            }
            // 人群包定向
            JSONArray population = JSONArray.parseArray(templateTarget.getPopulation());
            if (!Check.isNull(population) && !population.isEmpty()) {
                target.put("population", population);
            }
            // 人群包排除
            JSONArray excludePopulation = JSONArray.parseArray(templateTarget.getExcludePopulation());
            if (!Check.isNull(excludePopulation) && !excludePopulation.isEmpty()) {
                target.put("exclude_population", excludePopulation);
            }
            // 付费人群包id
            JSONArray paidAudience = JSONArray.parseArray(templateTarget.getPaidAudience());
            if (!Check.isNull(paidAudience) && !paidAudience.isEmpty()) {
                target.put("paid_audience", paidAudience);
            }

            // 过滤已转化人群
            if (!Check.isNull(templateTarget.getFilterConvertedLevel())) {
                target.put("filter_converted_level", templateTarget.getFilterConvertedLevel());
            }

            if (sceneId != 5) {
                // 网红粉丝
                JSONArray fansStarString = JSONArray.parseArray(templateTarget.getFansStar());
                if (!Check.isNull(fansStarString) && !fansStarString.isEmpty()) {
                    target.put("fans_star", fansStarString);
                }
                // 兴趣视频用户
                JSONArray interestVideoString = JSONArray.parseArray(templateTarget.getInterestVideo());
                if (!Check.isNull(interestVideoString) && !interestVideoString.isEmpty()) {
                    target.put("interest_video", interestVideoString);
                }

                // 开启智能扩量
                String isOpen = templateTarget.getIsOpen();
                if (!Check.isNull(isOpen) && "1".equals(isOpen)) {
                    JSONObject intelliExtend = new JSONObject();
                    intelliExtend.put("is_open", true);
                    //  不可突破年龄
                    if (!Check.isNull(templateTarget.getNoAgeBreak())) {
                        intelliExtend.put("no_age_break", "1".equals(templateTarget.getNoAgeBreak()));
                    }
                    //  不可突破性别
                    if (!Check.isNull(templateTarget.getNoGenderBreak())) {
                        intelliExtend.put("no_gender_break", "1".equals(templateTarget.getNoGenderBreak()));
                    }
                    //  不可突破地域
                    if (!Check.isNull(templateTarget.getNoAreaBreak())) {
                        intelliExtend.put("no_area_break", "1".equals(templateTarget.getNoAreaBreak()));
                    }
                    // 智能扩量
                    target.put("intelli_extend", intelliExtend);
                }
            }

            //-------------行为兴趣定向
            JSONObject behaviorInterest = new JSONObject();
            JSONObject behavior = new JSONObject();
            JSONObject interest = new JSONObject();

            String keyword = templateTarget.getKeyword();
            if (!Check.isNull(keyword) && !keyword.isEmpty()) {
                behavior.put("keyword", JSONArray.parseArray(templateTarget.getKeyword()));
            }
            String label = templateTarget.getLabel();
            if (!Check.isNull(label) && !label.isEmpty()) {
                behavior.put("label", JSONArray.parseArray(templateTarget.getLabel()));
            }
            String interestLabel = templateTarget.getInterestLabel();
            if (!Check.isNull(interestLabel) && !interestLabel.isEmpty()) {
                interest.put("label", JSONArray.parseArray(interestLabel));
            }
            String sceneType = templateTarget.getSceneType();
            if (!Check.isNull(templateTarget.getTimeType()) && !Check.isNull(sceneType) && !sceneType.isEmpty() && !Check.isNull(templateTarget.getInterestLabel())) {
                behavior.put("time_type", templateTarget.getTimeType());
                behavior.put("scene_type", JSONArray.parseArray(sceneType));
                behavior.put("strength_type", templateTarget.getStrengthType());
                behaviorInterest.put("behavior", behavior);
                interest.put("scene_type", JSONArray.parseArray(sceneType));
                behaviorInterest.put("interest", interest);
                target.put("behavior_interest", behaviorInterest);
            }
            return target;
        } catch (Exception e) {
            log.error("整合本地定向数据异常", e);
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 整理自定义创意参数
     */
    private JSONObject getCreativeParams(V3KuaishouAccountAutoStrategy strategy, CtopOauthToken token, List<AiKuaishouAccountAutoVideo> videos) {
        JSONObject creativeParams = new JSONObject();
        try {
            //actionBar点击监测链接
            if (null != strategy.getActionbarClickUrl() && !"".equals(strategy.getActionbarClickUrl())) {
                creativeParams.put("actionbar_click_url", strategy.getActionbarClickUrl());
            }
            //曝光监测链接
            if (null != strategy.getImpressionUrl() && !"".equals(strategy.getImpressionUrl())) {
                creativeParams.put("impression_url", strategy.getImpressionUrl());
            }
            //创意分类
            if (null != strategy.getCreativeCategory()) {
                creativeParams.put("creative_category", strategy.getCreativeCategory());
            }
            //创意标签
            if (null != strategy.getCreativeTag() && !"".equals(strategy.getCreativeTag())) {
                JSONArray array = JSONArray.parseArray(strategy.getCreativeTag());
                int index = new Random().nextInt(array.size());
                creativeParams.put("creative_tag", array.getString(index));
            }
            //创意播放方式；0=默认，1=轮播，2=优选
            if (!Check.isNull(strategy.getShowMode())) {
                creativeParams.put("show_mode", strategy.getShowMode());
            }
            //广告语集
            JSONArray descList = JSONArray.parseArray(strategy.getDescription());
            JSONArray creativeListParams = new JSONArray();

            Integer count = strategy.getImageCnt();
            if (!Check.isNull(count)) {
                for (int i = 0; i < count; i++) {
                    AiKuaishouAccountAutoVideo video = videos.get(0);
                    JSONObject creative = new JSONObject();
                    //创意名称
                    creative.put("name", "自定义创意_" + (i + 1));
                    creative.put("photo_id", video.getPhotoId());
                    //广告语
                    int index = new Random().nextInt(descList.size());
                    creative.put("description", descList.getString(index));
                    //行动号召按钮文案
                    creative.put("action_bar", getActionBar(strategy.getActionBarText()));
                    //素材类型 1：竖版视频 2：横版视频
                    creative.put("creative_material_type", video.getMaterialType());
                    // 广告标签
                    if (!Check.isNull(strategy.getExposeTag())) {
                        creative.put("expose_tag_str", JSONArray.parseArray(strategy.getExposeTag()));
                    }
                    creativeListParams.add(creative);
                }
            } else {
                for (int i = 0; i < videos.size(); i++) {
                    AiKuaishouAccountAutoVideo video = videos.get(i);
                    JSONObject creative = new JSONObject();
                    //创意名称
                    creative.put("name", "自定义创意_" + (i + 1));
                    creative.put("photo_id", video.getPhotoId());
                    //广告语
                    int index = new Random().nextInt(descList.size());
                    creative.put("description", descList.getString(index));
                    //行动号召按钮文案
                    creative.put("action_bar", getActionBar(strategy.getActionBarText()));
                    //素材类型 1：竖版视频 2：横版视频
                    creative.put("creative_material_type", video.getMaterialType());
                    // 广告标签
                    if (!Check.isNull(strategy.getExposeTag())) {
                        creative.put("expose_tag_str", JSONArray.parseArray(strategy.getExposeTag()));
                    }
                    creativeListParams.add(creative);
                }
            }
            //批量参数
            creativeParams.put("creative_list_param", creativeListParams);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("整理自定义创意参数异常", e);
        }
        return creativeParams;
    }


    /**
     * 替换通配符（{{***}}）获取有效的计划名称/组名称
     * (strategy.getGroupName(), strategy, videoType, videos.get(0).getPhotoName(), "unit", null,videos.get(0).getAutoTargetId());
     */
    private String getName(String wildcard, V3KuaishouAccountAutoStrategy strategy, Integer videoType,
                           String materialName, String type, Long campaignId, Long targetId) {
        Long appId = strategy.getAppId();
        if (wildcard.contains("-{{定向包名}}-")) {
            AiKuaishouAccountAutoTarget templateTarget = autoTargetService.getById(targetId);
            if (!Check.isNull(templateTarget)) {
                wildcard = wildcard.replace("-{{定向包名}}-", templateTarget.getTargetName());
            } else {
                wildcard = wildcard.replace("-{{定向包名}}-", "");
            }
        }

        if (wildcard.contains("-{{应用名称}}-") || wildcard.contains("-{{应用标记}}-") || wildcard.contains("-{{应用包名}}-")) {
            KuaiShouAppList getAppInfo = appListService.getAppInfo(strategy.getAccountId(), appId);
            if (!Check.isNull(getAppInfo)) {
                wildcard = wildcard.replace("-{{应用名称}}-", getAppInfo.getAppName());
                wildcard = wildcard.replace("-{{应用标记}}-", getAppInfo.getAppVersion());
                wildcard = wildcard.replace("-{{应用包名}}-", getAppInfo.getPackageName());
            } else {
                wildcard = wildcard.replace("-{{应用名称}}-", "");
                wildcard = wildcard.replace("-{{应用标记}}-", "");
                wildcard = wildcard.replace("-{{应用包名}}-", "");
            }
        }

        if (wildcard.contains("-{{创意制作方式}}-")) {
            if (strategy.getUnitType() == 4) {
                wildcard = wildcard.replace("-{{创意制作方式}}-", "自定义");
            } else if (strategy.getUnitType() == 7) {
                wildcard = wildcard.replace("-{{创意制作方式}}-", "程序化");
            }
        }

        if (wildcard.contains("-{{广告位置}}-")) {
            String adsence = getAdSenceByParams(strategy.getSceneIds());
            wildcard = wildcard.replace("-{{广告位置}}-", adsence);
        }

        if (wildcard.contains("-{{素材名称}}-")) {
            if (Check.isNull(materialName)) {
                wildcard = wildcard.replace("-{{素材名称}}-", "");
            } else {
                wildcard = wildcard.replace("-{{素材名称}}-", materialName);
            }
        }
        if (!wildcard.contains("-{{自定义}}-")) {
            wildcard = wildcard + "-{{自定义}}-";
        }
        String nowDate = DateUtils.formatDate(new Date(), "yyMMdd");
        if ("plan".equals(type)) {
            //创意制作方式
            if (strategy.getUnitType() == 4) {
                wildcard = wildcard.replace("-{{自定义}}-", "自动自定义");
            } else {
                wildcard = wildcard.replace("-{{自定义}}-", "自动程序化");
            }
            if (wildcard.contains("-{{日期}}-")) {
                wildcard = wildcard.replace("-{{日期}}-", nowDate);
            } else {
                wildcard = wildcard + "-" + nowDate;
            }
            if (!wildcard.contains("-{{序号}}-")) {
                wildcard = wildcard + "-{{序号}}-";
            }
            Integer count = 0;
            String redisKey = strategy.getAccountId() + "_planCnt";
            Integer redisValue = (Integer) redisUtil.get(redisKey);
            if (Check.isNull(redisValue)) {
                count = 1;
                redisUtil.set(redisKey, count);
            } else {
                count = redisValue + 1;
                redisUtil.set(redisKey, count);
            }
            wildcard = wildcard.replace("-{{序号}}-", count + "");
        } else if ("unit".equals(type)) {
            String targetName = KuaishouAutoTypeEnum.getNameByType(strategy.getUnitType().toString() + videoType.toString());
            wildcard = wildcard.replace("-{{自定义}}-", targetName);
            wildcard = wildcard.replace("-{{日期}}-", nowDate);
        }
        //组序号，必填
        if (Check.isNull(campaignId)) {
            wildcard = wildcard + "-" + 0;
        } else {
            Integer cnt = null;
            String redisKey = strategy.getAccountId() + "_" + campaignId + nowDate;
            Integer redisCnt = (Integer) redisUtil.get(redisKey);
            if (Check.isNull(redisCnt)) {
                cnt = 1;
                redisUtil.set(redisKey, cnt, 100000L);
            } else {
                cnt = redisCnt + 1;
                redisUtil.set(redisKey, cnt, 100000L);
            }
            wildcard = wildcard + "-" + cnt;
        }
        wildcard = wildcard.replace("--", "-");
        wildcard = wildcard.replace("--", "-");

        if (wildcard.startsWith("-")) {
            wildcard = wildcard.substring(1, wildcard.length());
        }

        if (wildcard.endsWith("-")) {
            wildcard = wildcard.substring(0, wildcard.length() - 1);
        }
        return wildcard;
    }

    /**
     * 获取广告位置
     */
    private String getAdSenceByParams(String sceneIdString) {
        String adsence = "";
        if (null == sceneIdString || "".equals(sceneIdString)) {
            return adsence;
        }
        JSONArray sceneIdArray = JSONArray.parseArray(sceneIdString);
        if (null == sceneIdArray || sceneIdArray.isEmpty()) {
            return adsence;
        }
        for (int i = 0; i < sceneIdArray.size(); i++) {
            adsence += KuaishouCreativeAdsenceTypeEnum.getNameByType(sceneIdArray.getInteger(i)) + ",";
        }
        return adsence.substring(0, adsence.length() - 1);
    }


    /**
     * 组 创建是否超限
     */
    @Override
    public Boolean getUnitOverrun(Long accountId) {
        String nowDate = DateUtils.getNowDate("yyyy-MM-dd");
        String key = accountId + "_" + nowDate + "unit_create_overrun";
        Object redisObj = redisUtil.get(key);
        if (Check.isNull(redisObj)) {
            return false;
        }
        Integer value = (Integer) redisObj;
        if (value == 1) {
            return true;
        } else {
            return false;
        }
    }

    @Override
    public void supplementTodayCreatives(V3KuaishouAccountAutoStrategy strategy) {
        Long accountId = strategy.getAccountId();
        //==========账户{}执行自定义上新自动创建==========start==========
        String date = DateUtils.formatDate(new Date());
        String material = strategy.getSourceMaterial();

        if (strategy.getCustomPlanCnt() != null && strategy.getCustomUnitCnt() != null) {
            //当前需要创建广告组数量
            int unitNum = strategy.getCustomPlanCnt() * strategy.getCustomUnitCnt();
            //获取自定义已创建广告组数量
            String customkey = date + "_v3_custom_cnt_" + accountId;
            Integer customUnitCnt = (Integer) redisUtil.get(customkey);
            if (Check.isNull(customUnitCnt)) {
                if (unitNum > 600) {
                    unitNum = 600;
                }
            } else {
                unitNum = unitNum - customUnitCnt;
            }
            if (unitNum == 0) {
                return;
            }
            if (unitNum > 0) {
                // 1上新
                log.info("-----每日补充创建 （自定义上新）账户：{}，本次可建组数：{}", strategy.getAccountId(), unitNum);
                unitNum = this.autoCreateCreative(strategy, 1, unitNum);
            }

            if (unitNum > 0 && material.contains("2")) {
                //2高质量素材
                log.info("-----每日补充创建 （自定义高质量）账户：{}，本次可建组数：{}", strategy.getAccountId(), unitNum);
                unitNum = this.autoCreateCreative(strategy, 2, unitNum);
            }
//            if (unitNum > 0 && material.contains("3")) {
//                //3历史遗漏
//                log.info("-----每日补充创建 （自定义历史遗漏）账户：{}，本次可建组数：{}", strategy.getAccountId(), unitNum);
//                unitNum = this.autoCreateCreative(strategy, 3, unitNum);
//            }
//            if (unitNum > 0 && material.contains("4")) {
//                //4历史打捞素材
//                log.info("-----每日补充创建 （自定义历史打捞）账户：{}，本次可建组数：{}", strategy.getAccountId(), unitNum);
//                this.autoCreateCreative(strategy, 4, unitNum);
//            }
        }

        if (strategy.getProgramPlanCnt() != null && strategy.getProgramUnitCnt() != null) {

            int punitNum = strategy.getProgramPlanCnt() * strategy.getProgramUnitCnt();
            String programkey = date + "_v3_program_cnt_" + accountId;
            Integer programUnitCnt = (Integer) redisUtil.get(programkey);
            if (Check.isNull(programUnitCnt)) {
                if (punitNum > 300) {
                    punitNum = 300;
                }
            } else {
                punitNum = punitNum - programUnitCnt;
            }
            if (punitNum == 0) {
                return;
            }
            if (punitNum > 0) {
                //1上新
                log.info("-----每日补充创建 （程序化上新）账户：{}，本次可建组数：{}", strategy.getAccountId(), punitNum);
                punitNum = this.autoCreateProgramCreative(strategy, 1, punitNum);
            }
            if (punitNum > 0 && material.contains("2")) {
                log.info("-----每日补充创建 （程序化高质量）账户：{}，本次可建组数：{}", strategy.getAccountId(), punitNum);
                //2高质量素材
                this.autoCreateProgramCreative(strategy, 2, punitNum);
            }
        }
    }


    /**
     * 获取创建比例
     */
    private JSONObject getCnt(String material, int unitNum, int hours) {
        JSONObject object = new JSONObject();
        int upNewCnt = unitNum;
        //高质量待建数
        int highCnt = 0;
        //遗漏
        int omitCnt = 0;
        //打捞
        int salvageCnt = 0;

        //如果当前待建数低于循环次数（hours),则待建数 = 1
        if (unitNum < hours) {
            unitNum = 1;
        }
        /*素材优先级以及百分比：1上新、2高质量、3遗漏、4打捞
         *全选的情况：上新（70%）：高质量（20%）：遗漏（5%）：打捞（5%）
         *部分选择：上新：高质量=75%:25%；上新：遗漏/+打捞=85%：15%
         *上新：高质量：遗漏/打捞=70%:20%:10% */
        if (material.contains("2")) {
            if (material.contains("3") && material.contains("4")) {
                /*----------全选的情况：上新（70%）：高质量（20%）：遗漏（5%）：打捞（5%）----------*/
                //上新
                upNewCnt = getRealCnt(unitNum, hours, 0.7);
                //高质量
                highCnt = getRealCnt(unitNum, hours, 0.2);
                //遗漏
                omitCnt = getRealCnt(unitNum, hours, 0.05);
                //打捞
                salvageCnt = getRealCnt(unitNum, hours, 0.05);
            } else if (material.contains("3") || material.contains("4")) {
                /*----------上新：高质量：遗漏/打捞=70%:20%:10% ----------*/
                //上新
                upNewCnt = getRealCnt(unitNum, hours, 0.7);
                //高质量
                highCnt = getRealCnt(unitNum, hours, 0.2);
                //遗漏
                omitCnt = getRealCnt(unitNum, hours, 0.1);
                //打捞
                salvageCnt = getRealCnt(unitNum, hours, 0.1);
            } else {
                /*----------部分选择：上新：高质量=75%:25%；----------*/
                //上新
                upNewCnt = getRealCnt(unitNum, hours, 0.75);
                //高质量
                highCnt = getRealCnt(unitNum, hours, 0.25);
            }
        } else {
            if (material.contains("3") && material.contains("4")) {
                /*---------- 上新：遗漏:打捞=80%：10%：10%----------*/
                //上新
                upNewCnt = getRealCnt(unitNum, hours, 0.8);
                //遗漏
                omitCnt = getRealCnt(unitNum, hours, 0.1);
                //打捞
                salvageCnt = getRealCnt(unitNum, hours, 0.1);

            } else if (material.contains("3") || material.contains("4")) {
                /*---------- 上新：遗漏/+打捞=85%：15%----------*/
                //上新
                upNewCnt = getRealCnt(unitNum, hours, 0.85);
                //遗漏
                omitCnt = getRealCnt(unitNum, hours, 0.15);
                //打捞
                salvageCnt = getRealCnt(unitNum, hours, 0.15);
            } else {
                /*---------- 100%上新----------*/
                //上新
                upNewCnt = getRealCnt(unitNum, hours, 1);
            }
        }
        object.put("upNewCnt", upNewCnt);
        object.put("highCnt", highCnt);
        object.put("omitCnt", omitCnt);
        object.put("salvageCnt", salvageCnt);
        System.out.println("upNewCnt：" + upNewCnt + "，highCnt：" + highCnt + "omitCnt：" + omitCnt + "，salvageCnt：" + salvageCnt);
        return object;
    }

    private int getRealCnt(int unitNum, int hours, double i) {
        //上新总的待建数
        int upNewCnt = (int) (unitNum * i);
        if (upNewCnt > hours) {
            //总待建数大于循环次数
            upNewCnt = upNewCnt / hours;
        } else if (upNewCnt != 0) {
            //总待建数大于1时，本次执行创建一个高质量
            upNewCnt = 1;
        } else {
            upNewCnt = 0;
        }
        return upNewCnt;
    }

    public static String getActionBar(String actionBarText) {
        if (Check.isNull(actionBarText)) {
            return null;
        }
        String[] split = actionBarText.split(",");
        int i = getrandom(0, split.length - 1);
        return split[i];
    }

    public static int getrandom(int start, int end) {
        int num = (int) (Math.random() * (end - start + 1) + start);
        return num;
    }

    /**
     * 轮询使用 playableIds 值，playableIds string格式：123,456,789,122
     */
    public Long getPlayId(String playableIds, Long strategyId, Long campaignId) {
        JSONObject obj = JSONObject.parseObject(playableIds);
        String playIds = obj.getString("ids");
        String playId = null;
        Map<String, Object> map = new HashMap<>();
        String redisKey = strategyId + "-play-" + campaignId + "-" + DateUtils.formatDate(new Date(), "yyMMdd");
        Map<Object, Object> mget = redisUtil.hmget(redisKey);
        if (Check.isNullMap(mget)) {
            String[] ids = playIds.split(",");
            playId = ids[0];
            if (ids.length > 1) {
                String newValues = playIds.replace(playId + ",", "");
                map.put(String.valueOf(0), newValues);
            }
            map.put(String.valueOf(1), playId);
            redisUtil.hmset(redisKey, map, 100000L);
        } else {
            for (Object key : mget.keySet()) {
                String keyIndex = String.valueOf(key);
                String values = String.valueOf(mget.get(keyIndex));
                String[] ids = values.split(",");
                playId = ids[0];
                if (ids.length > 1) {
                    String newValues = values.replace(playId + ",", "");
                    map.put(keyIndex, newValues);
                } else {
                    redisUtil.hdel(redisKey, keyIndex);
                }
                int upIndex = Integer.valueOf(keyIndex) + 1;
                String upValues = String.valueOf(mget.get(String.valueOf(upIndex)));
                if ("null".equals(upValues)) {
                    map.put(String.valueOf(upIndex), playId);
                } else {
                    map.put(String.valueOf(upIndex), upValues + "," + playId);
                }
                break;
            }
            redisUtil.hmset(redisKey, map, 100000L);
        }
        return Long.valueOf(playId);
    }

    /**
     * 回滚 playableIds 值
     */
    public void rollbackPlayId(JSONObject unitParams, V3KuaishouAccountAutoStrategy strategy) {
        Long playableId = unitParams.getLong("playable_id");
        if (Check.isNull(playableId)) {
            return;
        }
        Map<String, Object> map = new HashMap<>();
        String redisKey = strategy.getId() + "-play-" + unitParams.getLong("campaign_id") + "-" + DateUtils.formatDate(new Date(), "yyMMdd");
        Map<Object, Object> mget = redisUtil.hmget(redisKey);
        if (!Check.isNullMap(mget)) {
            for (Object key : mget.keySet()) {
                String keyIndex = String.valueOf(key);
                String values = String.valueOf(mget.get(keyIndex));
                if (values.contains(playableId + "")) {
                    break;
                }
                values = playableId + "," + values;
                map.put(keyIndex, values);

                String upIndex = Integer.valueOf(keyIndex) + 1 + "";
                String upValues = String.valueOf(mget.get(upIndex));
                if (!"null".equals(upValues)) {
                    if (String.valueOf(playableId).equals(upValues)) {
                        redisUtil.hdel(redisKey, upIndex);
                    } else {
                        String newValues = upValues.replace("," + playableId, "");
                        map.put(upIndex, newValues);
                    }
                }
                break;
            }
            redisUtil.hmset(redisKey, map, 100000L);
        }
    }


}
