package com.adx.tracking.conversionsync;

import com.adx.tracking.baidu.ConversionClient;
import com.adx.tracking.baidu.model.ConversionQuery;
import com.adx.tracking.baidu.model.ConversionResponse;
import com.adx.tracking.baidu.model.PaymentInfo;
import com.adx.tracking.media.kuaishou.KuaishouClient;
import com.adx.tracking.storage.RedisHotStore;
import com.adx.tracking.storage.TiDBColdStore;
import com.adx.tracking.storage.model.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.Instant;
import java.util.List;

/**
 * 对应 Go internal/conversionsync/service.go 的 Service。
 * 核心逻辑：
 *   1. 轮询百度转化数据（分页）
 *   2. 按 qk 查找对应的 bid 记录
 *   3. 若是快手 media 且有 callback，发送快手回调（支持幂等去重）
 *   4. 持久化 ConversionRecord 和 MediaCallbackRecord
 */
public class ConversionSyncService {

    private static final Logger log = LoggerFactory.getLogger(ConversionSyncService.class);

    private final ConversionClient baiduClient;
    private final RedisHotStore    hotStore;
    private final KuaishouClient   kuaishouClient;
    private final TiDBColdStore    coldStore;  // nullable

    public ConversionSyncService(ConversionClient baiduClient, RedisHotStore hotStore,
                                  KuaishouClient kuaishouClient, TiDBColdStore coldStore) {
        this.baiduClient    = baiduClient;
        this.hotStore       = hotStore;
        this.kuaishouClient = kuaishouClient;
        this.coldStore      = coldStore;
    }

    /**
     * 对应 Go Service.SyncKuaishouConversions()。
     * 分页拉取并处理，返回汇总 Result。
     */
    public SyncResult syncKuaishouConversions(ConversionQuery query) throws Exception {
        if (query.getPageSize() <= 0) query.setPageSize(1);

        SyncResult result = new SyncResult();
        int maxPages = 100; // 安全上限，防止死循环
        for (int page = query.getPageSize(); ; page++) {
            query.setPageSize(page);
            log.info("[ConversionSync] fetching page={}", page);
            ConversionResponse response = baiduClient.queryPayments(query);
            List<PaymentInfo> data = response.getData();
            int dataSize = data != null ? data.size() : 0;
            log.info("[ConversionSync] page={} returned {} records, responsePageSize={}",
                    page, dataSize, response.getPageSize());
            if (data != null) {
                processPayments(data, result);
            }
            if (response.getPageSize() <= page) break;
            if (page >= maxPages) {
                log.warn("[ConversionSync] reached max page limit ({}), stopping", maxPages);
                break;
            }
        }
        return result;
    }

    private void processPayments(List<PaymentInfo> payments, SyncResult result) throws Exception {
        for (PaymentInfo payment : payments) {
            result.fetched++;

            // 用 qk 查找 bid
            BidRecord bid = hotStore.findBidByQk(payment.getQk());

            if (bid == null) {
                // bid 未找到 → 只保存转化记录，标记 skipped
                if (coldStore != null) {
                    coldStore.saveConversion(conversionRecord(payment, ""));
                }
                result.skipped++;
                continue;
            }

            // bid 不是快手，或没有 callback
            String callback = bid.getMediaParams() != null
                    ? bid.getMediaParams().get("callback") : null;
            if (!"kuaishou".equals(bid.getMedia()) || callback == null || callback.isBlank()) {
                if (coldStore != null) {
                    coldStore.saveConversion(conversionRecord(payment, bid.getMedia()));
                }
                result.skipped++;
                continue;
            }

            result.matched++;
            String conversionKey = conversionDedupeKey(payment, bid.getMedia());
            if (coldStore != null) {
                coldStore.saveConversion(conversionRecord(payment, bid.getMedia()));
            }

            // 获取事件类型列表
            List<Integer> eventTypes;
            try {
                eventTypes = kuaishouClient.conversionEventTypes(payment);
            } catch (Exception e) {
                result.failed++;
                continue;
            }

            for (int eventType : eventTypes) {
                String callbackKey = callbackDedupeKey(payment, bid.getMedia(), eventType);

                // 幂等检查
                if (coldStore != null) {
                    boolean exists = coldStore.successfulCallbackExistsForEvent(
                            callbackKey, conversionKey, eventType);
                    if (exists) {
                        result.alreadySent++;
                        continue;
                    }
                }

                // 发送回调（对应 Go: err != nil 才是网络级失败，HTTP 非2xx 算 sent）
                MediaCallbackRecord callbackRecord = kuaishouClient.sendConversionEvent(bid, payment, eventType);
                callbackRecord.setDedupeKey(callbackKey);
                if (callbackRecord.getStatus() == 0 && callbackRecord.getErrorMessage() != null) {
                    // 网络级错误（连接被拒/超时等），对应 Go err != nil
                    result.failed++;
                    if (coldStore != null) {
                        try { coldStore.saveMediaCallback(callbackRecord); } catch (Exception ignored) {}
                    }
                    continue;
                }
                // HTTP 请求完成（即使状态码非 2xx 也算 sent，对应 Go err==nil）
                result.sent++;
                if (coldStore != null) {
                    coldStore.saveMediaCallback(callbackRecord);
                }
            }
        }
    }

    // ─── 辅助方法 ─────────────────────────────────────────────────────────────

    private static ConversionRecord conversionRecord(PaymentInfo p, String media) {
        ConversionRecord r = new ConversionRecord();
        r.setDedupeKey(conversionDedupeKey(p, media));
        r.setQk(p.getQk());
        r.setMedia(media);
        r.setDate(p.getDate());
        r.setAppSid(p.getAppSid());
        r.setCustomerName(p.getCustomerName());
        r.setDeviceId(p.getDeviceId());
        r.setConv(p.getConv());
        r.setPayment(p.getPayment());
        r.setAct(p.getAct());
        r.setTu(p.getTu());
        r.setClkTime(p.getClkTime());
        r.setCreatedAt(Instant.now());
        return r;
    }

    static String conversionDedupeKey(PaymentInfo p, String media) {
        return String.format("%s:%s:%s:%s:%d", media, p.getQk(), p.getDate(), p.getDeviceId(), p.getAct());
    }

    static String callbackDedupeKey(PaymentInfo p, String media, int eventType) {
        return conversionDedupeKey(p, media) + ":" + eventType;
    }

    // ─── Result DTO ─────────────────────────────────────────────────────────

    public static class SyncResult {
        public int fetched, matched, sent, skipped, failed, alreadySent;
    }
}
