package com.ruoyi.ai.service.impl;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ruoyi.ai.domain.*;
import com.ruoyi.ai.service.*;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.AiUser;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.http.OkHttpUtils;
import com.ruoyi.common.utils.sign.Md5Utils;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.TreeMap;
import java.util.UUID;

/**
 * VM支付服务实现
 *
 * @author system
 * @date 2025-01-XX
 */
@Service
@Slf4j
public class VmService implements IVmService {

    private final ObjectMapper objectMapper = new ObjectMapper().setSerializationInclusion(JsonInclude.Include.NON_NULL);

    @Value("${vm.url:http://payment-api.togame.top}")
    private String url;

    @Value("${vm.mchNo}")
    private String mchNo;

    @Value("${vm.appId}")
    private String appId;

    @Value("${vm.secret}")
    private String secret;

    @Value("${vm.notifyUrl}")
    private String notifyUrl;

    @Value("${vm.wayCode:BUZHI_VM}")
    private String wayCode;

    @Value("${vm.currency:USD}")
    private String currency;

    @Autowired
    private IAiRechargeService aiRechargeService;

    @Autowired
    private IAiUserService aiUserService;

    @Autowired
    private IAiRechargeGiftGearService aiRechargeGiftGearService;

    @Autowired
    private IAiRechargeGiftService aiRechargeGiftService;

    @Autowired
    private IExchangeRateService exchangeRateService;

    @Override
    public PayResVO vmPay(Long gearId, VmCardInfo vmCardInfo, String clientIp) throws Exception {
        // 充值挡位查询
        AiRechargeGiftGear aiRechargeGiftGear = aiRechargeGiftGearService.selectAiRechargeGiftGearById(gearId);
        if (aiRechargeGiftGear == null) {
            throw new ServiceException("The gear position does not exist.", -1);
        }

        BigDecimal amount = aiRechargeGiftGear.getRechargeAmount();
        // 使用汇率服务转换金额（从USD转换为目标货币，默认USD）
        // 注意：VM支付使用USD作为基础货币
        if (!"USD".equalsIgnoreCase(currency)) {
            amount = exchangeRateService.convertAmount(amount, "USD", currency);
        }

        // 生成订单号
        String uuid = UUID.randomUUID().toString().replaceAll("-", "").substring(0, 8);
        String dateTime = new SimpleDateFormat("yyyyMMdd").format(new Date());
        String orderNo = dateTime + "vm" + uuid;

        // 金额转换为分（整数）
        int amountInCents = amount.multiply(new BigDecimal(100)).intValue();

        // 构建extParam（VM卡信息）
        String extParam = null;
        if (vmCardInfo != null) {
            extParam = objectMapper.writeValueAsString(vmCardInfo);
        }

        // 构建统一下单请求
        VmUnifiedOrderReq req = new VmUnifiedOrderReq();
        req.setMchNo(mchNo);
        req.setAppId(appId);
        req.setMchOrderNo(orderNo);
        req.setWayCode(wayCode);
        req.setAmount(amountInCents);
        req.setCurrency(currency.toLowerCase());
        req.setSubject("游戏充值");
        req.setBody("游戏充值");
        req.setNotifyUrl(notifyUrl + "/api/pay/vm-callBack");
        req.setExpiredTime(7200); // 默认2小时
        req.setExtParam(extParam);
        req.setReqTime(System.currentTimeMillis());
        req.setVersion("1.0");
        req.setSignType("MD5");
        // 设置客户端IP
        if (clientIp != null && !clientIp.trim().isEmpty()) {
            req.setClientIp(clientIp);
            log.debug("VM支付设置客户端IP: {}", clientIp);
        } else {
            log.warn("VM支付客户端IP为空，订单号: {}", orderNo);
        }

        // 生成签名
        String sign = generateSign(req);
        req.setSign(sign);

        // 构建请求体JSON
        String jsonBody = objectMapper.writeValueAsString(req);
        log.info("VM支付请求参数: {}", jsonBody);

        // 构建POST请求
        Request request = new Request.Builder()
                .url(url + "/api/pay/unifiedOrder")
                .header("Content-Type", "application/json")
                .post(RequestBody.create(MediaType.parse("application/json"), jsonBody))
                .build();

        // 发送请求
        Response response = OkHttpUtils.newCall(request).execute();
        ResponseBody body = response.body();

        if (body == null) {
            log.error("VM支付请求的响应异常");
            throw new Exception("vm pay response body is null");
        }

        String responseBody = body.string();
        log.info("VM支付请求响应: {}", responseBody);

        VmUnifiedOrderRes res = objectMapper.readValue(responseBody, VmUnifiedOrderRes.class);

        // 检查响应code，0表示成功
        if (res.getCode() == null || res.getCode() != 0) {
            String msg = res.getMsg();
            log.error("VM支付请求失败，code: {}, msg: {}", res.getCode(), msg);
            throw new ServiceException(msg != null ? msg : "支付请求失败", res.getCode() != null ? res.getCode() : -1);
        }

        // 检查返回数据
        if (res.getData() == null) {
            log.error("VM支付返回的data为空，订单号: {}", orderNo);
            throw new ServiceException("支付返回的数据为空", -1);
        }

        VmUnifiedOrderRes.VmUnifiedOrderData data = res.getData();
        
        // 记录关键字段信息
        String payOrderId = data.getPayOrderId();
        String mchOrderNo = data.getMchOrderNo();
        log.info("VM支付返回数据，商户订单号: {}, 支付订单号: {}, 订单状态: {}, payDataType: {}", 
                mchOrderNo, payOrderId, data.getOrderState(), data.getPayDataType());
        
        // 检查订单状态
        Integer orderState = data.getOrderState();
        if (orderState == null) {
            log.error("VM支付返回的订单状态为空，订单号: {}", orderNo);
            throw new ServiceException("支付返回的订单状态为空", -1);
        }

        // 订单状态说明：
        // 0-订单生成, 1-支付中, 2-支付成功, 3-支付失败, 4-已撤销, 5-已退款, 6-订单关闭
        String payUrl = null;
        String payDataType = data.getPayDataType();
        String payData = data.getPayData();
        
        // 如果订单状态是支付失败（3），抛出异常
        if (orderState == 3) {
            String errCode = data.getErrCode();
            String errMsg = data.getErrMsg();
            String errorMessage = "支付失败";
            if (errMsg != null && !errMsg.trim().isEmpty()) {
                errorMessage = errMsg;
            } else if (errCode != null && !errCode.trim().isEmpty()) {
                errorMessage = String.format("支付失败，错误码: %s", errCode);
            }
            log.error("VM支付失败，订单号: {}, 订单状态: {}, 错误码: {}, 错误消息: {}", 
                    orderNo, orderState, errCode, errMsg);
            throw new ServiceException(errorMessage, -1);
        }
        
        // 如果订单状态是支付中（1），需要获取支付URL让用户去支付
        // 如果订单状态是支付成功（2），不需要payUrl（用户说"订单状态只要是支付成功就不用管订单状态"）
        if (orderState == 1) {
            // 根据payDataType获取支付URL
            if (payDataType != null && payData != null && !payData.trim().isEmpty()) {
                // payurl类型（注意：实际返回是小写payurl）：跳转链接的方式
                if ("payurl".equalsIgnoreCase(payDataType) || "payUrl".equalsIgnoreCase(payDataType)) {
                    payUrl = payData;
                    log.info("VM支付使用payData作为支付URL（payDataType={}），订单号: {}", payDataType, orderNo);
                }
                // codeUrl类型：二维码地址
                else if ("codeUrl".equalsIgnoreCase(payDataType)) {
                    payUrl = payData;
                    log.info("VM支付使用payData作为支付URL（payDataType=codeUrl），订单号: {}", orderNo);
                }
                // codeImgUrl类型：二维码图片地址
                else if ("codeImgUrl".equalsIgnoreCase(payDataType)) {
                    payUrl = payData;
                    log.info("VM支付使用payData作为支付URL（payDataType=codeImgUrl），订单号: {}", orderNo);
                }
                // 其他类型（form, wxapp, aliapp, ysfapp, none）暂不处理payUrl
                else if (!"none".equalsIgnoreCase(payDataType)) {
                    log.info("VM支付payDataType为: {}，暂不支持作为支付URL，订单号: {}", payDataType, orderNo);
                }
            }
            
            // 兼容旧版本：如果payUrl字段有值，优先使用
            if ((payUrl == null || payUrl.trim().isEmpty()) && 
                data.getPayUrl() != null && !data.getPayUrl().trim().isEmpty()) {
                payUrl = data.getPayUrl();
                log.info("VM支付使用payUrl字段，订单号: {}", orderNo);
            }
            
            // 如果仍然没有payUrl，记录警告
            if (payUrl == null || payUrl.trim().isEmpty()) {
                log.warn("VM支付返回的payUrl为空，订单状态为支付中，订单号: {}, payDataType: {}", 
                        orderNo, payDataType);
                payUrl = "";
            }
        } else if (orderState == 2) {
            // 支付成功，不需要payUrl
            log.info("VM支付订单状态为支付成功，不需要返回payUrl，订单号: {}", orderNo);
            payUrl = "";
        } else {
            // 其他状态（0-订单生成, 4-已撤销, 5-已退款, 6-订单关闭）
            log.info("VM支付订单状态: {}，订单号: {}", orderState, orderNo);
            payUrl = "";
        }

        // 创建充值管理
        AiUser userInfo = aiUserService.getUserInfo(SecurityUtils.getAiUserId());
        AiRecharge aiRecharge = new AiRecharge();
        aiRecharge.setOrderNum(orderNo);
        aiRecharge.setUserId(SecurityUtils.getAiUserId());
        aiRecharge.setAmount(amount);
        aiRecharge.setGearId(gearId);
        aiRecharge.setGearAmount(aiRechargeGiftGear.getRechargeAmount());
        aiRecharge.setSource(userInfo.getSource());
        AiRechargeGift aiRechargeGift = aiRechargeGiftService.selectAiRechargeGiftById(aiRechargeGiftGear.getRechargeId());
        aiRecharge.setPayType(aiRechargeGift.getPayType());
        aiRecharge.setPayUrl(payUrl);
        aiRechargeService.insertAiRecharge(aiRecharge);

        PayResVO payResVO = new PayResVO();
        payResVO.setOrderNo(orderNo);
        payResVO.setPayUrl(payUrl);
        return payResVO;
    }

    @Override
    @Transactional
    public String vmCallBack(VmCallBackReq req) {
        log.info("VM支付回调，订单号: {}, 状态: {}", req.getMchOrderNo(), req.getState());

        log.debug("VM支付回调请求对象: mchNo={}, appId={}, mchOrderNo={}, payOrderId={}, ifCode={}, wayCode={}, amount={}, currency={}, state={}, subject={}, body={}, createdAt={}, reqTime={}, sign={}",
                req.getMchNo(), req.getAppId(), req.getMchOrderNo(), req.getPayOrderId(), req.getIfCode(), req.getWayCode(),
                req.getAmount(), req.getCurrency(), req.getState(), req.getSubject(), req.getBody(),
                req.getCreatedAt(), req.getReqTime(), req.getSign());

        // 验证签名
        String sign = generateCallBackSign(req);
        if (!sign.equals(req.getSign())) {
            log.error("VM支付回调签名错误，订单号: {}, 期望签名: {}, 实际签名: {}",
                    req.getMchOrderNo(), sign, req.getSign());
            return "fail";
        }

        // 处理订单状态（文档字段名：state）
        // 0-订单生成, 1-支付中, 2-支付成功, 3-支付失败, 4-已撤销, 5-已退款, 6-订单关闭
        Integer state = req.getState();
        if (state == null) {
            log.warn("VM支付回调state为空，订单号: {}，返回 success 等待后续回调", req.getMchOrderNo());
            return "success";
        }

        if (state == 2) {
            log.info("VM支付成功，订单号: {}", req.getMchOrderNo());
            aiRechargeService.addRecharge(req.getMchOrderNo());
            return "success";
        } else if (state == 3) {
            log.warn("VM支付失败，订单号: {}", req.getMchOrderNo());
            return "success";
        } else {
            log.info("VM支付状态: {}, 订单号: {}", state, req.getMchOrderNo());
            return "success";
        }
    }

    /**
     * 生成签名（统一下单）
     * 第一步：将集合M内非空参数值的参数按照参数名ASCII码从小到大排序（字典序），使用URL键值对的格式拼接成字符串stringA
     * 第二步：在stringA最后拼接上key[即 StringA +"&key=" + 私钥 ] 得到stringSignTemp字符串，并对stringSignTemp进行MD5运算，再将得到的字符串所有字符转换为大写
     */
    private String generateSign(VmUnifiedOrderReq req) {
        TreeMap<String, Object> params = new TreeMap<>();
        
        // 添加所有非空参数（sign不参与签名）
        if (req.getMchNo() != null && !req.getMchNo().isEmpty()) {
            params.put("mchNo", req.getMchNo());
        }
        if (req.getAppId() != null && !req.getAppId().isEmpty()) {
            params.put("appId", req.getAppId());
        }
        if (req.getMchOrderNo() != null && !req.getMchOrderNo().isEmpty()) {
            params.put("mchOrderNo", req.getMchOrderNo());
        }
        if (req.getWayCode() != null && !req.getWayCode().isEmpty()) {
            params.put("wayCode", req.getWayCode());
        }
        if (req.getAmount() != null) {
            params.put("amount", req.getAmount());
        }
        if (req.getCurrency() != null && !req.getCurrency().isEmpty()) {
            params.put("currency", req.getCurrency());
        }
        if (req.getClientIp() != null && !req.getClientIp().isEmpty()) {
            params.put("clientIp", req.getClientIp());
        }
        if (req.getSubject() != null && !req.getSubject().isEmpty()) {
            params.put("subject", req.getSubject());
        }
        if (req.getBody() != null && !req.getBody().isEmpty()) {
            params.put("body", req.getBody());
        }
        if (req.getNotifyUrl() != null && !req.getNotifyUrl().isEmpty()) {
            params.put("notifyUrl", req.getNotifyUrl());
        }
        if (req.getExpiredTime() != null) {
            params.put("expiredTime", req.getExpiredTime());
        }
        if (req.getChannelExtra() != null && !req.getChannelExtra().isEmpty()) {
            params.put("channelExtra", req.getChannelExtra());
        }
        if (req.getExtParam() != null && !req.getExtParam().isEmpty()) {
            params.put("extParam", req.getExtParam());
        }
        if (req.getReqTime() != null) {
            params.put("reqTime", req.getReqTime());
        }
        if (req.getVersion() != null && !req.getVersion().isEmpty()) {
            params.put("version", req.getVersion());
        }
        if (req.getSignType() != null && !req.getSignType().isEmpty()) {
            params.put("signType", req.getSignType());
        }

        // 拼接参数
        StringBuilder stringA = new StringBuilder();
        for (Map.Entry<String, Object> entry : params.entrySet()) {
            if (stringA.length() > 0) {
                stringA.append("&");
            }
            stringA.append(entry.getKey()).append("=").append(entry.getValue());
        }

        // 拼接key
        String stringSignTemp = stringA.toString() + "&key=" + secret;
        log.debug("VM签名源字符串: {}", stringSignTemp);

        // MD5并转大写
        String sign = Md5Utils.hash(stringSignTemp).toUpperCase();
        return sign;
    }

    /**
     * 生成回调签名。按文档字段名：state、reqTime；sign、signType 不参与签名。
     * 参与签名的非空参数按 key 字典序排序，key=value 用 & 拼接，最后 &key=secret，MD5 后转大写。
     */
    private String generateCallBackSign(VmCallBackReq req) {
        TreeMap<String, Object> params = new TreeMap<>();

        if (req.getMchNo() != null && !req.getMchNo().isEmpty()) {
            params.put("mchNo", req.getMchNo());
        }
        if (req.getAppId() != null && !req.getAppId().isEmpty()) {
            params.put("appId", req.getAppId());
        }
        if (req.getMchOrderNo() != null && !req.getMchOrderNo().isEmpty()) {
            params.put("mchOrderNo", req.getMchOrderNo());
        }
        if (req.getPayOrderId() != null && !req.getPayOrderId().isEmpty()) {
            params.put("payOrderId", req.getPayOrderId());
        }
        if (req.getIfCode() != null && !req.getIfCode().isEmpty()) {
            params.put("ifCode", req.getIfCode());
        }
        if (req.getWayCode() != null && !req.getWayCode().isEmpty()) {
            params.put("wayCode", req.getWayCode());
        }
        if (req.getAmount() != null) {
            params.put("amount", req.getAmount());
        }
        if (req.getCurrency() != null && !req.getCurrency().isEmpty()) {
            params.put("currency", req.getCurrency());
        }
        if (req.getState() != null) {
            params.put("state", req.getState());
        }
        if (req.getClientIp() != null && !req.getClientIp().isEmpty()) {
            params.put("clientIp", req.getClientIp());
        }
        if (req.getSubject() != null && !req.getSubject().isEmpty()) {
            params.put("subject", req.getSubject());
        }
        if (req.getBody() != null && !req.getBody().isEmpty()) {
            params.put("body", req.getBody());
        }
        if (req.getChannelOrderNo() != null && !req.getChannelOrderNo().isEmpty()) {
            params.put("channelOrderNo", req.getChannelOrderNo());
        }
        if (req.getErrCode() != null && !req.getErrCode().isEmpty()) {
            params.put("errCode", req.getErrCode());
        }
        if (req.getErrMsg() != null && !req.getErrMsg().isEmpty()) {
            params.put("errMsg", req.getErrMsg());
        }
        if (req.getExtParam() != null && !req.getExtParam().isEmpty()) {
            params.put("extParam", req.getExtParam());
        }
        if (req.getCreatedAt() != null) {
            params.put("createdAt", req.getCreatedAt());
        }
        if (req.getSuccessTime() != null) {
            params.put("successTime", req.getSuccessTime());
        }
        if (req.getReqTime() != null) {
            params.put("reqTime", req.getReqTime());
        }

        StringBuilder stringA = new StringBuilder();
        for (Map.Entry<String, Object> entry : params.entrySet()) {
            if (stringA.length() > 0) {
                stringA.append("&");
            }
            stringA.append(entry.getKey()).append("=").append(entry.getValue());
        }

        String stringSignTemp = stringA.toString() + "&key=" + secret;
        log.debug("VM回调签名源字符串: {}", stringSignTemp);

        String sign = Md5Utils.hash(stringSignTemp).toUpperCase();
        log.debug("VM回调计算出的签名: {}", sign);
        return sign;
    }
}
