package com.adx.tracking.baidu;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Base64;

/**
 * 对应 Go NewAuctionPriceEncoder + newAESCBCPriceEncoder + newAESECBPriceEncoder。
 * 支持三种模式：none(原始数字)、AES-ECB、AES-CBC。
 */
@FunctionalInterface
public interface AuctionPriceEncoder {

    String encode(long price);

    // ─── 工厂方法 ────────────────────────────────────────────────────────────

    static AuctionPriceEncoder create(String mode, String eKey, String iKey) {
        String m = (mode == null ? "" : mode.trim().toLowerCase());
        if (m.isEmpty()) {
            // 自动推断模式
            boolean hasEKey = eKey != null && !eKey.isEmpty();
            boolean hasIKey = iKey != null && !iKey.isEmpty();
            if (hasEKey && hasIKey) {
                m = "aes-cbc";
            } else if (hasEKey) {
                m = "aes-ecb";
            } else {
                m = "none";
            }
        }
        return switch (m) {
            case "none", "raw" -> AuctionPriceEncoder::defaultEncode;
            case "aes-cbc", "aes-128-cbc", "aes-192-cbc", "aes-256-cbc" ->
                    newAesCbcEncoder(eKey, iKey);
            case "aes-ecb", "aes-128-ecb", "aes-192-ecb", "aes-256-ecb" ->
                    newAesEcbEncoder(eKey);
            default -> throw new IllegalArgumentException(
                    "Unsupported auction price encryption mode: " + mode);
        };
    }

    static String defaultEncode(long price) {
        return URLEncoder.encode(String.valueOf(price), StandardCharsets.UTF_8);
    }

    // ─── AES-CBC ────────────────────────────────────────────────────────────
    static AuctionPriceEncoder newAesCbcEncoder(String eKey, String iKey) {
        if (eKey == null || eKey.isEmpty()) {
            throw new IllegalArgumentException("AES-CBC requires eKey");
        }
        // 对应 Go: len([]byte(iKey)) != aes.BlockSize
        // 必须按 UTF-8 字节数检查，不能使用 String.length()（字符数）
        // 否则包含多字节字符的 iKey 会被错误接受或拒绝
        byte[] keyBytes = eKey.getBytes(StandardCharsets.UTF_8);
        byte[] ivBytes  = iKey == null ? new byte[0] : iKey.getBytes(StandardCharsets.UTF_8);
        if (ivBytes.length != 16) {
            throw new IllegalArgumentException(
                    "AES-CBC iKey must be 16 bytes, got: " + ivBytes.length);
        }
        return price -> {
            try {
                byte[] plaintext = String.valueOf(price).getBytes(StandardCharsets.UTF_8);
                byte[] padded    = pkcs7Pad(plaintext, 16);
                SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
                IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
                Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
                cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
                byte[] encrypted = cipher.doFinal(padded);
                return URLEncoder.encode(
                        Base64.getEncoder().encodeToString(encrypted),
                        StandardCharsets.UTF_8);
            } catch (Exception e) {
                throw new RuntimeException("AES-CBC encode failed", e);
            }
        };
    }

    // ─── AES-ECB ────────────────────────────────────────────────────────────
    static AuctionPriceEncoder newAesEcbEncoder(String eKey) {
        if (eKey == null || eKey.isEmpty()) {
            throw new IllegalArgumentException("AES-ECB requires eKey");
        }
        byte[] keyBytes = eKey.getBytes(StandardCharsets.UTF_8);
        return price -> {
            try {
                byte[] plaintext = String.valueOf(price).getBytes(StandardCharsets.UTF_8);
                byte[] padded    = pkcs7Pad(plaintext, 16);
                SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
                Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
                cipher.init(Cipher.ENCRYPT_MODE, keySpec);
                byte[] encrypted = cipher.doFinal(padded);
                return URLEncoder.encode(
                        Base64.getEncoder().encodeToString(encrypted),
                        StandardCharsets.UTF_8);
            } catch (Exception e) {
                throw new RuntimeException("AES-ECB encode failed", e);
            }
        };
    }

    // ─── PKCS7 填充（对应 Go pkcs7Pad）──────────────────────────────────────
    static byte[] pkcs7Pad(byte[] value, int blockSize) {
        int padding = blockSize - (value.length % blockSize);
        byte[] padded = new byte[value.length + padding];
        System.arraycopy(value, 0, padded, 0, value.length);
        for (int i = value.length; i < padded.length; i++) {
            padded[i] = (byte) padding;
        }
        return padded;
    }
}
