package com.moka.gdtauto.service.impl;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

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

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.Constant;
import com.moka.gdtauto.entity.AdPlan;
import com.moka.gdtauto.entity.AdPlanConf;
import com.moka.gdtauto.mapper.AdPlanMapper;
import com.moka.gdtauto.service.AdPlanService;
import com.moka.gdtauto.util.StrUtil;
import com.tencent.ads.model.v3.AdgroupsGetResponseData;
import com.tencent.ads.model.v3.AdgroupsUpdateRequest;
import com.tencent.ads.model.v3.FilterOperator;
import com.tencent.ads.model.v3.FilteringStruct;
import com.tencent.ads.model.v3.SmartTargetingMode;
import com.tencent.ads.model.v3.WriteTargetingSetting;
import com.tencent.ads.v3.TencentAds;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * 广告计划服务实现类
 * 
 * @author moka
 * @since 2026-02-24
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class AdPlanServiceImpl extends ServiceImpl<AdPlanMapper, AdPlan> implements AdPlanService {
    private final AdPlanMapper adPlanMapper;
    private final TencentAdsApiClientFactory clientFactory;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createAdPlan(AdPlan adPlan) {
        adPlan.setCreateTime(LocalDateTime.now());
        adPlan.setUpdateTime(LocalDateTime.now());
        adPlan.setDeleted(0);
        if (adPlan.getStatus() == null) {
            adPlan.setStatus(1); // 默认待执行
        }
        
        this.save(adPlan);
        log.info("创建广告计划成功，计划ID: {}, 广告组名称: {}", adPlan.getId(), adPlan.getAdgroupName());
        
        return adPlan.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateAdPlan(AdPlan adPlan) {
        AdPlan existingPlan = this.getById(adPlan.getId());
        
        if (existingPlan == null || existingPlan.getDeleted() == 1) {
            log.warn("广告计划不存在，计划ID: {}", adPlan.getId());
            return false;
        }
        
        adPlan.setUpdateTime(LocalDateTime.now());
        boolean result = this.updateById(adPlan);
        log.info("更新广告计划成功，计划ID: {}", adPlan.getId());
        
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteAdPlan(Long planId) {
        AdPlan existingPlan = this.getById(planId);
        
        if (existingPlan == null || existingPlan.getDeleted() == 1) {
            log.warn("广告计划不存在，计划ID: {}", planId);
            return false;
        }
        
        existingPlan.setDeleted(1);
        existingPlan.setUpdateTime(LocalDateTime.now());
        boolean result = this.updateById(existingPlan);
        
        log.info("删除广告计划成功，计划ID: {}", planId);
        return result;
    }

    @Override
    public Page<AdPlan> getAdPlans(Long confId, Long accountId, Long adgroupId, Integer status, Integer pageNum, Integer pageSize) {
        Page<AdPlan> page = new Page<>(pageNum, pageSize);
        
        LambdaQueryWrapper<AdPlan> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AdPlan::getDeleted, 0);
        if (confId != null) {
            queryWrapper.eq(AdPlan::getAdPlanConfId, confId);
        }
        
        if (accountId != null) {
            queryWrapper.eq(AdPlan::getAccountId, accountId);
        }
        if (adgroupId != null) {
            queryWrapper.eq(AdPlan::getAdgroupId, adgroupId);
        }
        if (status != null) {
            queryWrapper.eq(AdPlan::getStatus, status);
        }
        
        queryWrapper.orderByDesc(AdPlan::getId);
        
        return this.page(page, queryWrapper);
    }

    @Override
    public AdPlan getAdPlanById(Long planId) {
        return this.getOne(new LambdaQueryWrapper<AdPlan>()
                .eq(AdPlan::getId, planId)
                .eq(AdPlan::getDeleted, 0));
    }

    @Override
    public List<AdPlan> getPlansByConfId(Long confId) {
        return this.list(new LambdaQueryWrapper<AdPlan>()
                .eq(AdPlan::getAdPlanConfId, confId)
                .eq(AdPlan::getDeleted, 0).orderByAsc(AdPlan::getId));
    }



 
    @Async
    @Override
    public void syncAdPlanStatus(Long confId) {
        final int PAGE_SIZE = 500;
        final int BATCH_LIMIT = 100; // 腾讯广告 adgroup_id IN 最大 100

        log.info("[定时任务] 开始同步广告计划 system_status");
        int totalUpdated = 0;
        // 获取 TencentAds 实例（使用主 orgAccountId）
        try {
            TencentAds tencentAds = clientFactory.getTencentAds(null, Constant.MAIN_ORG_ACCOUNT_ID);
            int pageNum = 1;
            while (true) {
                // 1. 分页查询 status=4 的广告计划，ID 降序
                LambdaQueryWrapper<AdPlan> queryWrapper = new LambdaQueryWrapper<>();
                queryWrapper.eq(confId != null, AdPlan::getAdPlanConfId, confId);
                queryWrapper.eq(AdPlan::getDeleted, 0);
                queryWrapper.eq(AdPlan::getStatus, 4);
                queryWrapper.orderByDesc(AdPlan::getId);
                Page<AdPlan> page = this.page(new Page<>(pageNum, PAGE_SIZE), queryWrapper);
                List<AdPlan> records = page.getRecords();
                if (records == null || records.isEmpty()) {
                    break;
                }

                // 2. 按 accountId 分组
                Map<Long, List<AdPlan>> groupByAccount = records.stream()
                        .filter(p -> p.getAdgroupId() != null && p.getAccountId() != null)
                        .collect(Collectors.groupingBy(AdPlan::getAccountId));

                for (Map.Entry<Long, List<AdPlan>> entry : groupByAccount.entrySet()) {
                    Long accountId = entry.getKey();
                    List<AdPlan> plansOfAccount = entry.getValue();

                    // 3. 每组按 100 条分批调用 API
                    int batchCount = (int) Math.ceil((double) plansOfAccount.size() / BATCH_LIMIT);
                    for (int i = 0; i < batchCount; i++) {
                        int fromIdx = i * BATCH_LIMIT;
                        int toIdx = Math.min(fromIdx + BATCH_LIMIT, plansOfAccount.size());
                        List<AdPlan> batch = plansOfAccount.subList(fromIdx, toIdx);

                        try {
                            int updated = syncBatch(tencentAds, accountId, batch);
                            totalUpdated += updated;
                        } catch (Exception e) {
                            log.error("[定时任务] 同步 accountId={} 的广告状态失败，batch index={}", accountId, i, e);
                        }
                    }
                }

                if (pageNum >= page.getPages()) {
                    break;
                }
                pageNum++;
            }

        } catch (Exception e) {
            log.error("[定时任务] 同步广告计划 system_status 发生异常", e);
        }

        log.info("[定时任务] 同步广告计划 system_status 完成，共更新 {} 条", totalUpdated);
    }

    /**
     * 针对同一 accountId 下的一批广告计划，调用 API 获取 system_status 并批量更新
     *
     * @param accountId 广告账户 ID
     * @param batch     该账户下的广告计划列表（不超过 100 条）
     * @return 实际更新条数
     */
    private int syncBatch(TencentAds tencentAds, Long accountId, List<AdPlan> batch) throws Exception {
        final int BATCH_LIMIT = 100; // 腾讯广告 adgroup_id IN 最大 100
        final List<String> FIELDS = List.of("adgroup_id", "created_time", "configured_status", "system_status", "is_deleted");

        // 构建 adgroup_id IN [...] 过滤条件
        List<String> adgroupIdValues = batch.stream()
                .map(p -> String.valueOf(p.getAdgroupId()))
                .collect(Collectors.toList());

        FilteringStruct filter = new FilteringStruct();
        filter.setField("adgroup_id");
        filter.setOperator(FilterOperator.IN);
        filter.setValues(adgroupIdValues);

        List<FilteringStruct> filtering = new ArrayList<>();
        filtering.add(filter);



        AdgroupsGetResponseData response = tencentAds.adgroups().adgroupsGet(
                accountId,
                filtering,
                1L,                        // page
                (long) BATCH_LIMIT,
                null,              // is_deleted
                FIELDS,
                "PAGINATION_MODE_NORMAL",
                null                         // cursor（首次请求）
        );

        if (response == null || response.getList() == null || response.getList().isEmpty()) {
            log.warn("[定时任务] accountId={} 未返回广告状态数据", accountId);
            return 0;
        }

        // 构建待更新列表
        List<AdPlan> toUpdate = new ArrayList<>();
        response.getList().stream().forEach(item -> {
            AdPlan update = new AdPlan();
            // item.getCreatedTime() 秒
            update.setCreateTime(LocalDateTime.ofEpochSecond(item.getCreatedTime(), 0, java.time.ZoneOffset.UTC));
            update.setAdgroupId(item.getAdgroupId());
            update.setConfiguredStatus(item.getConfiguredStatus().getValue());
            update.setSystemStatus(item.getSystemStatus().getValue());
            update.setSystemDelete(item.isIsDeleted() != null ? String.valueOf(item.isIsDeleted()) : null);
            toUpdate.add(update);
        });

        if (toUpdate.isEmpty()) {
            return 0;
        }

        adPlanMapper.batchUpdateSystemStatus(toUpdate);
        log.info("[定时任务] accountId={} 批量更新 {} 条 system_status", accountId, toUpdate.size());
        return toUpdate.size();
    }

    @Override
    public void updateAdgroupDailyBudget(AdPlanConf conf, AdPlan plan, Long dailyBudget) throws Exception {
       try {
        TencentAds tencentAds = clientFactory.getTencentAds(conf.getJobNumber(), Constant.MAIN_ORG_ACCOUNT_ID);
        AdgroupsUpdateRequest req = new AdgroupsUpdateRequest();
        req.setAccountId(plan.getAccountId());
        req.setAdgroupId(plan.getAdgroupId());
        req.setDailyBudget(dailyBudget);
        req.setSmartTargetingMode(SmartTargetingMode.SMART_TARGETING_MANUAL);
        tencentAds.adgroups().adgroupsUpdate(req);
        plan.setDailyBudget(dailyBudget);
        plan.setFailReason("设置日预算成功");
       } catch (Exception e) {
           log.error("[定时任务] 修改广告日预算失败，计划ID: {}", plan.getId(), e);
           plan.setFailReason("设置日预算失败：" + StrUtil.truncateError(e.getMessage()));
       }
       this.lambdaUpdate()
       .eq(AdPlan::getId, plan.getId())
       .set(AdPlan::getDailyBudget, plan.getDailyBudget())
       .set(AdPlan::getFailReason, plan.getFailReason())
       .update();
    }

    @Override
    public void updateAdgroupTargeting(TencentAds tencentAds,AdPlanConf conf, AdPlan plan, WriteTargetingSetting targeting) throws Exception {
        try {
        AdgroupsUpdateRequest req = new AdgroupsUpdateRequest();
        req.setAccountId(plan.getAccountId());
        req.setAdgroupId(plan.getAdgroupId());
        req.setTargeting(targeting);
        req.setSmartTargetingMode(SmartTargetingMode.SMART_TARGETING_MANUAL);
        tencentAds.adgroups().adgroupsUpdate(req);
        plan.setFailReason("修改定向模版成功");
       } catch (Exception e) {
           log.error("[定时任务] 修改广告定向模版失败，ID: {}，计划ID: {}",plan.getId(), plan.getAdgroupId(), e);
           plan.setFailReason("修改定向模版失败：" + StrUtil.truncateError(e.getMessage()));
       }
       this.lambdaUpdate()
       .eq(AdPlan::getId, plan.getId())
       .set(AdPlan::getFailReason, plan.getFailReason())
       .update();
    }
}
