package com.sckw.transport.service.zj;


import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import com.sckw.core.utils.StringUtils;
import com.sckw.transport.response.CollectZjxlResponse;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.stereotype.Service;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.*;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.*;


/**
 * Author: donglang
 * Time: 2025-10-19
 * Des: 中交接口
 * Version: 1.0
 */

@Service
@Slf4j
public class VehicleCollectService {

    @Resource
    private ObjectMapper objectMapper;

    public static final String HMAC_SHA1 = "HmacSHA1";
    public static final char[] DIGITAL = "0123456789ABCDEF".toCharArray();


    //调用中交接口的私钥、客户端id、业务参数等常量
    private static final String BASE_URL = "https://openapi-test.sinoiov.cn/save/apis/";
    private static final String TRANS_TIME_MANAGE_URL = BASE_URL + "transTimeManageV3";
    private static final String CID = "7598ca30-000e-4930-953d-39e6756b6ee5";
    private static final String SRT = "2b15bfd6-3546-4e25-bc9b-7cf88d4d2c3c";
    private static final String DEFAULT_TIME_NEARBY = "24";

    /**
     * <pre>
     * 请求流程：
     * 1、其他 API，cid + 私钥生成的签名 + 业务参数
     * </pre>
     */
    public CollectZjxlResponse transTimeManage(String carNo) {
        // 1. 参数校验：前置检查入参合法性，避免无效调用
        if (StringUtils.isBlank(carNo)) {
            log.error("查询失败：车牌号不能为空");
            throw new IllegalArgumentException("车牌号不能为空");
        }

        //2、构建参数
        Map<String, String> vLastLocationParam = new HashMap<>();
        // 客户端ID
        vLastLocationParam.put("cid", CID);
        // 私钥
        vLastLocationParam.put("srt", SRT);
        // 业务参数
        vLastLocationParam.put("vclN", carNo);
        vLastLocationParam.put("timeNearby", DEFAULT_TIME_NEARBY);

        //3、调用第三方
        String responseJson = null;
        try {
            log.info("开始调用中交接口，车牌号：{}，URL：{}", carNo, TRANS_TIME_MANAGE_URL);
            responseJson = httpsCall(TRANS_TIME_MANAGE_URL, vLastLocationParam, false);
            log.info("中交接口返回原始数据：{}", responseJson);

            // 4. 反序列化
            if (responseJson == null) {
                log.error("查询失败：接口返回空数据，车牌号：{}", carNo);
                throw new RuntimeException("中交接口返回空数据");
            }

            CollectZjxlResponse response = objectMapper.readValue(responseJson, CollectZjxlResponse.class);
            log.info("查询成功，车牌号：{}，返回状态码：{}", carNo, response.getStatus());
            return response;

        } catch (JsonProcessingException e) {
            log.error("JSON解析失败，原始数据：{}，错误：{}", responseJson, e.getMessage());
            throw new RuntimeException("第三方接口返回格式异常", e);
        } catch (Exception e) {
            log.error("中交接口调用失败，车牌号：{}，错误：{}", carNo, e.getMessage());
            throw new RuntimeException("中交接口调用失败", e);
        }

    }


    /**
     * https 调用，1、处理参数：①移除srt，②计算签名，③增加sign；2、将 map参数拼接成string格式；3、发起调用；4、处理结果
     *
     * @param url    url
     * @param param  请求参数
     * @param strict 严格模式 true 校验证书等；false 不校验
     */
    public String httpsCall(String url, Map<String, String> param, boolean strict) throws Exception {
        // 1、处理参数
        processParam(param);
        // 2、将 map 参数拼接成 string 格式
        String reqBody = convertMapToString(param);
        System.out.printf("Request - url: %s, reqBody: %s\n", url, reqBody);
        if (!strict) {
            // 信任证书
            trustAllCerts();
            // 信任域名
            trustAllHosts();
        }
        // 3、发起调用；
        HttpsURLConnection conn = (HttpsURLConnection) new URL(url).openConnection();
        conn.setDoInput(true);
        conn.setDoOutput(true);

        conn.setUseCaches(false);
        conn.setRequestMethod("POST");
        conn.setRequestProperty("Content-Length", String.valueOf(reqBody.getBytes(StandardCharsets.UTF_8).length));
        conn.setRequestProperty("charset", "UTF-8");
        conn.connect();
        PrintWriter writer = new PrintWriter(new OutputStreamWriter(conn.getOutputStream(), StandardCharsets.UTF_8));
        // 注意这里不能有换行
        writer.print(reqBody);
        writer.flush();
        writer.close();
        BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8));
        String line;
        StringBuilder result = new StringBuilder();
        while ((line = reader.readLine()) != null) {
            result.append(line);
        }

        reader.close();
        System.out.printf("Response - code: %s, message: %s, respBody: %s\n", conn.getResponseCode(), conn.getResponseMessage(), result);
        // 4、处理结果
        return result.toString();
    }


    /**
     * 处理参数：①移除 srt，②计算签名，③增加 sign；
     *
     * @param param 原始参数
     */
    private void processParam(Map<String, String> param) throws Exception {
        if (!param.containsKey("srt")) {
            throw new NullPointerException();
        }
        String srt = param.remove("srt");
        List<String> paramValueList = new ArrayList<>();
        for (Map.Entry<String, String> entry : param.entrySet()) {
            paramValueList.add(entry.getKey() + entry.getValue());
        }
        Collections.sort(paramValueList);
        String[] data = new String[paramValueList.size()];
        paramValueList.toArray(data);
        byte[] signature = hmacSha1(data, srt.getBytes(StandardCharsets.UTF_8));
        String sign = encodeHexStr(signature);
        param.put("sign", sign);
    }


    /**
     * 将参数转换为字符串
     *
     * @param param 请求参数
     */
    private String convertMapToString(Map<String, String> param) {
        StringBuilder sb = new StringBuilder();
        if (param != null && !param.isEmpty()) {
            for (Map.Entry<String, String> entry : param.entrySet()) {
                sb.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
            }
            sb.deleteCharAt(sb.length() - 1);
        }
        return sb.toString();
    }


    /**
     * 信任所有证书
     */
    private void trustAllCerts() throws Exception {
        TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
            public X509Certificate[] getAcceptedIssuers() {
                return null;
            }

            public void checkClientTrusted(X509Certificate[] certs, String authType) {
            }

            public void checkServerTrusted(X509Certificate[] certs, String authType) {
            }
        }};
        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(null, trustAllCerts, new SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
    }

    /**
     * 信任所有域名
     */
    private void trustAllHosts() {
        HostnameVerifier trustAllHosts = (hostname, session) -> true;
        HttpsURLConnection.setDefaultHostnameVerifier(trustAllHosts);
    }


    /**
     * HmacSha1 定制算法
     *
     * @param strings 内容
     * @param key     key
     * @return 签名
     */
    private byte[] hmacSha1(String[] strings, byte[] key) throws Exception {
        SecretKeySpec signingKey = new SecretKeySpec(key, HMAC_SHA1);
        Mac mac = Mac.getInstance(HMAC_SHA1);
        mac.init(signingKey);
        for (String data : strings) {
            mac.update(data.getBytes(StandardCharsets.UTF_8));
        }
        return mac.doFinal();

    }


    /**
     * 字节转为 Hex 字符串
     *
     * @param bytes 字节数组
     * @return Hex 字符串
     */
    private String encodeHexStr(final byte[] bytes) {
        if (bytes == null) {
            return null;
        }
        char[] result = new char[bytes.length * 2];
        for (int i = 0; i < bytes.length; i++) {
            result[i * 2] = DIGITAL[(bytes[i] & 0xf0) >> 4];
            result[i * 2 + 1] = DIGITAL[bytes[i] & 0x0f];
        }
        return new String(result);
    }


}
