package com.moka.gdtauto.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.moka.gdtauto.client.TencentAdsApiClientFactory;
import com.moka.gdtauto.entity.AccountGroup;
import com.moka.gdtauto.entity.AccountGroupAccountRelationship;
import com.moka.gdtauto.entity.BaseDailyCreativeReport;
import com.moka.gdtauto.entity.RepDailyCreativeReport;
import com.moka.gdtauto.entity.ReqDailyCreativeReport;
import com.moka.gdtauto.mapper.AccountGroupAccountRelationshipMapper;
import com.moka.gdtauto.mapper.AccountGroupMapper;
import com.moka.gdtauto.mapper.RepDailyCreativeReportMapper;
import com.moka.gdtauto.mapper.ReqDailyCreativeReportMapper;
import com.tencent.ads.model.v3.DailyReportsGetResponseData;
import com.tencent.ads.model.v3.ReportDateRange;
import com.tencent.ads.model.v3.ReportStruct;
import com.tencent.ads.model.v3.TimeLine;
import com.tencent.ads.v3.TencentAds;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.Arrays;
import java.util.List;

/**
 * 动态创意日报表 Service
 * 支持双口径（REQUEST_TIME / REPORTING_TIME）同步与查询
 *
 * @author moka
 * @since 2026-03-03
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class DailyReportService {

    private final TencentAdsApiClientFactory clientFactory;
    private final TencentAccountService accountService;
    private final AccountGroupAccountRelationshipMapper relationshipMapper;
    private final AccountGroupMapper accountGroupMapper;
    private final ReqDailyCreativeReportMapper reqMapper;
    private final RepDailyCreativeReportMapper repMapper;

    /** 同步时请求的指标字段 */
    private static final List<String> SYNC_FIELDS = Arrays.asList(
            "dynamic_creative_id", "date",
            // 曝光点击
            "view_count", "view_user_count", "avg_view_per_user",
            "valid_click_count", "click_user_count", "cpc", "ctr",
            "valuable_click_count", "valuable_click_cost", "valuable_click_rate",
            // 花费
            "cost", "thousand_display_price",
            // 转化
            "conversions_count", "conversions_rate", "conversions_cost",
            "deep_conversions_count", "deep_conversions_rate", "deep_conversions_cost",
            "conversions_by_display_count", "conversions_by_click_count",
            "preview_conversions_count", "preview_deep_conversions_count",
            // 视频
            "video_outer_play_count", "video_outer_play_user_count", "avg_user_play_count",
            "video_outer_play_time_count", "video_outer_play_rate", "video_outer_play_cost",
            "video_outer_play3s_count", "video_outer_play3s_rate",
            "video_outer_play25_count", "video_outer_play50_count",
            "video_outer_play75_count", "video_outer_play100_count",
            // 互动
            "read_count", "comment_count", "praise_count", "forward_count",
            // 落地页
            "platform_page_view_count", "platform_page_view_rate",
            "lan_button_click_count", "lan_jump_button_clickers",
            // 关键页面
            "key_page_view_count", "key_page_uv",
            // ROI/LTV
            "income_val_1", "income_roi_1",
            "income_val_3", "income_roi_3",
            "income_val_7", "income_roi_7",
            "income_val_14", "income_roi_14",
            "income_val_24h", "income_roi_1_24h"
    );

    // ─── 同步方法 ─────────────────────────────────────────────────────────────

    /**
     * 同步指定账户的日报表（分页拉取直到全量完成）
     *
     * @param jobNumber    工号（用于获取 access_token）
     * @param orgAccountId 组织账户ID
     * @param accountId    广告账户ID
     * @param groupId      账号组ID
     * @param groupName    账号组名称
     * @param startDate    开始日期 yyyy-MM-dd
     * @param endDate      结束日期 yyyy-MM-dd
     * @param timeLine     时间口径 REQUEST_TIME / REPORTING_TIME
     * @return 成功写入行数
     */
    public int syncReport(String jobNumber, Long orgAccountId, Long accountId,
                          Long groupId, String groupName,
                          String startDate, String endDate, String timeLine) throws Exception {
        log.info("开始同步日报表 accountId={} timeLine={} [{}, {}]", accountId, timeLine, startDate, endDate);

        TencentAds tencentAds = clientFactory.getTencentAds(jobNumber, orgAccountId);

        ReportDateRange dateRange = new ReportDateRange();
        dateRange.setStartDate(startDate);
        dateRange.setEndDate(endDate);

        List<String> groupBy = Arrays.asList("dynamic_creative_id", "date");

        long page = 1L;
        long pageSize = 1000L;
        int totalSaved = 0;

        while (true) {
            DailyReportsGetResponseData response = tencentAds.dailyReports().dailyReportsGet(
                    "REPORT_LEVEL_DYNAMIC_CREATIVE",
                    dateRange,
                    groupBy,
                    SYNC_FIELDS,
                    accountId,
                    null,
                    null,
                    timeLine,
                    page,
                    pageSize,
                    null
            );

            if (response == null || response.getList() == null || response.getList().isEmpty()) {
                break;
            }

            List<ReportStruct> list = response.getList();
            for (ReportStruct rs : list) {
                if (rs.getDynamicCreativeId() == null || rs.getDate() == null) {
                    continue;
                }
                if (TimeLine.REQUEST_TIME.getValue().equals(timeLine)) {
                    ReqDailyCreativeReport entity = toReqEntity(accountId, groupId, groupName, rs);
                    reqMapper.upsert(entity);
                } else {
                    RepDailyCreativeReport entity = toRepEntity(accountId, groupId, groupName, rs);
                    repMapper.upsert(entity);
                }
                totalSaved++;
            }

            long totalNumber = response.getPageInfo() != null
                    ? response.getPageInfo().getTotalNumber() : 0L;
            if (page * pageSize >= totalNumber) {
                break;
            }
            page++;
        }

        log.info("日报表同步完成 accountId={} timeLine={} saved={}", accountId, timeLine, totalSaved);
        return totalSaved;
    }

    /**
     * 同步所有账户（定时任务调用）——通过 AccountGroupAccountRelationship 关联获取 groupId / groupName
     *
     * @param startDate 开始日期
     * @param endDate   结束日期
     * @param timeLine  时间口径
     * @return 成功保存总行数
     */
    public int syncAllAccounts(String startDate, String endDate, String timeLine) {
        // 查询所有账号组-账户关系
        // TODO for test
        LambdaQueryWrapper<AccountGroupAccountRelationship> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(AccountGroupAccountRelationship::getGroupId, 1L);
        List<AccountGroupAccountRelationship> relationships = relationshipMapper.selectList(null);
        if (relationships.isEmpty()) {
            log.warn("没有可同步的账户（AccountGroupAccountRelationship 为空）");
            return 0;
        }

        // 预载所有账号组信息（减少重复查询）
        java.util.Map<Long, AccountGroup> groupMap = new java.util.HashMap<>();
        accountGroupMapper.selectList(new LambdaQueryWrapper<AccountGroup>().eq(AccountGroup::getDeleted, 0))
                .forEach(g -> groupMap.put(g.getId(), g));

        log.info("开始批量同步日报表 relationships={} timeLine={} [{}, {}]",
                relationships.size(), timeLine, startDate, endDate);

        int total = 0;
        for (AccountGroupAccountRelationship rel : relationships) {
            Long groupId = rel.getGroupId();
            Long accountId = rel.getAccountId();
            AccountGroup group = groupMap.get(groupId);

            if (group == null) {
                log.warn("账号组信息不存在 groupId={}", groupId);
                continue;
            }
           
            try {
                int saved = syncReport(
                        "12458",
                        41644744L,
                        accountId,
                        groupId,
                        group.getGroupName(),
                        startDate, endDate, timeLine
                );
                total += saved;
            } catch (Exception e) {
                log.error("同步日报表失败 groupId={} accountId={}", groupId, accountId, e);
            }
        }
        log.info("批量同步日报表完成 total={}", total);
        return total;
    }

    // ─── 查询方法 ─────────────────────────────────────────────────────────────

    /**
     * 分页查询日报表
     *
     * @param timeLine  时间口径（决定查哪张表）
     * @param groupId   可选，账号组ID
     * @param accountId 可选，广告账户ID
     * @param startDate 可选，开始日期
     * @param endDate   可选，结束日期
     * @param pageNum   页码
     * @param pageSize  每页大小
     * @return 分页结果
     */
    public Page<? extends BaseDailyCreativeReport> listReports(
            String timeLine, Long groupId, Long accountId, Long dynamicCreativeId,
            String startDate, String endDate, long pageNum, long pageSize) {

        if ("REPORTING_TIME".equals(timeLine)) {
            LambdaQueryWrapper<RepDailyCreativeReport> wrapper = buildRepWrapper(groupId, accountId, dynamicCreativeId, startDate, endDate);
            return repMapper.selectPage(new Page<>(pageNum, pageSize), wrapper);
        } else {
            LambdaQueryWrapper<ReqDailyCreativeReport> wrapper = buildReqWrapper(groupId, accountId, dynamicCreativeId, startDate, endDate);
            return reqMapper.selectPage(new Page<>(pageNum, pageSize), wrapper);
        }
    }

    private LambdaQueryWrapper<ReqDailyCreativeReport> buildReqWrapper(
            Long groupId, Long accountId, Long dynamicCreativeId, String startDate, String endDate) {
        LambdaQueryWrapper<ReqDailyCreativeReport> wrapper = new LambdaQueryWrapper<>();
        if (groupId != null) {
            wrapper.eq(ReqDailyCreativeReport::getGroupId, groupId);
        }
        if (accountId != null) {
            wrapper.eq(ReqDailyCreativeReport::getAccountId, accountId);
        }
        if (dynamicCreativeId != null) {
            wrapper.eq(ReqDailyCreativeReport::getDynamicCreativeId, dynamicCreativeId);
        }
        if (StringUtils.hasText(startDate)) {
            wrapper.ge(ReqDailyCreativeReport::getDate, LocalDate.parse(startDate));
        }
        if (StringUtils.hasText(endDate)) {
            wrapper.le(ReqDailyCreativeReport::getDate, LocalDate.parse(endDate));
        }
        wrapper.orderByDesc(ReqDailyCreativeReport::getDate);
        return wrapper;
    }

    private LambdaQueryWrapper<RepDailyCreativeReport> buildRepWrapper(
            Long groupId, Long accountId, Long dynamicCreativeId, String startDate, String endDate) {
        LambdaQueryWrapper<RepDailyCreativeReport> wrapper = new LambdaQueryWrapper<>();
        if (groupId != null) {
            wrapper.eq(RepDailyCreativeReport::getGroupId, groupId);
        }
        if (accountId != null) {
            wrapper.eq(RepDailyCreativeReport::getAccountId, accountId);
        }
        if (dynamicCreativeId != null) {
            wrapper.eq(RepDailyCreativeReport::getDynamicCreativeId, dynamicCreativeId);
        }
        if (StringUtils.hasText(startDate)) {
            wrapper.ge(RepDailyCreativeReport::getDate, LocalDate.parse(startDate));
        }
        if (StringUtils.hasText(endDate)) {
            wrapper.le(RepDailyCreativeReport::getDate, LocalDate.parse(endDate));
        }
        wrapper.orderByDesc(RepDailyCreativeReport::getDate);
        return wrapper;
    }

    // ─── 映射方法 ─────────────────────────────────────────────────────────────

    private ReqDailyCreativeReport toReqEntity(Long accountId, Long groupId, String groupName, ReportStruct rs) {
        ReqDailyCreativeReport e = new ReqDailyCreativeReport();
        fillFields(e, accountId, groupId, groupName, rs);
        return e;
    }

    private RepDailyCreativeReport toRepEntity(Long accountId, Long groupId, String groupName, ReportStruct rs) {
        RepDailyCreativeReport e = new RepDailyCreativeReport();
        fillFields(e, accountId, groupId, groupName, rs);
        return e;
    }

    private void fillFields(BaseDailyCreativeReport e, Long accountId, Long groupId, String groupName, ReportStruct rs) {
        e.setAccountId(accountId);
        e.setGroupId(groupId);
        e.setGroupName(groupName);
        e.setDynamicCreativeId(rs.getDynamicCreativeId());
        e.setDate(LocalDate.parse(rs.getDate()));
        // 基础
        e.setViewCount(rs.getViewCount());
        e.setViewUserCount(rs.getViewUserCount());
        e.setAvgViewPerUser(d2b(rs.getAvgViewPerUser()));
        e.setValidClickCount(rs.getValidClickCount());
        e.setClickUserCount(rs.getClickUserCount());
        e.setCpc(rs.getCpc());
        e.setCtr(d2b(rs.getCtr()));
        e.setValuableClickCount(rs.getValuableClickCount());
        e.setValuableClickCost(rs.getValuableClickCost());
        e.setValuableClickRate(d2b(rs.getValuableClickRate()));
        // 花费
        e.setCost(rs.getCost());
        e.setAcquisitionCost(rs.getAcquisitionCost());
        e.setThousandDisplayPrice(rs.getThousandDisplayPrice());
        // 转化
        e.setConversionsCount(rs.getConversionsCount());
        e.setConversionsRate(d2b(rs.getConversionsRate()));
        e.setConversionsCost(rs.getConversionsCost());
        e.setDeepConversionsCount(rs.getDeepConversionsCount());
        e.setDeepConversionsRate(d2b(rs.getDeepConversionsRate()));
        e.setDeepConversionsCost(rs.getDeepConversionsCost());
        e.setConversionsByDisplayCount(rs.getConversionsByDisplayCount());
        e.setConversionsByDisplayRate(d2b(rs.getConversionsByDisplayRate()));
        e.setConversionsByDisplayCost(rs.getConversionsByDisplayCost());
        e.setConversionsByClickCount(rs.getConversionsByClickCount());
        e.setConversionsByClickRate(d2b(rs.getConversionsByClickRate()));
        e.setConversionsByClickCost(rs.getConversionsByClickCost());
        e.setPreviewConversionsCount(rs.getPreviewConversionsCount());
        e.setPreviewDeepConversionsCount(rs.getPreviewDeepConversionsCount());
        // 视频
        e.setVideoOuterPlayCount(rs.getVideoOuterPlayCount());
        e.setVideoOuterPlayUserCount(rs.getVideoOuterPlayUserCount());
        e.setAvgUserPlayCount(d2b(rs.getAvgUserPlayCount()));
        e.setVideoOuterPlayTimeCount(d2b(rs.getVideoOuterPlayTimeCount()));
        e.setVideoOuterPlayTimeAvgRate(d2b(rs.getVideoOuterPlayTimeAvgRate()));
        e.setVideoOuterPlayRate(d2b(rs.getVideoOuterPlayRate()));
        e.setVideoOuterPlayCost(rs.getVideoOuterPlayCost());
        e.setVideoOuterPlay10Count(rs.getVideoOuterPlay10Count());
        e.setVideoOuterPlay25Count(rs.getVideoOuterPlay25Count());
        e.setVideoOuterPlay50Count(rs.getVideoOuterPlay50Count());
        e.setVideoOuterPlay75Count(rs.getVideoOuterPlay75Count());
        e.setVideoOuterPlay90Count(rs.getVideoOuterPlay90Count());
        e.setVideoOuterPlay95Count(rs.getVideoOuterPlay95Count());
        e.setVideoOuterPlay100Count(rs.getVideoOuterPlay100Count());
        e.setVideoOuterPlay3sCount(rs.getVideoOuterPlay3sCount());
        e.setVideoOuterPlay3sRate(d2b(rs.getVideoOuterPlay3sRate()));
        e.setVideoOuterPlay5sCount(rs.getVideoOuterPlay5sCount());
        e.setVideoOuterPlay7sCount(rs.getVideoOuterPlay7sCount());
        // 互动
        e.setReadCount(rs.getReadCount());
        e.setReadCost(rs.getReadCost());
        e.setCommentCount(rs.getCommentCount());
        e.setCommentCost(rs.getCommentCost());
        e.setPraiseCount(rs.getPraiseCount());
        e.setPraiseCost(rs.getPraiseCost());
        e.setForwardCount(rs.getForwardCount());
        e.setForwardCost(rs.getForwardCost());
        e.setNoInterestCount(rs.getNoInterestCount());
        // 落地页
        e.setPlatformPageViewCount(rs.getPlatformPageViewCount());
        e.setPlatformPageViewRate(d2b(rs.getPlatformPageViewRate()));
        e.setLanButtonClickCount(rs.getLanButtonClickCount());
        e.setLanJumpButtonClickers(rs.getLanJumpButtonClickers());
        e.setLanButtonClickCost(rs.getLanButtonClickCost());
        e.setLanJumpButtonCtr(d2b(rs.getLanJumpButtonCtr()));
        e.setLanJumpButtonClickCost(rs.getLanJumpButtonClickCost());
        // 关键页面
        e.setKeyPageViewCount(rs.getKeyPageViewCount());
        e.setKeyPageUv(rs.getKeyPageUv());
        // ROI/LTV - 注意：SDK字段名 incomeRoi124h 对应 API income_roi_1_24h
        e.setIncomeVal1(rs.getIncomeVal1());
        e.setIncomeRoi1(d2b(rs.getIncomeRoi1()));
        e.setIncomeVal3(rs.getIncomeVal3());
        e.setIncomeRoi3(d2b(rs.getIncomeRoi3()));
        e.setIncomeVal7(rs.getIncomeVal7());
        e.setIncomeRoi7(d2b(rs.getIncomeRoi7()));
        e.setIncomeVal14(rs.getIncomeVal14());
        e.setIncomeRoi14(d2b(rs.getIncomeRoi14()));
        e.setIncomeVal24h(rs.getIncomeVal24h());
        e.setIncomeRoi124h(d2b(rs.getIncomeRoi124h()));
    }

    /** Double → BigDecimal 安全转换 */
    private BigDecimal d2b(Double d) {
        return d == null ? null : BigDecimal.valueOf(d);
    }
}
