package com.maintain.common.pay;

import cn.hutool.json.JSONUtil;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;

/**
 * 银联支付API
 */
@Component
public class YinLianPayApi {

    @Value("${pay.appid}")
    private String appid;
    @Value("${pay.appkey}")
    private String appkey;
    @Value("${pay.url}")
    private String url;
    @Value("${pay.merchantCode}")
    private String merchantCode;
    @Value("${pay.deviceType}")
    private String deviceType;
    @Value("${pay.payMode}")
    private String payMode;

    public static void main(String[] args) throws Exception {
        // 1. 组建请求报文
        PayBody reqBody = new PayBody();
        reqBody.merchantCode = "123456789111115";
        reqBody.terminalCode = "gxrb0001";
        reqBody.transactionAmount = 1;
        reqBody.transactionCurrencyCode = "156";
        reqBody.merchantOrderId = "121423534643";
        reqBody.merchantRemark = "remark";
        reqBody.payMode = "CODE_SCAN";
        reqBody.payCode = "134183932097088249";
        reqBody.deviceType = "11";

        String requestBody = reqBody.toString();
        System.out.println("请求参数:\n" + requestBody);

        // 2. 获取认证报文
        String timestamp = getCurrentTimestamp14();
        //2. 获取认证报文，timestamp为当前日期，老旧日期无法请求成功
        String authorization = getAuthorization("10037e6f72b586db0172b6ea24ec0000","6f7a00170935445fbe25e67a5ce1be48",timestamp,"nonce",reqBody.toString());
        System.out.println("认证报文:\n"+authorization);

        //3. 发送http请求，并解析返回信息
        String response = request("https://test-api-open.chinaums.com/v6/poslink/transaction/pay",authorization,reqBody.toString());
        System.out.println("响应参数:\n"+response);
        PayResponse payResponse = JSONUtil.toBean(response, PayResponse.class);
        System.out.println(payResponse);
    }

    /**
     * 发起支付
     * @param merchantOrderId 订单号
     * @param transactionAmount 交易金额
     * @param payCode 支付码
     * @param terminalCode 终端号
     * @return 支付结果
     */
    public PayResult pay(String merchantOrderId,int transactionAmount, String payCode, String terminalCode) {
        PayResult payResult = new PayResult();
        // 组建请求报文
        PayBody reqBody = new PayBody();
        reqBody.merchantCode = merchantCode;
        reqBody.terminalCode = terminalCode;
        // 交易金额
        reqBody.transactionAmount = transactionAmount;
        // 交易币种
        reqBody.transactionCurrencyCode = "156";
        // 订单号
        reqBody.merchantOrderId = merchantOrderId;
        reqBody.merchantRemark = "remark";
        // 支付方式(E_CASH – 电子现金、SOUNDWAVE – 声波、NFC – NFC、CODE_SCAN – 扫码、MANUAL – 手输、FACE_SCAN – 扫脸)
        reqBody.payMode = payMode;
        // 支付码
        reqBody.payCode = payCode;
        // 设备类型固定11
        reqBody.deviceType = deviceType;
        String requestBody = reqBody.toString();
        payResult.setRequestParams(requestBody);
        System.out.println("请求参数:\n" + requestBody);
        // 获取认证报文，timestamp为当前日期
        String timestamp = getCurrentTimestamp14();
        String authorization;
        try {
            authorization = getAuthorization(appid,appkey,timestamp,"nonce",reqBody.toString());
            payResult.setAuthorization(authorization);
            System.out.println("认证报文:\n"+authorization);
        }catch (Exception e){
            throw new RuntimeException("获取认证报文失败，请检查！");
        }
        // 发送http请求，并解析返回信息
        String response = request(url,authorization,reqBody.toString());
        payResult.setResponse(response);
        System.out.println("响应参数:\n"+response);
        PayResponse payResponse = JSONUtil.toBean(response, PayResponse.class);
        payResult.setPayResponse(payResponse);
        return payResult;
    }

    /**
     * 获取14位时间戳 (yyyyMMddHHmmss)
     */
    static String getCurrentTimestamp14() {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
        sdf.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai")); // 设置为北京时区
        return sdf.format(new Date());
    }

    /**
     * 发送http请求 - 增强错误处理
     */
    static String request(String url, String authorization, String reqBody) {
        StringBuilder response = new StringBuilder();
        PrintWriter out = null;
        BufferedReader in = null;
        HttpURLConnection httpUrlConnection = null;

        try {
            URL realUrl = new URL(url);
            httpUrlConnection = (HttpURLConnection) realUrl.openConnection();

            // 设置请求参数
            httpUrlConnection.setRequestMethod("POST");
            httpUrlConnection.setRequestProperty("Content-Type", "application/json; charset=utf-8");
            httpUrlConnection.setRequestProperty("authorization", authorization);
            httpUrlConnection.setRequestProperty("User-Agent", "Mozilla/5.0");
            httpUrlConnection.setConnectTimeout(30000);
            httpUrlConnection.setReadTimeout(30000);
            httpUrlConnection.setDoOutput(true);
            httpUrlConnection.setDoInput(true);

            // 发送请求体
            out = new PrintWriter(new OutputStreamWriter(httpUrlConnection.getOutputStream(), "UTF-8"));
            out.write(reqBody);
            out.flush();

            // 获取响应状态
            int responseCode = httpUrlConnection.getResponseCode();
            System.out.println("HTTP响应码: " + responseCode);

            // 读取响应
            if (responseCode == HttpURLConnection.HTTP_OK) {
                in = new BufferedReader(new InputStreamReader(httpUrlConnection.getInputStream(), "UTF-8"));
            } else {
                // 读取错误流 - 这是关键！
                in = new BufferedReader(new InputStreamReader(httpUrlConnection.getErrorStream(), "UTF-8"));
            }

            String line;
            while ((line = in.readLine()) != null) {
                response.append(line);
            }

            // 记录错误详情
            if (responseCode != HttpURLConnection.HTTP_OK) {
                System.err.println("HTTP错误: " + responseCode);
                System.err.println("错误响应内容: " + response.toString());
            }

        } catch (Exception e) {
            System.err.println("请求异常: " + e.getMessage());
            e.printStackTrace();
            return "HTTP请求异常: " + e.getMessage();
        } finally {
            try {
                if (out != null) out.close();
                if (in != null) in.close();
                if (httpUrlConnection != null) httpUrlConnection.disconnect();
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }

        return response.toString();
    }

    /**
     * 获取签名头
     * @param appid
     * @param appkey
     * @param timestamp 格式:"yyyyMMddHHmmss"
     * @param nonce 随机字符串，
     * @param body 请求体
     * @return authorization 认证报文
     * @throws Exception
     */
    static String getAuthorization(String appid, String appkey, String timestamp, String nonce, String body) throws Exception {
        System.out.println("appid:\n"+appid);
        System.out.println("appkey:\n"+appkey);
        byte[] data = body.getBytes("utf-8");
        InputStream is = new ByteArrayInputStream(data);
        String testSH = DigestUtils.sha256Hex(is);
        String s1 = appid+timestamp+nonce+testSH;
        Mac mac = Mac.getInstance("HmacSHA256");
        mac.init(new SecretKeySpec(appkey.getBytes("utf-8"),"HmacSHA256"));
        byte[] bytes = s1.getBytes("utf-8");
        byte[] localSignature = mac.doFinal(bytes);
        String localSignatureStr = Base64.encodeBase64String(localSignature);
        return  "OPEN-BODY-SIG AppId="+"\""+appid+"\""+", Timestamp="+"\""+timestamp+"\""+", Nonce="+"\""+nonce+"\""+", Signature="+"\""+localSignatureStr+"\"";
    }

    static class PayBody {
        // 商户号
        String merchantCode;
        // 终端号
        String terminalCode;
        // 交易金额
        int transactionAmount;
        // 交易币种
        String transactionCurrencyCode;
        // 商户订单号
        String merchantOrderId;
        // 商户备注
        String merchantRemark;
        // 支付方式
        String payMode;
        // 支付码
        String payCode;
        // 设备类型
        String deviceType;
        // 终端硬件序列号
        String serialNum;
        // 加密随机因子
        String encryptRandNum;
        // 硬件序列号密文数据
        String secretText;
        // 经度
        String longitude;
        // 纬度
        String latitude;
        // 商品信息
        List<GoodsItem> goods;
        // 商户冗余信息
        String srcReserved;
        // 门店号
        String storeId;
        // 是否限制信用卡
        String limitCreditCard;
        // 操作员编号
        String operatorId;
        // 业务标识
        String bizIdentifier;
        // 商品标识
        String goodsTag;

        String toJson() {
            StringBuilder sb = new StringBuilder();
            sb.append("{");

            appendField(sb, "merchantCode", merchantCode);
            appendField(sb, "terminalCode", terminalCode);
            appendField(sb, "transactionAmount", transactionAmount, transactionAmount != 0);
            appendField(sb, "transactionCurrencyCode", transactionCurrencyCode);
            appendField(sb, "merchantOrderId", merchantOrderId);
            appendField(sb, "merchantRemark", merchantRemark);
            appendField(sb, "payMode", payMode);
            appendField(sb, "payCode", payCode);
            appendField(sb, "deviceType", deviceType);
            appendField(sb, "serialNum", serialNum);
            appendField(sb, "encryptRandNum", encryptRandNum);
            appendField(sb, "secretText", secretText);
            appendField(sb, "longitude", longitude);
            appendField(sb, "latitude", latitude);

            // 处理商品列表
            if (goods != null && !goods.isEmpty()) {
                if (sb.charAt(sb.length() - 1) != '{') {
                    sb.append(",");
                }
                sb.append("\"goods\":[");
                for (int i = 0; i < goods.size(); i++) {
                    if (i > 0) {
                        sb.append(",");
                    }
                    sb.append(goods.get(i).toJson());
                }
                sb.append("]");
            }

            appendField(sb, "srcReserved", srcReserved);
            appendField(sb, "storeId", storeId);
            appendField(sb, "limitCreditCard", limitCreditCard);
            appendField(sb, "operatorId", operatorId);
            appendField(sb, "bizIdentifier", bizIdentifier);
            appendField(sb, "goodsTag", goodsTag);

            // 移除末尾多余的逗号
            if (sb.charAt(sb.length() - 1) == ',') {
                sb.deleteCharAt(sb.length() - 1);
            }

            sb.append("}");
            return sb.toString();
        }

        // 辅助方法：添加字符串字段
        private void appendField(StringBuilder sb, String fieldName, String value) {
            appendField(sb, fieldName, value, value != null);
        }

        // 辅助方法：添加整数字段
        private void appendField(StringBuilder sb, String fieldName, int value, boolean condition) {
            if (condition) {
                if (sb.charAt(sb.length() - 1) != '{') {
                    sb.append(",");
                }
                sb.append("\"").append(fieldName).append("\":").append(value);
            }
        }

        // 辅助方法：添加字符串字段（带条件）
        private void appendField(StringBuilder sb, String fieldName, String value, boolean condition) {
            if (condition) {
                if (sb.charAt(sb.length() - 1) != '{') {
                    sb.append(",");
                }
                sb.append("\"").append(fieldName).append("\":\"").append(escapeJson(value)).append("\"");
            }
        }

        // 转义JSON特殊字符
        private String escapeJson(String value) {
            if (value == null) return "";
            return value.replace("\\", "\\\\")
                .replace("\"", "\\\"")
                .replace("\b", "\\b")
                .replace("\f", "\\f")
                .replace("\n", "\\n")
                .replace("\r", "\\r")
                .replace("\t", "\\t");
        }

        public String toString() {
            return this.toJson();
        }

        static class GoodsItem {
            // 商品ID
            String goodsId;
            // 商品名称
            String goodsName;
            // 商品数量
            String quantity;
            // 商品单价（分）
            String price;
            // 商品分类
            String goodsCategory;
            // 商品说明
            String body;
            // 商品折扣
            String discount;

            String toJson() {
                StringBuilder sb = new StringBuilder();
                sb.append("{");

                appendGoodsField(sb, "goodsId", goodsId);
                appendGoodsField(sb, "goodsName", goodsName);
                appendGoodsField(sb, "quantity", quantity);
                appendGoodsField(sb, "price", price);
                appendGoodsField(sb, "goodsCategory", goodsCategory);
                appendGoodsField(sb, "body", body);
                appendGoodsField(sb, "discount", discount);

                // 移除末尾多余的逗号
                if (sb.charAt(sb.length() - 1) == ',') {
                    sb.deleteCharAt(sb.length() - 1);
                }

                sb.append("}");
                return sb.toString();
            }

            private void appendGoodsField(StringBuilder sb, String fieldName, String value) {
                if (value != null) {
                    if (sb.charAt(sb.length() - 1) != '{') {
                        sb.append(",");
                    }
                    sb.append("\"").append(fieldName).append("\":\"").append(escapeJson(value)).append("\"");
                }
            }

            // 转义JSON特殊字符
            private String escapeJson(String value) {
                if (value == null) return "";
                return value.replace("\\", "\\\\")
                    .replace("\"", "\\\"")
                    .replace("\b", "\\b")
                    .replace("\f", "\\f")
                    .replace("\n", "\\n")
                    .replace("\r", "\\r")
                    .replace("\t", "\\t");
            }

            public String toString() {
                return this.toJson();
            }
        }
    }
}
