/**
 * 知岁OS 第三方对接示例 — Java 版
 *
 * 使用前请确保：
 * 1. 已在知岁OS后台注册服务商，获取 uuid 和 secretkey
 * 2. Java >= 8
 * 3. 无需额外依赖（使用 javax.crypto 和 java.net.http）
 */

import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.Base64;

public class ZsosClient {

    private final String baseUrl;
    private final String uuid;
    private final byte[] secretKey; // 二进制密钥

    private static final int GCM_IV_LENGTH = 12;   // GCM 推荐 12 字节 IV
    private static final int GCM_TAG_LENGTH = 128;  // 16 字节 Tag = 128 bit

    public ZsosClient(String baseUrl, String uuid, String secretKeyHex) {
        this.baseUrl = baseUrl.replaceAll("/+$", "");
        this.uuid = uuid;
        this.secretKey = hexToBytes(secretKeyHex);
    }

    // ==========================================
    //  开放接口 — useToken（第三方核心接口）
    // ==========================================

    /**
     * 消费数据 token，获取解密后的院校应用数据
     *
     * @param token 用户通过知岁OS小程序获取的接口数据token
     * @return 解密后的原始 JSON 字符串
     */
    public String useToken(String token) throws Exception {
        // 1. 构造明文：UUID + token
        String plaintext = this.uuid + token;

        // 2. AES-256-GCM 加密
        String encrypted = aes256GcmEncrypt(plaintext);

        // 3. 发送请求
        String body = "uuid=" + URLEncoder.encode(uuid, "UTF-8")
                + "&data=" + URLEncoder.encode(encrypted, "UTF-8");

        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(baseUrl + "/api/open/useToken"))
                .header("Content-Type", "application/x-www-form-urlencoded")
                .POST(HttpRequest.BodyPublishers.ofString(body))
                .build();

        HttpClient client = HttpClient.newHttpClient();
        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

        // 解析响应
        String responseBody = response.body();
        // 简单解析（生产环境建议使用 Jackson/Gson）
        if (!responseBody.contains("\"code\":1")) {
            String msg = extractJsonString(responseBody, "msg");
            throw new Exception(msg != null ? msg : "请求失败");
        }

        // 4. 提取并解密响应数据
        String responseData = extractJsonString(responseBody, "data");
        if (responseData == null) {
            throw new Exception("响应中无 data 字段");
        }

        return aes256GcmDecrypt(responseData);
    }

    // ==========================================
    //  AES-256-GCM 加密/解密
    // ==========================================

    private String aes256GcmEncrypt(String plaintext) throws Exception {
        byte[] iv = new byte[GCM_IV_LENGTH];
        new SecureRandom().nextBytes(iv);

        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        SecretKeySpec keySpec = new SecretKeySpec(secretKey, "AES");
        GCMParameterSpec gcmSpec = new GCMParameterSpec(GCM_TAG_LENGTH, iv);
        cipher.init(Cipher.ENCRYPT_MODE, keySpec, gcmSpec);

        byte[] encrypted = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8));
        // encrypted = ciphertext + tag

        // 拼接 IV + encrypted(tag+ciphertext)
        ByteBuffer buffer = ByteBuffer.allocate(iv.length + encrypted.length);
        buffer.put(iv);
        buffer.put(encrypted);

        return Base64.getEncoder().encodeToString(buffer.array());
    }

    private String aes256GcmDecrypt(String encoded) throws Exception {
        byte[] decoded = Base64.getDecoder().decode(encoded);

        byte[] iv = new byte[GCM_IV_LENGTH];
        System.arraycopy(decoded, 0, iv, 0, GCM_IV_LENGTH);

        byte[] ciphertextAndTag = new byte[decoded.length - GCM_IV_LENGTH];
        System.arraycopy(decoded, GCM_IV_LENGTH, ciphertextAndTag, 0, ciphertextAndTag.length);

        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        SecretKeySpec keySpec = new SecretKeySpec(secretKey, "AES");
        GCMParameterSpec gcmSpec = new GCMParameterSpec(GCM_TAG_LENGTH, iv);
        cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmSpec);

        byte[] decrypted = cipher.doFinal(ciphertextAndTag);
        return new String(decrypted, StandardCharsets.UTF_8);
    }

    // ==========================================
    //  工具方法
    // ==========================================

    private static byte[] hexToBytes(String hex) {
        int len = hex.length();
        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            data[i / 2] = (byte) ((Character.digit(hex.charAt(i), 16) << 4)
                    + Character.digit(hex.charAt(i + 1), 16));
        }
        return data;
    }

    /**
     * 简单提取 JSON 字符串值（生产环境建议用 JSON 库）
     */
    private static String extractJsonString(String json, String key) {
        String pattern = "\"" + key + "\":\"";
        int start = json.indexOf(pattern);
        if (start < 0) return null;
        start += pattern.length();
        int end = json.indexOf("\"", start);
        return end > start ? json.substring(start, end) : null;
    }

    // ==========================================
    //  使用示例
    // ==========================================

    public static void main(String[] args) {
        // 配置（替换为你的实际值）
        String baseUrl = "https://your-domain.com";
        String uuid = "your-service-uuid";           // 服务商 UUID
        String secretKey = "your-secret-key-hex";     // 服务商密钥（hex 编码）

        ZsosClient client = new ZsosClient(baseUrl, uuid, secretKey);

        try {
            // 用户通过知岁OS小程序获取的 token（有效期 15 分钟，一次性使用）
            String token = "user-interface-data-token";

            // 调用 useToken 获取数据
            String data = client.useToken(token);

            // 根据接口类型，数据格式不同
            // data 可能是 userInfo、course、score、progress 等格式
            // 具体格式参考 API 文档第 7 节"标准数据格式"

            System.out.println("获取数据成功：");
            System.out.println(data);

        } catch (Exception e) {
            System.err.println("错误: " + e.getMessage());
        }
    }
}
