package com.adx.tracking.baidu;

import com.adx.tracking.baidu.model.ConversionQuery;
import com.adx.tracking.baidu.model.ConversionResponse;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.IOException;
import java.math.BigInteger;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.time.Duration;
import java.time.Instant;
import java.util.HashMap;
import java.util.HexFormat;
import java.util.Map;

/**
 * 对应 Go internal/baidu/conversion.go 的 ConversionClient。
 * 签名算法：md5(timestamp + secret + randomStr)
 */
public class ConversionClient {

    private static final String PAYMENT_PATH = "/media/order-stimulate/payment";

    private final String baseUrl;
    private final String customerName;
    private final String secret;
    private final HttpClient httpClient;
    private final ObjectMapper objectMapper;

    public ConversionClient(String baseUrl, String customerName, String secret,
                            ObjectMapper objectMapper) {
        this.baseUrl = baseUrl.replaceAll("/+$", "");
        this.customerName = customerName;
        this.secret = secret;
        this.httpClient = HttpClient.newBuilder()
                .connectTimeout(Duration.ofSeconds(10))
                .build();
        this.objectMapper = objectMapper;
    }

    /**
     * 查询百度转化数据（支付信息）。
     * 对应 Go ConversionClient.QueryPayments()。
     */
    public ConversionResponse queryPayments(ConversionQuery query) throws IOException, InterruptedException {
        if (customerName == null || customerName.isBlank()) {
            throw new IllegalStateException("customer name is required");
        }
        if (secret == null || secret.isBlank()) {
            throw new IllegalStateException("secret is required");
        }
        if (query.getDate() == null || query.getDate().isBlank()) {
            throw new IllegalArgumentException("date is required");
        }
        if (query.getPageSize() <= 0) {
            throw new IllegalArgumentException("pageSize must be > 0");
        }

        long timestamp = Instant.now().getEpochSecond();
        String randomStr = newRandomString();
        String sign = conversionSign(timestamp, secret, randomStr);

        Map<String, Object> body = new HashMap<>();
        body.put("timestamp",    timestamp);
        body.put("randomStr",    randomStr);
        body.put("sign",         sign);
        body.put("customerName", customerName);
        body.put("date",         query.getDate());
        body.put("pageSize",     query.getPageSize());
        if (query.getAppSid()  != null && !query.getAppSid().isBlank())  body.put("appsid",   query.getAppSid());
        if (query.getTu()      != null && !query.getTu().isBlank())      body.put("tu",        query.getTu());
        if (query.getDeviceId()!= null && !query.getDeviceId().isBlank())body.put("deviceid",  query.getDeviceId());
        if (query.getActs()    != null && !query.getActs().isEmpty())    body.put("acts",       query.getActs());

        byte[] payload = objectMapper.writeValueAsBytes(body);
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(baseUrl + PAYMENT_PATH))
                .POST(HttpRequest.BodyPublishers.ofByteArray(payload))
                .header("Content-Type", "application/json")
                .header("User-Agent", AdxClient.BAIDU_USER_AGENT)
                .timeout(Duration.ofSeconds(30))
                .build();

        HttpResponse<byte[]> response = httpClient.send(request, HttpResponse.BodyHandlers.ofByteArray());
        if (response.statusCode() < 200 || response.statusCode() >= 300) {
            throw new IOException("Baidu conversion HTTP status: " + response.statusCode());
        }

        ConversionResponse resp = objectMapper.readValue(response.body(), ConversionResponse.class);
        // 对应 Go: response.BaseResponse.Code != http.StatusOK
        // Go 中 BaseResponse 是值类型（非指针），缺省 Code=0 != 200 → 也会报错
        if (resp.getBaseResponse() == null || resp.getBaseResponse().getCode() != 200) {
            int code = resp.getBaseResponse() != null ? resp.getBaseResponse().getCode() : 0;
            String msg = resp.getBaseResponse() != null ? resp.getBaseResponse().getMsg() : "";
            throw new IOException("Baidu conversion API failed: " + code + " " + msg);
        }
        return resp;
    }

    /**
     * 签名：md5(timestamp + secret + randomStr)
     * 对应 Go conversionSign()
     */
    static String conversionSign(long timestamp, String secret, String randomStr) {
        try {
            String raw = timestamp + secret + randomStr;
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] hash = md.digest(raw.getBytes(java.nio.charset.StandardCharsets.UTF_8));
            return HexFormat.of().formatHex(hash);
        } catch (Exception e) {
            throw new RuntimeException("MD5 sign failed", e);
        }
    }

    /** 对应 Go newRandomString()：16字节随机数的十六进制字符串 */
    static String newRandomString() {
        byte[] bytes = new byte[16];
        new SecureRandom().nextBytes(bytes);
        return HexFormat.of().formatHex(bytes);
    }
}
