package com.ruoyi.applet.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.cyl.wechat.WechatPayData;
import com.cyl.wechat.WechatPayUtil;
import com.google.gson.annotations.SerializedName;
import com.ruoyi.applet.transfer.WXPayUtility;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.io.UncheckedIOException;
import java.security.PrivateKey;
import java.util.ArrayList;
import java.util.List;

/**
 * 微信退款工具类
 * 参考微信支付V3 API文档：https://pay.weixin.qq.com/doc/v3/merchant/4014931831
 */
@Slf4j
@Component
public class WechatRefundUtil {

    private static final String HOST = "https://api.mch.weixin.qq.com";
    private static final String METHOD = "POST";
    private static final String PATH = "/v3/refund/domestic/refunds";

    /**
     * 申请退款
     * @param request 退款请求参数
     * @return 退款响应结果
     */
    public RefundResponse applyRefund(RefundRequest request) {
        String uri = PATH;
        String reqBody = JSON.toJSONString(request);

        Request.Builder reqBuilder = new Request.Builder().url(HOST + uri);
        reqBuilder.addHeader("Accept", "application/json");
        reqBuilder.addHeader("Wechatpay-Serial", WechatPayData.merchantSerialNumber);
        reqBuilder.addHeader("Authorization", buildAuthorization(WechatPayData.merchantId, 
                WechatPayData.merchantSerialNumber, WechatPayData.privateKeyPath, METHOD, uri, reqBody));
        reqBuilder.addHeader("Content-Type", "application/json");
        RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), reqBody);
        reqBuilder.method(METHOD, requestBody);
        Request httpRequest = reqBuilder.build();

        log.info("微信退款请求参数: {}", reqBody);

        // 发送HTTP请求
        OkHttpClient client = new OkHttpClient.Builder().build();
        try (Response httpResponse = client.newCall(httpRequest).execute()) {
            String respBody = httpResponse.body().string();
            log.info("微信退款响应: {}", respBody);
            
            if (httpResponse.code() >= 200 && httpResponse.code() < 300) {
                // 2XX 成功，验证应答签名
                validateResponse(WechatPayData.merchantSerialNumber, WechatPayData.privateKeyPath,
                        httpResponse.headers(), respBody);

                // 从HTTP应答报文构建返回数据
                return JSON.parseObject(respBody, RefundResponse.class);
            } else {
                throw new RuntimeException("退款申请失败，状态码: " + httpResponse.code() + ", 响应: " + respBody);
            }
        } catch (IOException e) {
            throw new UncheckedIOException("发送退款请求到 " + uri + " 失败.", e);
        }
    }

    /**
     * 构建授权头
     */
    private String buildAuthorization(String mchid, String certificateSerialNo, String privateKeyPath,
                                   String method, String uri, String reqBody) {
        PrivateKey privateKey = WXPayUtility.loadPrivateKeyFromPath(privateKeyPath);
        return WXPayUtility.buildAuthorization(mchid, certificateSerialNo, privateKey, method, uri, reqBody);
    }

    /**
     * 构建签名字符串
     */
    private String buildSignatureString(String method, String uri, String reqBody) {
        // 构建微信支付V3签名字符串
        // 格式：HTTP请求方法\nURL路径\n请求时间戳\n随机串\n请求报文主体\n
        long timestamp = System.currentTimeMillis() / 1000;
        String nonce = WechatPayUtil.generateNonceStr();
        
        return method + "\n" + uri + "\n" + timestamp + "\n" + nonce + "\n" + reqBody + "\n";
    }

    /**
     * 验证响应签名
     */
    private void validateResponse(String wechatPayPublicKeyId, String wechatPayPublicKeyPath, 
                                Headers headers, String respBody) {
        // 简化实现，实际项目中应该验证微信支付响应的签名
        log.info("验证响应签名: {}", respBody);
    }

    /**
     * 退款状态枚举
     */
    public enum RefundStatus {
        @SerializedName("SUCCESS")
        SUCCESS,
        @SerializedName("CLOSED")
        CLOSED,
        @SerializedName("PROCESSING")
        PROCESSING,
        @SerializedName("ABNORMAL")
        ABNORMAL
    }

    /**
     * 账户类型枚举
     */
    public enum Account {
        @SerializedName("AVAILABLE")
        AVAILABLE,
        @SerializedName("UNAVAILABLE")
        UNAVAILABLE
    }

    /**
     * 资金账户类型枚举
     */
    public enum FundsAccount {
        @SerializedName("UNSETTLED")
        UNSETTLED,
        @SerializedName("AVAILABLE")
        AVAILABLE,
        @SerializedName("UNAVAILABLE")
        UNAVAILABLE,
        @SerializedName("OPERATION")
        OPERATION,
        @SerializedName("BASIC")
        BASIC,
        @SerializedName("ECNY_BASIC")
        ECNY_BASIC
    }

    /**
     * 退款渠道枚举
     */
    public enum Channel {
        @SerializedName("ORIGINAL")
        ORIGINAL,
        @SerializedName("BALANCE")
        BALANCE,
        @SerializedName("OTHER_BALANCE")
        OTHER_BALANCE,
        @SerializedName("OTHER_BANKCARD")
        OTHER_BANKCARD
    }

    /**
     * 退款请求参数
     */
    public static class RefundRequest {
        @SerializedName("transaction_id")
        public String transactionId;

        @SerializedName("out_trade_no")
        public String out_trade_no;

        @SerializedName("out_refund_no")
        public String out_refund_no;

        @SerializedName("reason")
        public String reason;

        @SerializedName("notify_url")
        public String notify_url;

        @SerializedName("funds_account")
        public ReqFundsAccount funds_account;

        @SerializedName("amount")
        public AmountReq amount;

        @SerializedName("goods_detail")
        public List<GoodsDetail> goods_detail;
    }

    /**
     * 退款金额请求参数
     */
    public static class AmountReq {
        @SerializedName("refund")
        public Long refund;

        @SerializedName("from")
        public List<FundsFromItem> from;

        @SerializedName("total")
        public Long total;

        @SerializedName("currency")
        public String currency;
    }

    /**
     * 资金账户类型请求枚举
     */
    public enum ReqFundsAccount {
        @SerializedName("AVAILABLE")
        AVAILABLE,
        @SerializedName("UNSETTLED")
        UNSETTLED
    }

    /**
     * 资金来源项
     */
    public static class FundsFromItem {
        @SerializedName("account")
        public Account account;

        @SerializedName("amount")
        public Long amount;
    }

    /**
     * 商品详情
     */
    public static class GoodsDetail {
        @SerializedName("merchant_goods_id")
        public String merchant_goods_id;

        @SerializedName("wechatpay_goods_id")
        public String wechatpay_goods_id;

        @SerializedName("goods_name")
        public String goods_name;

        @SerializedName("unit_price")
        public Long unit_price;

        @SerializedName("refund_amount")
        public Long refund_amount;

        @SerializedName("refund_quantity")
        public Integer refund_quantity;
    }

    /**
     * 退款响应
     */
    public static class RefundResponse {
        @SerializedName("refund_id")
        public String refundId;

        @SerializedName("out_refund_no")
        public String outRefundNo;

        @SerializedName("transaction_id")
        public String transactionId;

        @SerializedName("out_trade_no")
        public String outTradeNo;

        @SerializedName("channel")
        public Channel channel;

        @SerializedName("user_received_account")
        public String userReceivedAccount;

        @SerializedName("success_time")
        public String successTime;

        @SerializedName("create_time")
        public String createTime;

        @SerializedName("status")
        public RefundStatus status;

        @SerializedName("funds_account")
        public FundsAccount fundsAccount;

        @SerializedName("amount")
        public Amount amount;
    }

    /**
     * 退款金额
     */
    public static class Amount {
        @SerializedName("total")
        public Long total;

        @SerializedName("refund")
        public Long refund;

        @SerializedName("from")
        public List<FundsFromItem> from;

        @SerializedName("payer_total")
        public Long payerTotal;

        @SerializedName("payer_refund")
        public Long payerRefund;

        @SerializedName("settlement_refund")
        public Long settlementRefund;

        @SerializedName("settlement_total")
        public Long settlementTotal;

        @SerializedName("discount_refund")
        public Long discountRefund;

        @SerializedName("currency")
        public String currency;

        @SerializedName("refund_fee")
        public Long refundFee;
    }
} 