package com.zt.plat.module.qms;

import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zt.plat.framework.common.util.security.CryptoSignatureUtils;

import java.io.IOException;
import java.io.PrintStream;
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.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.time.Duration;
import java.util.*;

public class DatabusApiInvocationExample {
    public static final String TIMESTAMP = Long.toString(System.currentTimeMillis());
    private static final String APP_ID = "qms-auto-ingredients";
    private static final String APP_SECRET = "tjDKCUGNEDR9yNgbxIsvtXsRMuQK+tj1HNEMpgjJOPU=";
    private static final String ENCRYPTION_TYPE = CryptoSignatureUtils.ENCRYPT_TYPE_AES;
//    private static final String TARGET_API = "http://172.16.46.62:30081/admin-api/databus/api/portal";    //test
    private static final String TARGET_API = "http://172.17.19.29:48080/admin-api/databus/api/portal";      //prd
//    private static final String TARGET_API = "https://jygk.chncopper.com:30078/admin-api/databus/api/portal";
    private static final HttpClient HTTP_CLIENT = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(5)).build();
    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
    private static final PrintStream OUT = buildConsolePrintStream();
    public static final String ZT_APP_ID = "ZT-App-Id";
    public static final String ZT_TIMESTAMP = "ZT-Timestamp";
    public static final String ZT_NONCE = "ZT-Nonce";
    public static final String ZT_SIGNATURE = "ZT-Signature";
    public static final String CONTENT_TYPE = "Content-Type";

    private DatabusApiInvocationExample() {
    }

    public static void main(String[] args) throws Exception {
//        OUT.println("=== GET 请求示例 ===");
//        executeGetExample();
//        OUT.println();
//        OUT.println("=== POST 请求示例 ===");
        executePostExample();
//        String encodeStr = "SFqGUDidGgWMWvcU6Qy8SNvyhESvZV9uViIz6lisYB9A1qvKrhotX0JXLrcgcL057d+ZVMDJxngw+UvQ5F6UnYXeDGtyAr/ZXKqSXApygdPYZQPwbj1hzUAZhTw/oHUc7ucU59jT5N1TYW/4jJPzmnMU28D0gXrRVDUmX/SNfPlpN9YvPIobzMeoXZlNkFdURZzIX9aQbhgRNXZHruGQGcqDLePkSh01oq9D+i65DNWQGAYDTGfdVTXuGJw6UY4fQJsn7/5HxZoVWKXgKwAmWo/+KwUG5AVD1BcfK7avYLXAcmnJJftFwZvB08eoe7/ySp4ytv1iSHYxUGZ8YNbUS/wIA3pVhfJHWz1Ko0t3nBbxBtLAOmj2jNzjxU/TI/g1n5dtX8B2OoiMZUHHJaHNEMqmULfLILZE7oMGNbRAh1T0gZcU+rkFfm/995ZnTUmaCCKrKZAs5p0FPqhXRf/ZchN0G6opd9hBE+hJY7v92uDqK5RkgomRIAFcf73d7pnIXTgKHMtLLn7r298+A1kKgyH28wGS6pqGfvdZ1yHqbb5zFNvA9IF60VQ1Jl/0jXz5CJBXy+d+QqX/s06uXDzZ2UWcyF/WkG4YETV2R67hkBnKgy3j5EodNaKvQ/ouuQzVkBgGA0xn3VU17hicOlGOH0CbJ+/+R8WaFVil4CsAJlqjj0mF1g5XV34ddfgOcGFYdcKvMDA+YRf1zK8oGRwGekqeMrb9Ykh2MVBmfGDW1EueSNggsyxhSQWEnThMCB2stDi4frs5kanGFhC7er/L9fTD1leamk8i04Wy5eC0PALKplC3yyC2RO6DBjW0QIdU9IGXFPq5BX5v/feWZ01JmggiqymQLOadBT6oV0X/2XIimcN9BgTUX2en4UohmG1NAjFVyWRqx4KmhX10IRwB47CxOMt1NRhwz7UCTqb7fn60/OcolT4ljcD7SuLCj5zwNCt3cQ4eX+mpPecoUtsPdA==";

//        String decrypt = tryDecrypt(encodeStr);
//        String aa = normalizePotentialMojibake(decrypt);
//        OUT.println(decrypt);
    }

    private static void executeGetExample() throws Exception {
        Map<String, Object> queryParams = new LinkedHashMap<>();
        queryParams.put("taskTimeStart", "2026-01-01 09:00:00");
        queryParams.put("taskTimeEnd", "2026-02-12 23:00:00");
//        queryParams.put("id", "1994326604806447106");
        String signature = generateSignature(queryParams, Map.of());
        String url = TARGET_API + "/qms.ytjc.auto-ingredients.task-list/v1";            //获取检验批列表
//        String url = TARGET_API + "/qms.ytjc.auto-ingredients.task-detail-list/v1";   //获取检验批中的检测任务
        System.out.println("url:" + url);
//        System.out.println(System.getProperty("http.proxyHost"));
//        System.out.println(System.getProperty("http.proxyPort"));
        URI requestUri = buildUri(url, queryParams);
        String nonce = randomNonce();
        HttpRequest request = HttpRequest.newBuilder(requestUri)
                .timeout(Duration.ofSeconds(60))
                .header(ZT_APP_ID, APP_ID)
                .header(ZT_TIMESTAMP, TIMESTAMP)
                .header(ZT_NONCE, nonce)
                .header(ZT_SIGNATURE, signature)
//                .header("ZT-Auth-Token", "a75c0ea94c7f4a88b86b60bbc0b432c3")
                .GET()
                .build();
        HttpResponse<String> response = HTTP_CLIENT.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
        printResponse(response);
    }

    private static void executePostExample() throws Exception {
        Map<String, Object> queryParams = new LinkedHashMap<>();
        String jsonStr = "{\n" +
                "  \"taskId\": \"2021811965242306561\",\n" +
                "  \"sampleList\": [\n" +
                "  {\n" +
                "    \"cupNumber\": 1,\n" +
                "    \"id\": \"2020704495104233505\",\n" +
                "    \"remark\": [\"\"],\n" +
                "    \"sampleCode\": \"25P0107-34-1\",\n" +
                "    \"sampleName\": \"铜精矿\",\n" +
                "    \"ingredient_Na2CO3\": \"35.30\",\n" +
                "    \"ingredient_Na2B4O7_10H2O\": \"10.90\",\n" +
                "    \"ingredient_PbO\": \"157.30\",\n" +
                "    \"ingredient_KNO3\": \"4.60\",\n" +
                "    \"ingredient_C\": \"/\",\n" +
                "    \"BLF\": \"/\",\n" +
                "    \"ingredient_SiO2\": \"14.80\",\n" +
                "    \"ingredient_AgNO3\": \"/\"\n" +
                "  },\n" +
                "  {\n" +
                "    \"cupNumber\": 2,\n" +
                "    \"id\": \"2020704495108427802\",\n" +
                "    \"remark\": [\"\"],\n" +
                "    \"sampleCode\": \"25P0107-34-2\",\n" +
                "    \"sampleName\": \"铜精矿\",\n" +
                "    \"ingredient_Na2CO3\": \"37.80\",\n" +
                "    \"ingredient_Na2B4O7_10H2O\": \"11.20\",\n" +
                "    \"ingredient_PbO\": \"157.60\",\n" +
                "    \"ingredient_KNO3\": \"4.20\",\n" +
                "    \"ingredient_C\": \"/\",\n" +
                "    \"BLF\": \"/\",\n" +
                "    \"ingredient_SiO2\": \"14.90\",\n" +
                "    \"ingredient_AgNO3\": \"/\"\n" +
                "  },\n" +
                "  {\n" +
                "    \"cupNumber\": 3,\n" +
                "    \"id\": \"2020704495112622115\",\n" +
                "    \"remark\": [\"\"],\n" +
                "    \"sampleCode\": \"25P0107-34-3\",\n" +
                "    \"sampleName\": \"铜精矿\",\n" +
                "    \"ingredient_Na2CO3\": \"37.20\",\n" +
                "    \"ingredient_Na2B4O7_10H2O\": \"11.00\",\n" +
                "    \"ingredient_PbO\": \"157.90\",\n" +
                "    \"ingredient_KNO3\": \"4.30\",\n" +
                "    \"ingredient_C\": \"/\",\n" +
                "    \"BLF\": \"/\",\n" +
                "    \"ingredient_SiO2\": \"14.90\",\n" +
                "    \"ingredient_AgNO3\": \"/\"\n" +
                "  },\n" +
                "  {\n" +
                "    \"cupNumber\": 4,\n" +
                "    \"id\": \"2020704495112622157\",\n" +
                "    \"remark\": [\"\"],\n" +
                "    \"sampleCode\": \"25P0107-34-4\",\n" +
                "    \"sampleName\": \"铜精矿\",\n" +
                "    \"ingredient_Na2CO3\": \"36.30\",\n" +
                "    \"ingredient_Na2B4O7_10H2O\": \"10.90\",\n" +
                "    \"ingredient_PbO\": \"157.80\",\n" +
                "    \"ingredient_KNO3\": \"4.10\",\n" +
                "    \"ingredient_C\": \"/\",\n" +
                "    \"BLF\": \"/\",\n" +
                "    \"ingredient_SiO2\": \"14.90\",\n" +
                "    \"ingredient_AgNO3\": \"/\"\n" +
                "  },\n" +
                "  {\n" +
                "    \"cupNumber\": 5,\n" +
                "    \"id\": \"2020704495116816424\",\n" +
                "    \"remark\": [\"\"],\n" +
                "    \"sampleCode\": \"25P0107-34-5\",\n" +
                "    \"sampleName\": \"铜精矿\",\n" +
                "    \"ingredient_Na2CO3\": \"35.90\",\n" +
                "    \"ingredient_Na2B4O7_10H2O\": \"11.20\",\n" +
                "    \"ingredient_PbO\": \"158.00\",\n" +
                "    \"ingredient_KNO3\": \"4.30\",\n" +
                "    \"ingredient_C\": \"/\",\n" +
                "    \"BLF\": \"/\",\n" +
                "    \"ingredient_SiO2\": \"15.30\",\n" +
                "    \"ingredient_AgNO3\": \"/\"\n" +
                "  },\n" +
                "  {\n" +
                "    \"cupNumber\": 6,\n" +
                "    \"id\": \"2020704495116816466\",\n" +
                "    \"remark\": [\"\"],\n" +
                "    \"sampleCode\": \"25P0107-34-6\",\n" +
                "    \"sampleName\": \"铜精矿\",\n" +
                "    \"ingredient_Na2CO3\": \"37.70\",\n" +
                "    \"ingredient_Na2B4O7_10H2O\": \"11.20\",\n" +
                "    \"ingredient_PbO\": \"157.10\",\n" +
                "    \"ingredient_KNO3\": \"4.20\",\n" +
                "    \"ingredient_C\": \"/\",\n" +
                "    \"BLF\": \"/\",\n" +
                "    \"ingredient_SiO2\": \"14.70\",\n" +
                "    \"ingredient_AgNO3\": \"/\"\n" +
                "  },\n" +
                "  {\n" +
                "    \"cupNumber\": 7,\n" +
                "    \"id\": \"2020704495121010692\",\n" +
                "    \"remark\": [\"\"],\n" +
                "    \"sampleCode\": \"25P0107-34-7\",\n" +
                "    \"sampleName\": \"铜精矿\",\n" +
                "    \"ingredient_Na2CO3\": \"34.30\",\n" +
                "    \"ingredient_Na2B4O7_10H2O\": \"10.70\",\n" +
                "    \"ingredient_PbO\": \"156.70\",\n" +
                "    \"ingredient_KNO3\": \"4.10\",\n" +
                "    \"ingredient_C\": \"/\",\n" +
                "    \"BLF\": \"/\",\n" +
                "    \"ingredient_SiO2\": \"14.50\",\n" +
                "    \"ingredient_AgNO3\": \"/\"\n" +
                "  },\n" +
                "  {\n" +
                "    \"cupNumber\": 8,\n" +
                "    \"id\": \"2020704495121010751\",\n" +
                "    \"remark\": [\"\"],\n" +
                "    \"sampleCode\": \"25P0107-34-8\",\n" +
                "    \"sampleName\": \"铜精矿\",\n" +
                "    \"ingredient_Na2CO3\": \"36.30\",\n" +
                "    \"ingredient_Na2B4O7_10H2O\": \"10.80\",\n" +
                "    \"ingredient_PbO\": \"157.00\",\n" +
                "    \"ingredient_KNO3\": \"4.00\",\n" +
                "    \"ingredient_C\": \"/\",\n" +
                "    \"BLF\": \"/\",\n" +
                "    \"ingredient_SiO2\": \"14.80\",\n" +
                "    \"ingredient_AgNO3\": \"/\"\n" +
                "  }\n" +
                "]\n" +
                "}";
        JSONObject jsonObject = JSONObject.parseObject(jsonStr);
        String bodyJson = jsonObject.toJSONString();

        Map<String, Object> bodyParams = parseBodyJson(bodyJson);
        String signature = generateSignature(queryParams, bodyParams);
        String url = TARGET_API + "/qms.ytjc.auto-ingredients.submit-assay-result/v1";
        URI requestUri = buildUri(url, queryParams);
        String nonce = randomNonce();
        String cipherBody = encryptPayload(bodyJson);
        OUT.println("原始 Request Body: " + bodyJson);
        OUT.println("加密 Request Body: " + cipherBody);

        HttpRequest request = HttpRequest.newBuilder(requestUri)
                .timeout(Duration.ofSeconds(10))
                .header(ZT_APP_ID, APP_ID)
                .header(ZT_TIMESTAMP, TIMESTAMP)
                .header(ZT_NONCE, nonce)
                .header(ZT_SIGNATURE, signature)
                .header(CONTENT_TYPE, "application/json")
                .POST(HttpRequest.BodyPublishers.ofString(cipherBody, StandardCharsets.UTF_8))
                .build();

        HttpResponse<String> response = HTTP_CLIENT.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
        printResponse(response);
    }

    private static String encryptPayload(String plaintext) {
        try {
            return CryptoSignatureUtils.encrypt(plaintext, APP_SECRET, ENCRYPTION_TYPE);
        } catch (Exception ex) {
            throw new IllegalStateException("Failed to encrypt request body", ex);
        }
    }

    private static void printResponse(HttpResponse<String> response) {
        OUT.println("HTTP Status: " + response.statusCode());
        String cipherText = response.body();
        OUT.println("加密 Response: " + cipherText);
        String plain = tryDecrypt(cipherText);
        OUT.println("原始 Response: " + normalizePotentialMojibake(plain));
    }

    private static String randomNonce() {
        return UUID.randomUUID().toString().replace("-", "");
    }

    private static URI buildUri(String baseUrl, Map<String, Object> queryParams) {
        if (queryParams == null || queryParams.isEmpty()) {
            return URI.create(baseUrl);
        }
        StringBuilder builder = new StringBuilder(baseUrl);
        builder.append(baseUrl.contains("?") ? '&' : '?');
        boolean first = true;
        for (Map.Entry<String, Object> entry : queryParams.entrySet()) {
            if (!first) {
                builder.append('&');
            }
            first = false;
            builder.append(URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8));
            builder.append('=');
            builder.append(URLEncoder.encode(String.valueOf(entry.getValue()), StandardCharsets.UTF_8));
        }
        return URI.create(builder.toString());
    }

    private static String generateSignature(Map<String, Object> queryParams, Map<String, Object> bodyParams) {
        TreeMap<String, Object> sorted = new TreeMap<>();
        if (queryParams != null) {
            queryParams.forEach((key, value) -> sorted.put(key, normalizeValue(value)));
        }
        if (bodyParams != null) {
            bodyParams.forEach((key, value) -> sorted.put(key, normalizeValue(value)));
        }
        sorted.put(ZT_APP_ID, APP_ID);
        sorted.put(ZT_TIMESTAMP, TIMESTAMP);
        StringBuilder canonical = new StringBuilder();
        sorted.forEach((key, value) -> {
            if (value == null) {
                return;
            }
            if (canonical.length() > 0) {
                canonical.append('&');
            }
            canonical.append(key).append('=').append(value);
        });
        OUT.println("原始 签名串: " + canonical);
        String md5Hex = md5Hex(canonical.toString());
        OUT.println("原始签名: " + md5Hex);
        return md5Hex;
    }

    private static Object normalizeValue(Object value) {
        if (value == null) {
            return null;
        }
        if (value instanceof Map || value instanceof Iterable) {
            try {
                return OBJECT_MAPPER.writeValueAsString(value);
            } catch (JsonProcessingException ignored) {
                return value.toString();
            }
        }
        return value;
    }

    private static Map<String, Object> parseBodyJson(String bodyJson) {
        if (bodyJson == null || bodyJson.isBlank()) {
            return Map.of();
        }
        try {
            return OBJECT_MAPPER.readValue(bodyJson, new TypeReference<Map<String, Object>>() { });
        } catch (IOException ex) {
            throw new IllegalArgumentException("Failed to parse request body JSON", ex);
        }
    }

    private static String md5Hex(String input) {
        try {
            MessageDigest digest = MessageDigest.getInstance("MD5");
            byte[] bytes = digest.digest(input.getBytes(StandardCharsets.UTF_8));
            StringBuilder hex = new StringBuilder(bytes.length * 2);
            for (byte b : bytes) {
                String segment = Integer.toHexString(b & 0xFF);
                if (segment.length() == 1) {
                    hex.append('0');
                }
                hex.append(segment);
            }
            return hex.toString();
        } catch (NoSuchAlgorithmException ex) {
            throw new IllegalStateException("MD5 algorithm not available", ex);
        }
    }

    private static String tryDecrypt(String cipherText) {
        if (cipherText == null || cipherText.isBlank()) {
            return cipherText;
        }
        try {
            // Databus 会在凭证开启加密时返回密文，这里做一次解密展示真实响应。
            return CryptoSignatureUtils.decrypt(cipherText, APP_SECRET, ENCRYPTION_TYPE);
        } catch (Exception ex) {
            return "<unable to decrypt> " + ex.getMessage();
        }
    }

    // 解决控制台打印 乱码问题
    private static String normalizePotentialMojibake(String value) {
        if (value == null || value.isEmpty()) {
            return value;
        }
        long suspectCount = value.chars().filter(ch -> ch >= 0x80 && ch <= 0xFF).count();
        long highCount = value.chars().filter(ch -> ch > 0xFF).count();
        if (suspectCount > 0 && highCount == 0) {
            try {
                byte[] decoded = value.getBytes(StandardCharsets.ISO_8859_1);
                String converted = new String(decoded, StandardCharsets.UTF_8);
                if (converted.chars().anyMatch(ch -> ch > 0xFF)) {
                    return converted;
                }
            } catch (Exception ignored) {
                return value;
            }
        }
        return value;
    }

    /**
     * 输出流编码与当前控制台保持一致，避免中文字符再次出现编码差异。
     */
    private static PrintStream buildConsolePrintStream() {
        try {
            String consoleEncoding = System.getProperty("sun.stdout.encoding");
            if (consoleEncoding != null && !consoleEncoding.isBlank()) {
                return new PrintStream(System.out, true, Charset.forName(consoleEncoding));
            }
            return new PrintStream(System.out, true, Charset.defaultCharset());
        } catch (Exception ignored) {
            return System.out;
        }
    }
}
