package com.moka.gdtauto.service.impl;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.moka.gdtauto.client.TencentAdsApiClientFactory;
import com.moka.gdtauto.common.AdPlanStatus;
import com.moka.gdtauto.common.Constant;
import com.moka.gdtauto.entity.AdPlan;
import com.moka.gdtauto.entity.AdPlanConf;
import com.moka.gdtauto.entity.AdPlanCreative;
import com.moka.gdtauto.entity.OssFile;
import com.moka.gdtauto.entity.ReqDailyAdgroupReport;
import com.moka.gdtauto.entity.TencentAccountIdBrandImage;
import com.moka.gdtauto.mapper.AdPlanConfMapper;
import com.moka.gdtauto.service.AdPlanConfService;
import com.moka.gdtauto.service.AdPlanCreativeService;
import com.moka.gdtauto.service.AdPlanService;
import com.moka.gdtauto.service.ReqDailyAdgroupReportService;
import com.moka.gdtauto.service.TencentAccountIdBrandImageService;
import com.moka.gdtauto.service.TencentAccountIdImageService;
import com.moka.gdtauto.service.TencentAccountIdVideoIdService;
import com.moka.gdtauto.service.TencentAdsAdgroupService;
import com.moka.gdtauto.service.TencentAssetService;
import com.moka.gdtauto.util.AdgroupsAddReqPool;
import com.moka.gdtauto.util.StrUtil;
import com.tencent.ads.exception.TencentAdsResponseException;
import com.tencent.ads.model.v3.ActionButtonComponent;
import com.tencent.ads.model.v3.ActionButtonStruct;
import com.tencent.ads.model.v3.AdgroupsAddRequest;
import com.tencent.ads.model.v3.AdgroupsAddResponseData;
import com.tencent.ads.model.v3.AdgroupsDeleteRequest;
import com.tencent.ads.model.v3.AdgroupsUpdateRequest;
import com.tencent.ads.model.v3.BrandComponent;
import com.tencent.ads.model.v3.BrandStruct;
import com.tencent.ads.model.v3.ConfiguredStatus;
import com.tencent.ads.model.v3.CreativeComponents;
import com.tencent.ads.model.v3.DeliveryMode;
import com.tencent.ads.model.v3.DescriptionComponent;
import com.tencent.ads.model.v3.DescriptionStruct;
import com.tencent.ads.model.v3.DynamicCreativeType;
import com.tencent.ads.model.v3.DynamicCreativesAddRequest;
import com.tencent.ads.model.v3.DynamicCreativesAddResponseData;
import com.tencent.ads.model.v3.DynamicCreativesDeleteRequest;
import com.tencent.ads.model.v3.DynamicCreativesUpdateRequest;
import com.tencent.ads.model.v3.ImageComponent;
import com.tencent.ads.model.v3.ImageStruct;
import com.tencent.ads.model.v3.JumpinfoComponent;
import com.tencent.ads.model.v3.JumpinfoStruct;
import com.tencent.ads.model.v3.PageSpec;
import com.tencent.ads.model.v3.PageType;
import com.tencent.ads.model.v3.SmartTargetingMode;
import com.tencent.ads.model.v3.TitleComponent;
import com.tencent.ads.model.v3.TitleStruct;
import com.tencent.ads.model.v3.VideoComponent;
import com.tencent.ads.model.v3.VideoStruct;
import com.tencent.ads.model.v3.XjPageSpec;
import com.tencent.ads.v3.TencentAds;

import lombok.extern.slf4j.Slf4j;

/**
 * 广告计划配置服务实现类
 *
 * @author moka
 * @since 2026-02-24
 */
@Slf4j
@Service
public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanConf> implements AdPlanConfService {

    @Autowired
    private AdPlanService adPlanService;
    @Autowired
    private AdPlanCreativeService adPlanCreativeService;
    @Autowired
    private TencentAdsAdgroupService tencentAdsAdgroupService;
    @Autowired
    private TencentAdsApiClientFactory clientFactory;
    @Autowired
    private TencentAccountIdImageService tencentAccountIdImageService;
    @Autowired
    private TencentAccountIdVideoIdService tencentAccountIdVideoIdService;
    @Autowired
    private TencentAccountIdBrandImageService tencentAccountIdBrandImageService;
    @Autowired
    private ReqDailyAdgroupReportService reqDailyAdgroupReportService;

    @Autowired
    private TencentAssetService tencentAssetService;

    @Async
    @Override
    public void gdtDeleteByConf(AdPlanConf conf) {
        try {
            doDeletePlanAll(conf);
            doDeleteCreativeAll(conf);
            this.lambdaUpdate()
                .eq(AdPlanConf::getId, conf.getId())
                .set(AdPlanConf::getSystemDeleteProcessStatus, 2)
                .update();
        } catch (Exception e) {
            log.error("gdtDeleteByConf error", e);
            this.lambdaUpdate()
                .eq(AdPlanConf::getId, conf.getId())
                .set(AdPlanConf::getSystemDeleteProcessStatus, 3)
                .set(AdPlanConf::getFailReason, StrUtil.truncateError(e.getMessage())).update();
        }
    }

    private void doDeletePlanAll(AdPlanConf conf) throws Exception {
        log.info("doDeletePlanAll {}", conf.getId());
        TencentAds tencentAds = clientFactory.getTencentAds(conf.getJobNumber(), Constant.MAIN_ORG_ACCOUNT_ID);
        int pageSize = 100;
        int pageNum = 1;
        while (true) {
            LambdaQueryWrapper<AdPlan> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(AdPlan::getAdPlanConfId, conf.getId())
                    .gt(AdPlan::getAdgroupId, 0)
                    .orderByAsc(AdPlan::getId);
            Page<AdPlan> page = adPlanService.page(new Page<>(pageNum, pageSize), queryWrapper);
            List<AdPlan> plans = page.getRecords();
            if (plans.isEmpty()) {
                break;
            }
            doDeletePlans(tencentAds, plans);
            if (!page.hasNext()) {
                break;
            }
            pageNum++;
            TimeUnit.SECONDS.sleep(1);
        }
    }

    private void doDeletePlans(TencentAds tencentAds, List<AdPlan> plans) throws Exception {
        for (AdPlan plan : plans) {
            try {
                AdgroupsDeleteRequest req = new AdgroupsDeleteRequest();
                req.setAccountId(plan.getAccountId());
                req.setAdgroupId(plan.getAdgroupId());
                tencentAds.adgroups().adgroupsDelete(req);
                plan.setFailReason("删除成功");
            } catch (Exception e) {
                plan.setFailReason(StrUtil.truncateError(e.getMessage()));
            }
            adPlanService.lambdaUpdate()
                    .eq(AdPlan::getId, plan.getId())
                    .set(AdPlan::getSystemDelete, "true")
                    .set(AdPlan::getFailReason, plan.getFailReason())
                    .update();
        }
    }
    private void doDeleteCreativeAll(AdPlanConf conf) throws Exception {
        log.info("doDeleteCreativeAll {}", conf.getId());
        TencentAds tencentAds = clientFactory.getTencentAds(conf.getJobNumber(), Constant.MAIN_ORG_ACCOUNT_ID);
        int pageSize = 100;
        int pageNum = 1;
        while (true) {
            LambdaQueryWrapper<AdPlanCreative> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(AdPlanCreative::getAdPlanConfId, conf.getId())
                    .gt(AdPlanCreative::getAdgroupId, 0)
                    .gt(AdPlanCreative::getCreativeId, 0)
                    .orderByAsc(AdPlanCreative::getId);
            Page<AdPlanCreative> page = adPlanCreativeService.page(new Page<>(pageNum, pageSize), queryWrapper);
            List<AdPlanCreative> creatives = page.getRecords();
            if (creatives.isEmpty()) {
                break;
            }
            doDeleteCreative(tencentAds, creatives);
            if (!page.hasNext()) {
                break;
            }
            pageNum++;
            TimeUnit.SECONDS.sleep(1);
        }
    }

    private void doDeleteCreative(TencentAds tencentAds, List<AdPlanCreative> creatives) throws Exception {
        for (AdPlanCreative creative : creatives) {
            try {
                DynamicCreativesDeleteRequest req = new DynamicCreativesDeleteRequest();
                req.setAccountId(creative.getAccountId());
                req.setDynamicCreativeId(creative.getCreativeId());
                tencentAds.dynamicCreatives().dynamicCreativesDelete(req);
                creative.setFailReason("删除成功");
            } catch (Exception e) {
                creative.setFailReason(StrUtil.truncateError(e.getMessage()));
            }
            adPlanCreativeService.lambdaUpdate()
                    .eq(AdPlanCreative::getId, creative.getId())
                    .set(AdPlanCreative::getSystemDelete, "true")
                    .set(AdPlanCreative::getFailReason, creative.getFailReason())
                    .update();
        }
    }

    @Async
    @Override
    public void gdtDeleteByPlanIds(AdPlanConf conf, List<Long> planIds) {
        log.info("gdtDeleteByPlanIds {}", conf.getId());
        try {
            TencentAds tencentAds = clientFactory.getTencentAds(conf.getJobNumber(), Constant.MAIN_ORG_ACCOUNT_ID);
            List<AdPlan> plans = adPlanService.lambdaQuery()
                    .eq(AdPlan::getAdPlanConfId, conf.getId())
                    .in(AdPlan::getId, planIds)
                    .gt(AdPlan::getAdgroupId, 0)
                    .orderByAsc(AdPlan::getId).list();
            doDeletePlans(tencentAds, plans);

            List<Long> adPlanIds = plans.stream().map(AdPlan::getId).collect(Collectors.toList());
            List<AdPlanCreative> creatives = adPlanCreativeService.lambdaQuery()
                                .in(AdPlanCreative::getAdPlanId, adPlanIds)
                                .gt(AdPlanCreative::getAdgroupId, 0)
                                .gt(AdPlanCreative::getCreativeId, 0)
                                .orderByAsc(AdPlanCreative::getId)
                                .list();
            doDeleteCreative(tencentAds, creatives);

            this.lambdaUpdate()
                .eq(AdPlanConf::getId, conf.getId())
                .set(AdPlanConf::getSystemDeleteProcessStatus, 2)
                .update();
        } catch (Exception e) {
            log.error("gdtDeleteByPlanIds error", e);
            this.lambdaUpdate()
                .eq(AdPlanConf::getId, conf.getId())
                .eq(AdPlanConf::getSystemDeleteProcessStatus, 3)
                .set(AdPlanConf::getFailReason, StrUtil.truncateError(e.getMessage())).update();
        }
    }

    @Async
    @Override
    public void gdtSuspendByConf(AdPlanConf conf) {
        try {
            doSuspendPlanAll(conf);
            doSuspendCreativeAll(conf);
            this.lambdaUpdate()
                .eq(AdPlanConf::getId, conf.getId())
                .set(AdPlanConf::getSystemSuspendProcessStatus, 2)
                .update();
        } catch (Exception e) {
            log.error("gdtSuspendByConf error", e);
            this.lambdaUpdate()
                .eq(AdPlanConf::getId, conf.getId())
                .set(AdPlanConf::getSystemSuspendProcessStatus, 3)
                .set(AdPlanConf::getFailReason, StrUtil.truncateError(e.getMessage())).update();
        }
    }

    private void doSuspendPlanAll(AdPlanConf conf) throws Exception {
        log.info("doSuspendPlanAll {}", conf.getId());
        TencentAds tencentAds = clientFactory.getTencentAds(conf.getJobNumber(), Constant.MAIN_ORG_ACCOUNT_ID);
        int pageSize = 100;
        int pageNum = 1;
        while (true) {
            LambdaQueryWrapper<AdPlan> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(AdPlan::getAdPlanConfId, conf.getId())
                    .gt(AdPlan::getAdgroupId, 0)
                    .orderByAsc(AdPlan::getId);
            Page<AdPlan> page = adPlanService.page(new Page<>(pageNum, pageSize), queryWrapper);
            List<AdPlan> plans = page.getRecords();
            if (plans.isEmpty()) {
                break;
            }
            doSuspendPlans(tencentAds, plans);
            if (!page.hasNext()) {
                break;
            }
            pageNum++;
            TimeUnit.SECONDS.sleep(1);
        }
    }

    private void doSuspendPlans(TencentAds tencentAds, List<AdPlan> plans) throws Exception {
        for (AdPlan plan : plans) {
            try {
                AdgroupsUpdateRequest req = new AdgroupsUpdateRequest();
                req.setAccountId(plan.getAccountId());
                req.setAdgroupId(plan.getAdgroupId());
                req.setConfiguredStatus(ConfiguredStatus.SUSPEND);
                req.setSmartTargetingMode(SmartTargetingMode.SMART_TARGETING_MANUAL);
                tencentAds.adgroups().adgroupsUpdate(req);
                plan.setConfiguredStatus(ConfiguredStatus.SUSPEND.getValue());
            } catch (Exception e) {
                plan.setFailReason(StrUtil.truncateError(e.getMessage()));
            }
            adPlanService.lambdaUpdate()
                    .eq(AdPlan::getId, plan.getId())
                    .set(AdPlan::getConfiguredStatus, plan.getConfiguredStatus())
                    .set(AdPlan::getFailReason, plan.getFailReason())
                    .update();
        }
    }

    private void doSuspendCreativeAll(AdPlanConf conf) throws Exception {
        log.info("doSuspendCreativeAll {}", conf.getId());
        TencentAds tencentAds = clientFactory.getTencentAds(conf.getJobNumber(), Constant.MAIN_ORG_ACCOUNT_ID);
        int pageSize = 100;
        int pageNum = 1;
        while (true) {
            LambdaQueryWrapper<AdPlanCreative> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(AdPlanCreative::getAdPlanConfId, conf.getId())
                    .gt(AdPlanCreative::getAdgroupId, 0)
                    .gt(AdPlanCreative::getCreativeId, 0)
                    .orderByAsc(AdPlanCreative::getId);
            Page<AdPlanCreative> page = adPlanCreativeService.page(new Page<>(pageNum, pageSize), queryWrapper);
            List<AdPlanCreative> creatives = page.getRecords();
            if (creatives.isEmpty()) {
                break;
            }
            doSuspendCreative(tencentAds, creatives);
            if (!page.hasNext()) {
                break;
            }
            pageNum++;
            TimeUnit.SECONDS.sleep(1);
        }

    }

    private void doSuspendCreative(TencentAds tencentAds, List<AdPlanCreative> creatives)
            throws Exception {
        for (AdPlanCreative creative : creatives) {
            try {
                DynamicCreativesUpdateRequest req = new DynamicCreativesUpdateRequest();
                req.setAccountId(creative.getAccountId());
                req.setDynamicCreativeId(creative.getCreativeId());
                req.setConfiguredStatus(ConfiguredStatus.SUSPEND);
                tencentAds.dynamicCreatives().dynamicCreativesUpdate(req);
                creative.setConfiguredStatus(ConfiguredStatus.SUSPEND.getValue());
            } catch (Exception e) {
                creative.setFailReason(StrUtil.truncateError(e.getMessage()));
            }
            adPlanCreativeService.lambdaUpdate()
                    .eq(AdPlanCreative::getId, creative.getId())
                    .set(AdPlanCreative::getConfiguredStatus, creative.getConfiguredStatus())
                    .set(AdPlanCreative::getFailReason, creative.getFailReason())
                    .update();
        }
    }

    @Async
    @Override
    public void gdtSuspendByPlanIds(AdPlanConf conf, List<Long> planIds) {
        try {
            TencentAds tencentAds = clientFactory.getTencentAds(conf.getJobNumber(), Constant.MAIN_ORG_ACCOUNT_ID);
            List<AdPlan> plans = adPlanService.lambdaQuery()
                    .eq(AdPlan::getAdPlanConfId, conf.getId())
                    .in(AdPlan::getId, planIds)
                    .gt(AdPlan::getAdgroupId, 0)
                    .orderByAsc(AdPlan::getId).list();
            doSuspendPlans(tencentAds, plans);

            List<Long> adPlanIds = plans.stream().map(AdPlan::getId).collect(Collectors.toList());
            List<AdPlanCreative> creatives = adPlanCreativeService.lambdaQuery()
                                .in(AdPlanCreative::getAdPlanId, adPlanIds)
                                .gt(AdPlanCreative::getAdgroupId, 0)
                                .gt(AdPlanCreative::getCreativeId, 0)
                                .orderByAsc(AdPlanCreative::getId)
                                .list();
            doSuspendCreative(tencentAds, creatives);
            this.lambdaUpdate()
                .eq(AdPlanConf::getId, conf.getId())
                .set(AdPlanConf::getSystemSuspendProcessStatus, 2)
                .update();
        } catch (Exception e) {
            log.error("gdtSuspendByPlanIds error", e);
            this.lambdaUpdate()
                .eq(AdPlanConf::getId, conf.getId())
                .set(AdPlanConf::getSystemSuspendProcessStatus, 3)
                .set(AdPlanConf::getFailReason, StrUtil.truncateError(e.getMessage())).update();
        }
    }

    @Override
    public void gdtSuspendByPlans(AdPlanConf conf, List<AdPlan> plans) {
        try {
            TencentAds tencentAds = clientFactory.getTencentAds(conf.getJobNumber(), Constant.MAIN_ORG_ACCOUNT_ID);
            doSuspendPlans(tencentAds, plans);
            this.lambdaUpdate()
                .eq(AdPlanConf::getId, conf.getId())
                .set(AdPlanConf::getSystemSuspendProcessStatus, 2)
                .update();
        } catch (Exception e) {
            log.error("gdtSuspendByPlanIds error", e);
            this.lambdaUpdate()
                .eq(AdPlanConf::getId, conf.getId())
                .set(AdPlanConf::getSystemSuspendProcessStatus, 3)
                .set(AdPlanConf::getFailReason, StrUtil.truncateError(e.getMessage())).update();
        }
    }

    @Override
    public void gdtSuspendByCreatives(AdPlanConf conf, List<AdPlanCreative> creatives) {
        try {
            TencentAds tencentAds = clientFactory.getTencentAds(conf.getJobNumber(), Constant.MAIN_ORG_ACCOUNT_ID);
            doSuspendCreative(tencentAds, creatives);
            this.lambdaUpdate()
                .eq(AdPlanConf::getId, conf.getId())
                .set(AdPlanConf::getSystemSuspendProcessStatus, 2)
                .update();
        } catch (Exception e) {
            log.error("gdtSuspendByCreatives error", e);
            this.lambdaUpdate()
                .eq(AdPlanConf::getId, conf.getId())
                .set(AdPlanConf::getSystemSuspendProcessStatus, 3)
                .set(AdPlanConf::getFailReason, StrUtil.truncateError(e.getMessage())).update();
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createAdPlanConf(AdPlanConf adPlanConf) {
        adPlanConf.setCreateTime(LocalDateTime.now());
        adPlanConf.setUpdateTime(LocalDateTime.now());
        adPlanConf.setDeleted(0);

        this.save(adPlanConf);
        log.info("创建广告计划配置成功，配置ID: {}, 配置名称: {}", adPlanConf.getId(), adPlanConf.getName());

        return adPlanConf.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateAdPlanConf(AdPlanConf adPlanConf) {
        AdPlanConf existingConf = this.getOne(new LambdaQueryWrapper<AdPlanConf>()
                .eq(AdPlanConf::getId, adPlanConf.getId())
                .eq(AdPlanConf::getDeleted, 0));

        if (existingConf == null) {
            log.warn("广告计划配置不存在或无权限，配置ID: {}, 用户ID: {}", adPlanConf.getId(), adPlanConf.getUserId());
            return false;
        }

        adPlanConf.setUpdateTime(LocalDateTime.now());
        boolean result = this.updateById(adPlanConf);
        log.info("更新广告计划配置成功，配置ID: {}", adPlanConf.getId());

        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteAdPlanConf(Long confId, Long userId) {
        AdPlanConf existingConf = this.getOne(new LambdaQueryWrapper<AdPlanConf>()
                .eq(AdPlanConf::getId, confId)
                .eq(AdPlanConf::getUserId, userId)
                .eq(AdPlanConf::getDeleted, 0));

        if (existingConf == null) {
            log.warn("广告计划配置不存在或无权限，配置ID: {}, 用户ID: {}", confId, userId);
            return false;
        }

        existingConf.setDeleted(1);
        existingConf.setUpdateTime(LocalDateTime.now());
        boolean result = this.updateById(existingConf);

        log.info("删除广告计划配置成功，配置ID: {}", confId);
        return result;
    }

    @Override
    public Page<AdPlanConf> getUserAdPlanConfs(Long userId, Integer pageNum, Integer pageSize, boolean isMy) {
        Page<AdPlanConf> page = new Page<>(pageNum, pageSize);

        LambdaQueryWrapper<AdPlanConf> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AdPlanConf::getDeleted, 0)
                .eq(isMy, AdPlanConf::getUserId, userId)
                .orderByDesc(AdPlanConf::getCreateTime);

        return this.page(page, queryWrapper);
    }

    @Override
    public AdPlanConf getAdPlanConfById(Long confId, Long userId) {
        return this.getOne(new LambdaQueryWrapper<AdPlanConf>()
                .eq(AdPlanConf::getId, confId)
                .eq(AdPlanConf::getDeleted, 0));
    }

    @Override
    public void savePlanAndCreative(List<AdPlan> plans) {
        // 实现生成计划的逻辑
        log.info("生成计划，计划列表: {}", plans);
        for (AdPlan plan : plans) {
            adPlanService.save(plan);
            Long planId = plan.getId();
            plan.getAdCreatives().forEach(adPlanCreative -> {
                adPlanCreative.setAdPlanId(planId);
                adPlanCreative.setAdPlanConfId(plan.getAdPlanConfId());
                adPlanCreative.setAccountId(plan.getAccountId());
            });
            adPlanCreativeService.saveBatch(plan.getAdCreatives());
        }
    }

    @Async
    @Override
    public void asyncReGeneratePlan(Long confId) {
        log.info("异步重新生成计划开始，配置ID: {}", confId);
        // 根据配置ID获取计划列表
        AdPlanConf conf = this.getById(confId);
        List<AdPlan> plans = adPlanService.getPlansByConfId(confId);
        // 替换最新更改
        plans.forEach(plan -> {
            plan.setConversionId(conf.getConversionId());
            plan.setWxCorpid(conf.getWxCorpid());
            plan.setBidAmount(conf.getBidAmount());
            plan.setDeepConversionWorthRate(conf.getDeepConversionWorthRate());
        });
        adPlanService.updateBatchById(plans);
        List<AdPlanCreative> creatives = adPlanCreativeService.getCreativesByConfId(confId);
        creatives.forEach(creative -> {
            creative.setBrandName(conf.getBrandName());
            creative.setCreativeText(conf.getCreativeText());
            creative.setButtonText(conf.getButtonText());
        });
        // group by planId
        Map<Long, List<AdPlanCreative>> creativeMap = creatives.stream()
                .collect(Collectors.groupingBy(AdPlanCreative::getAdPlanId));
        for (AdPlan plan : plans) {
            plan.setAdCreatives(creativeMap.getOrDefault(plan.getId(), new ArrayList<AdPlanCreative>()));
        }
        asyncTencentInterface(conf, plans);
    }

    @Async
    @Override
    public void asyncTencentInterface(AdPlanConf conf, List<AdPlan> plans) {
        if (plans == null || plans.isEmpty()) {
            log.warn("计划列表为空，无需处理");
            return;
        }


        int planCount = plans.size();
        conf.setPlanCount(planCount);
        int creativeCount = plans.stream().mapToInt(p -> p.getAdCreatives() == null ? 0 : p.getAdCreatives().size()).sum();
        conf.setCreativeCount(creativeCount);

        log.info("异步对接腾讯接口开始，计划数量: {} 创意数: {}", planCount, creativeCount);

        Long confId = conf.getId();
        String jobNumber = conf.getJobNumber();

        // 创建 TencentAds 实例，整个流程复用同一个实例
        TencentAds tencentAds;
        try {
            tencentAds = clientFactory.getTencentAds(jobNumber, Constant.MAIN_ORG_ACCOUNT_ID);
        } catch (Exception e) {
            log.error("创建TencentAds实例失败，confId={}", confId, e);
            conf.setStatus(AdPlanStatus.FAILED);
            conf.setFailReason("创建TencentAds实例失败: " + StrUtil.truncateError(e.getMessage()));
            this.updateById(conf);
            return;
        }

        // 第一步：准备阶段 - 填充创意的 imageId/videoId
        try {
            prepareCreativeAssets(conf, tencentAds, plans);
            this.lambdaUpdate()
                .eq(AdPlanConf::getId, confId)
                .set(AdPlanConf::getFailReason, "填充创意资产完毕")
                .update();
        } catch (Exception e) {
            log.error("准备创意资产失败，confId={}", confId, e);
            // 更新 AdPlanConf 状态为失败
            this.lambdaUpdate()
                .eq(AdPlanConf::getId, confId)
                .set(AdPlanConf::getStatus, AdPlanStatus.FAILED)
                .set(AdPlanConf::getFailReason, "准备创意资产失败: " + StrUtil.truncateError(e.getMessage()))
                .update();
            return;
        }

        // 第二步：创建广告组与创意（使用虚拟线程并行处理，Semaphore限制最大并发数为10）
        Semaphore semaphore = new Semaphore(10);
        CountDownLatch latch = new CountDownLatch(plans.size());
        ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
        
        for (AdPlan plan : plans) {
            executor.submit(() -> {
                try {
                    semaphore.acquire();
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    latch.countDown();
                    return;
                }
                
                // 每个虚拟线程创建独立的 TencentAds 实例
                TencentAds threadTencentAds;
                try {
                    threadTencentAds = clientFactory.getTencentAds(jobNumber, Constant.MAIN_ORG_ACCOUNT_ID);
                } catch (Exception e) {
                    log.error("虚拟线程创建TencentAds实例失败，planId={}", plan.getId(), e);
                    plan.setStatus(AdPlanStatus.FAILED);
                    plan.setFailReason("创建TencentAds实例失败: " + StrUtil.truncateError(e.getMessage()));
                    adPlanService.updateById(plan);
                    semaphore.release();
                    latch.countDown();
                    return;
                }
                
                try {
                    log.info("开始处理广告计划，planId={}, adgroupName={}", plan.getId(), plan.getAdgroupName());

                    if (plan.getAdgroupId() > 0 || plan.getStatus() == AdPlanStatus.SUCCESS) {
                        // 创建动态创意
                        createDynamicCreatives(threadTencentAds, plan);
                        plan.setFailReason("");
                        plan.setStatus(AdPlanStatus.SUCCESS);
                        plan.setSuccessTime(LocalDateTime.now());
                        adPlanService.updateById(plan);
                        return;
                    }

                    // 更新状态为执行中
                    plan.setStatus(AdPlanStatus.IN_PROGRESS);
                    adPlanService.updateById(plan);

                    // 根据 adType 构建请求
                    AdgroupsAddRequest request = buildAdgroupRequest(plan);
                    log.info("请求参数: {}", JSON.toJSON(request));
                    // 创建广告组
                    log.info("调用腾讯API创建广告组，accountId={}, orgAccountId={}", plan.getAccountId(),
                            Constant.MAIN_ORG_ACCOUNT_ID);
                    AdgroupsAddResponseData response = tencentAdsAdgroupService.createAdgroup(
                            threadTencentAds, request);

                    // 更新 adgroupId
                    plan.setAdgroupId(response.getAdgroupId());
                    log.info("广告组创建成功，adgroupId={}", response.getAdgroupId());

                    // 更新状态为成功
                    plan.setStatus(AdPlanStatus.SUCCESS);
                    plan.setFailReason("");
                    plan.setCreateTime(LocalDateTime.now());
                    plan.setSuccessTime(LocalDateTime.now());
                    adPlanService.updateById(plan);

                    // 创建动态创意
                    if (plan.getAdgroupId() > 0) {
                        createDynamicCreatives(threadTencentAds, plan);
                    }

                    log.info("广告计划处理成功，planId={}, adgroupId={}", plan.getId(), plan.getAdgroupId());

                } catch (TencentAdsResponseException e) {
                    // 记录失败
                    log.error("创建广告失败, planId={} code={} message={} messageCn={}", plan.getId(), e.getCode(),
                            e.getMessage(), e.getMessageCn());
                    plan.setStatus(AdPlanStatus.FAILED);
                    plan.setFailReason(StrUtil.truncateError(e.getMessageCn()));
                    adPlanService.updateById(plan);
                } catch (Exception e) {
                    log.error("创建广告失败, planId={}", plan.getId(), e);
                    plan.setStatus(AdPlanStatus.FAILED);
                    plan.setFailReason(StrUtil.truncateError(e.getMessage()));
                    adPlanService.updateById(plan);
                } finally {
                    semaphore.release();
                    latch.countDown();
                }
            });
        }
        
        // 等待所有虚拟线程任务完成
        try {
            latch.await();
        } catch (InterruptedException e) {
            log.error("等待虚拟线程任务完成被中断，confId={}", confId, e);
            Thread.currentThread().interrupt();
        } finally {
            executor.shutdown();
        }

        // 更新 AdPlanConf 状态
        updateAdPlanConfStatus(conf, plans);

        log.info("异步对接腾讯接口完成，confId={}", confId);
    }

    private void prepareCreativeAssets(AdPlanConf conf, TencentAds tencentAds, List<AdPlan> plans) throws Exception {
        log.info("准备创意资产开始，计划数量: {}", plans.size());
        String brandImageFile = conf.getBrandImageFile();
        OssFile brandFile = JSON.parseObject(brandImageFile, OssFile.class);

        // 第一步：收集所有 accountId
        Set<Long> accountIds = plans.stream()
        .flatMap(p -> p.getAdCreatives().stream())
        .filter(c -> c.getBrandImageId() == 0)
                .map(AdPlanCreative::getAccountId)
                .collect(Collectors.toSet());
        log.info("收集到 {} 个账户ID", accountIds.size());

        // 第二步：处理 brandFile（品牌形象图片）
        Map<Long, Map<String, Long>> brandImageIdMap = new HashMap<>();
        if (brandFile != null && brandFile.getFileMd5() != null && brandFile.getFileUrl() != null) {
            String brandFileMd5 = brandFile.getFileMd5();
            String brandFileUrl = brandFile.getFileUrl();
            // 批量查询已上传的品牌形象记录
            List<TencentAccountIdBrandImage> existingBrandImages = tencentAccountIdBrandImageService
                    .batchGetByAccountIdsAndMd5s(new ArrayList<>(accountIds), List.of(brandFileMd5));
            for (TencentAccountIdBrandImage image : existingBrandImages) {
                brandImageIdMap.computeIfAbsent(image.getAccountId(), k -> new HashMap<>())
                        .put(image.getFileMd5(), image.getImageId());
            }
            log.info("从数据库查询到 {} 条已上传品牌形象记录", existingBrandImages.size());
            // 处理未上传的品牌形象
            List<Long> needUploadAccountIds = accountIds.stream()
                    .filter(accId -> !brandImageIdMap.getOrDefault(accId, Collections.emptyMap()).containsKey(brandFileMd5))
                    .toList();
            if (!needUploadAccountIds.isEmpty()) {
                log.info("品牌形象需要上传到 {} 个账户", needUploadAccountIds.size());
                // 获取品牌名称（从 conf 或创意中获取）
                String brandName = conf.getBrandName();
                for (Long accountId : needUploadAccountIds) {
                    TencentAssetService.BrandUploadResult brandResult = tencentAssetService.uploadBrandImageFromUrl(tencentAds, accountId, brandFileUrl, brandFileMd5, brandName);
                    tencentAccountIdBrandImageService.saveBrandImageId(accountId, brandFileMd5, brandResult.imageId(), brandFileUrl, brandResult.width(), brandResult.height());
                    brandImageIdMap.computeIfAbsent(accountId, k -> new HashMap<>()).put(brandFileMd5, brandResult.imageId());
                    log.info("品牌形象上传成功，accountId={}, fileMd5={}, brandImageId={}", accountId, brandFileMd5, brandResult.imageId());
                }
            }
        }

        // 第三步 给 plan.getAdCreatives() 下的创意填充 brandImageId
        for (AdPlan plan : plans) {
            Long accountId = plan.getAccountId();
            List<AdPlanCreative> creatives = plan.getAdCreatives();
            if (creatives == null) continue;

            for (AdPlanCreative creative : creatives) {
                if (creative.getBrandImageId() > 0) {
                    continue;
                }
                // 填充品牌形象的 brandImageId
                if (brandFile != null && brandFile.getFileMd5() != null) {
                    Long brandImageId = brandImageIdMap.getOrDefault(accountId, Collections.emptyMap()).get(brandFile.getFileMd5());
                    if (brandImageId != null) {
                        creative.setBrandImageId(brandImageId);
                    }
                }
            }
            adPlanCreativeService.updateBatchById(creatives);
        }

        log.info("准备创意资产完成");
    }


    /**
     * 构建广告组请求
     */
    private AdgroupsAddRequest buildAdgroupRequest(AdPlan plan) {
        if (plan.getAdType() == 1) {
            // 企微广告
            return AdgroupsAddReqPool.getAdgroupsAddRequest(
                    plan.getAccountId(),
                    plan.getAdgroupName(),
                    plan.getWxCorpid(),
                    calculateBeginDate(),
                    "",
                    parseTargetConfId(plan.getTarget()),
                    parseSiteConfId(plan.getSite()),
                    plan.getBidAmount(),
                    plan.getConversionId());
        } else {
            // ROI广告
            return AdgroupsAddReqPool.getROIAdgroupsAddRequest(
                    plan.getAccountId(),
                    plan.getAdgroupName(),
                    plan.getAssetType() == 1 ? plan.getDramaId() : plan.getFictionId(),
                    calculateBeginDate(),
                    "",
                    parseTargetConfId(plan.getTarget()),
                    parseSiteConfId(plan.getSite()),
                    plan.getBidAmount(),
                    plan.getConversionId(),
                    plan.getDeepConversionWorthRate());
        }
    }

    /**
     * 创建动态创意
     */
    private void createDynamicCreatives(TencentAds tencentAds, AdPlan plan)
            throws TencentAdsResponseException, Exception {

        for (AdPlanCreative creative : plan.getAdCreatives()) {
            try {
                log.info("开始创建动态创意，planId={}, creativeName={}", plan.getId(), creative.getCreativeName());

                if (creative.getCreativeId() > 0 || creative.getStatus() == AdPlanStatus.SUCCESS)
                    continue;

                // 判定creative的imageId或者videoId没有值或者=0，直接判定为失败
                if (creative.getImageId() == 0 && creative.getVideoId() == 0) {
                    creative.setStatus(AdPlanStatus.FAILED);
                    creative.setFailReason("创意图片或视频未上传成功");
                    adPlanCreativeService.updateById(creative);
                    continue;
                }
                

                // 更新状态为执行中
                creative.setStatus(AdPlanStatus.IN_PROGRESS);
                creative.setAdgroupId(plan.getAdgroupId());
                adPlanCreativeService.updateById(creative);

                // 构建动态创意请求
                DynamicCreativesAddRequest request = buildDynamicCreativeRequest(plan, creative);

                // 调用API创建
                DynamicCreativesAddResponseData response = tencentAds.dynamicCreatives().dynamicCreativesAdd(request);

                // 更新创意ID和状态
                creative.setCreativeId(response.getDynamicCreativeId());
                creative.setStatus(AdPlanStatus.SUCCESS);
                creative.setFailReason("");
                creative.setCreateTime(LocalDateTime.now());
                creative.setSuccessTime(LocalDateTime.now());
                adPlanCreativeService.updateById(creative);

                log.info("动态创意创建成功，creativeId={}", response.getDynamicCreativeId());

            } catch (TencentAdsResponseException e) {
                log.error("创建动态创意失败, creativeId={}", creative.getId(), e);
                creative.setStatus(AdPlanStatus.FAILED);
                creative.setFailReason(StrUtil.truncateError(e.getMessage()));
                adPlanCreativeService.updateById(creative);
            }
        }
    }

    /**
     * 构建动态创意请求
     */
    private DynamicCreativesAddRequest buildDynamicCreativeRequest(AdPlan plan, AdPlanCreative creative) {
        DynamicCreativesAddRequest request = new DynamicCreativesAddRequest();
        request.setAccountId(plan.getAccountId());
        request.setAdgroupId(plan.getAdgroupId());
        request.setDynamicCreativeName(creative.getCreativeName());
        request.setDeliveryMode(DeliveryMode.COMPONENT);
        request.setDynamicCreativeType(DynamicCreativeType.PROGRAM);

        CreativeComponents components = new CreativeComponents();

        // 标题组件 (固定标题)
        components.setTitle(buildTitleComponents(creative.getCreativeText()));

        // 文案组件 (固定文案)
        components.setDescription(buildDescriptionComponents(creative.getCreativeText()));

        // 图片组件 (从 creative.imageId)
        if (creative.getImageId() > 0) {
             components.setImage(buildImageComponents(creative.getImageId()));
        }

        if (creative.getVideoId() > 0) {
             components.setVideo(buildVideoComponents(creative.getVideoId()));
        }


        // 品牌形象 (固定品牌)
        components.setBrand(buildBrandComponents(creative.getBrandImageId(), creative.getBrandName()));

        // 落地页 (从 creative.pageIds, 逗号分隔)
        components.setMainJumpInfo(buildJumpInfoComponents(creative.getPageIds()));

        // 行动按钮 (数量与落地页一致, 文案统一)
        components.setActionButton(buildActionButtonComponents(creative.getPageIds(), creative.getButtonText()));

        request.setCreativeComponents(components);
        return request;
    }

    private List<VideoComponent> buildVideoComponents(Long videoId) {
        List<VideoComponent> videos = new ArrayList<>();
        VideoComponent video = new VideoComponent();
        VideoStruct videoValue = new VideoStruct();
        videoValue.setVideoId(String.valueOf(videoId));
        video.setValue(videoValue);
        videos.add(video);
        return videos;
    }

    /**
     * 构建标题组件
     */
    private List<TitleComponent> buildTitleComponents(String creativeText) {
        List<TitleComponent> titles = new ArrayList<>();

        TitleComponent title1 = new TitleComponent();
        TitleStruct titleValue1 = new TitleStruct();
        titleValue1.setContent(creativeText);
        title1.setValue(titleValue1);
        titles.add(title1);

        return titles;
    }

    /**
     * 构建文案组件
     */
    private List<DescriptionComponent> buildDescriptionComponents(String creativeText) {
        List<DescriptionComponent> descriptions = new ArrayList<>();

        DescriptionComponent desc1 = new DescriptionComponent();
        DescriptionStruct descValue1 = new DescriptionStruct();
        descValue1.setContent(creativeText);
        desc1.setValue(descValue1);
        descriptions.add(desc1);

        return descriptions;
    }

    /**
     * 构建图片组件
     */
    private List<ImageComponent> buildImageComponents(Long imageId) {
        List<ImageComponent> images = new ArrayList<>();
        ImageComponent image = new ImageComponent();
        ImageStruct imageValue = new ImageStruct();
        imageValue.setImageId(String.valueOf(imageId));
        image.setValue(imageValue);
        images.add(image);

        return images;
    }

    /**
     * 构建品牌组件
     */
    private List<BrandComponent> buildBrandComponents(Long brandImageId, String brandName) {
        List<BrandComponent> brands = new ArrayList<>();

        BrandComponent brand = new BrandComponent();
        BrandStruct brandValue = new BrandStruct();
        brandValue.setBrandName(brandName);
        brandValue.setBrandImageId(String.valueOf(brandImageId));
        brand.setValue(brandValue);
        brands.add(brand);

        return brands;
    }

    /**
     * 构建落地页组件
     */
    private List<JumpinfoComponent> buildJumpInfoComponents(String pageIds) {
        List<JumpinfoComponent> jumpInfoList = new ArrayList<>();

        if (pageIds == null || pageIds.isEmpty()) {
            return jumpInfoList;
        }

        String[] pageIdArray = pageIds.split(",");
        for (String pageIdStr : pageIdArray) {
            Long pageId = Long.parseLong(pageIdStr.trim());

            JumpinfoComponent jumpinfo = new JumpinfoComponent();
            JumpinfoStruct jumpinfoValue = new JumpinfoStruct();
            PageSpec pageSpec = new PageSpec();
            XjPageSpec xjPageSpec = new XjPageSpec();
            xjPageSpec.setPageId(pageId);
            pageSpec.setOfficialSpec(xjPageSpec);
            jumpinfoValue.setPageSpec(pageSpec);
            jumpinfoValue.setPageType(PageType.OFFICIAL);
            jumpinfo.setValue(jumpinfoValue);
            jumpInfoList.add(jumpinfo);
        }

        return jumpInfoList;
    }

    /**
     * 构建行动按钮组件
     */
    private List<ActionButtonComponent> buildActionButtonComponents(String pageIds, String buttonText) {
        List<ActionButtonComponent> actionButtons = new ArrayList<>();

        if (pageIds == null || pageIds.isEmpty()) {
            return actionButtons;
        }

        String[] pageIdArray = pageIds.split(",");
        for (int i = 0; i < pageIdArray.length; i++) {
            ActionButtonComponent actionButton = new ActionButtonComponent();
            ActionButtonStruct actionButtonValue = new ActionButtonStruct();
            actionButtonValue.setButtonText(buttonText);
            actionButton.setValue(actionButtonValue);
            actionButtons.add(actionButton);
        }

        return actionButtons;
    }

    /**
     * 更新 AdPlanConf 状态
     */
    private void updateAdPlanConfStatus(AdPlanConf conf, List<AdPlan> plans) {
        // 统计结果
        int totalCount = plans.size();
        int successCount = (int) plans.stream().filter(p -> AdPlanStatus.SUCCESS.equals(p.getStatus())).count();
        int failCount = totalCount - successCount;
        int creativeSuccessCount = (int) plans.stream().filter(p -> p.getAdCreatives() != null).flatMap(p -> p.getAdCreatives().stream()).filter(c -> c.getStatus().equals(AdPlanStatus.SUCCESS)).count();

        Long confId = conf.getId();
        conf.setPlanSuccessCount(successCount);
        conf.setCreativeSuccessCount(creativeSuccessCount);

        if (failCount == 0) {
            conf.setStatus(AdPlanStatus.SUCCESS); // 全部成功
            conf.setSuccessTime(LocalDateTime.now());
            conf.setUpdateTime(LocalDateTime.now());
            conf.setFailReason("");
        } else if (successCount == 0) {
            conf.setStatus(AdPlanStatus.FAILED); // 全部失败
            conf.setUpdateTime(LocalDateTime.now());
            conf.setFailReason("所有广告创建失败");
        } else {
            conf.setStatus(AdPlanStatus.PART_FAILED); // 部分失败
            conf.setSuccessTime(LocalDateTime.now());
            conf.setUpdateTime(LocalDateTime.now());
            conf.setFailReason(String.format("成功%d个,失败%d个", successCount, failCount));
        }

        this.updateById(conf);
        log.info("更新广告计划配置状态，confId={}, status={}, 成功:{}, 失败:{}",
                confId, conf.getStatus(), successCount, failCount);
    }

    /**
     * 计算开始日期 (明天)
     */
    private String calculateBeginDate() {
        return LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
    }

    /**
     * 解析定向配置ID
     */
    private Integer parseTargetConfId(String target) {
        if (target == null || target.isEmpty()) {
            return 1; // 默认值
        }
        try {
            return Integer.parseInt(target.trim());
        } catch (NumberFormatException e) {
            log.warn("解析定向配置ID失败，使用默认值1，target={}", target);
            return 1;
        }
    }

    /**
     * 解析站点配置ID
     */
    private Integer parseSiteConfId(String site) {
        if (site == null || site.isEmpty()) {
            return 1; // 默认值
        }
        try {
            return Integer.parseInt(site.trim());
        } catch (NumberFormatException e) {
            log.warn("解析站点配置ID失败，使用默认值1，site={}", site);
            return 1;
        }
    }


    @Override
    public void gdtAdgroupPatrolDelete(AdPlanConf conf) {
        try {
            List<AdPlan> plans = adPlanService.lambdaQuery()
                    .eq(AdPlan::getAdPlanConfId, conf.getId())
                    .gt(AdPlan::getAdgroupId, 0)
                    .ne(AdPlan::getSystemDelete, "true")
                    .orderByAsc(AdPlan::getId)
                    .list();
            if (plans.isEmpty()) {
                log.info("gdtAdgroupPatrolDelete 无可巡检广告组 confId={}", conf.getId());
                return;
            }

            Map<Long, AdPlan> planMap = plans.stream()
                    .filter(plan -> plan.getAdgroupId() != null)
                    .collect(Collectors.toMap(AdPlan::getAdgroupId, plan -> plan, (left, right) -> left));

            // 3天前连续5天消费为0
            LocalDate endDate = LocalDate.now().minusDays(3);
            LocalDate startDate = endDate.minusDays(5);

            List<ReqDailyAdgroupReport> reports = reqDailyAdgroupReportService.lambdaQuery()
                    .in(ReqDailyAdgroupReport::getAdgroupId, planMap.keySet())
                    .between(ReqDailyAdgroupReport::getDate, startDate, endDate)
                    .list();

            Map<Long, Long> validCostSumMap = reports.stream()
                    .collect(Collectors.groupingBy(
                            ReqDailyAdgroupReport::getAdgroupId,
                            Collectors.summingLong(report -> report.getCost() == null ? 0L : report.getCost())));

            List<AdPlan> deletePlans = planMap.entrySet().stream()
                    .filter(entry -> validCostSumMap.getOrDefault(entry.getKey(), 0L) == 0L)
                    .map(Map.Entry::getValue)
                    .toList();

            if (deletePlans.isEmpty()) {
                log.info("gdtAdgroupPatrolDelete 无需删除广告组 confId={} [{}, {}]", conf.getId(), startDate, endDate);
                return;
            }

            TencentAds tencentAds = clientFactory.getTencentAds(Constant.JOB_NUMBER, Constant.MAIN_ORG_ACCOUNT_ID);
            doDeletePlans(tencentAds, deletePlans);
            lambdaUpdate().eq(AdPlanConf::getId, conf.getId())
                    .set(AdPlanConf::getFailReason, "广告巡检删除" + deletePlans.size() + "个广告")
                    .update();
            log.info("gdtAdgroupPatrolDelete 删除广告组完成 confId={} deleteCount={} [{}, {}]",
                    conf.getId(), deletePlans.size(), startDate, endDate);
        } catch (Exception e) {
            log.error("gdtAdgroupPatrolDelete error confId={}", conf.getId(), e);
            lambdaUpdate()
            .eq(AdPlanConf::getId, conf.getId())
                    .set(AdPlanConf::getFailReason, e.getMessage())
                    .update();
        }
    }
}
