package com.jeesharp.business.modules.pay.combine.support;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.jeesharp.business.modules.pay.combine.dto.PayCombineCloseRequestDTO;
import com.jeesharp.business.modules.pay.combine.dto.PayCombineJsapiRequestDTO;
import com.jeesharp.business.modules.pay.combine.dto.PayCombineSubOrderDTO;
import com.jeesharp.business.modules.pay.combine.dto.PayCombineTransactionResultDTO;
import com.jeesharp.business.modules.pay.config.weixin.PayWechatCredentialSupport;
import com.jeesharp.business.modules.pay.config.weixin.PayWechatWxPayClientHolder;
import com.jeesharp.business.modules.pay.core.IJPayHttpResponse;
import com.jeesharp.business.modules.pay.core.enums.RequestMethod;
import com.jeesharp.business.modules.pay.paychannelconfig.support.PayChannelTenantSupport;
import com.jeesharp.business.modules.pay.paysubmerchantapplyment.support.PayWechatInterfaceLogRecorder;
import com.jeesharp.business.modules.pay.weixin.support.PayWechatInterfaceLogScenario;
import com.jeesharp.business.modules.pay.paywechatconfig.entity.PayWechatConfig;
import com.jeesharp.business.modules.pay.wxpay.WxPayApi;
import com.jeesharp.business.modules.pay.wxpay.enums.WxApiType;
import com.jeesharp.business.modules.pay.wxpay.enums.WxDomain;
import com.jeesharp.framework.log.api.entity.InterfaceLogEntity;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.util.List;

/**
 * 微信合单支付 V3 客户端（JSAPI 下单 / 查询 / 关单），供阶段 5 partner 合单链路调用。
 *
 * @author jeesharp
 * @since 2026-06-12
 */
@Component
@RequiredArgsConstructor
public class PayWechatCombineTransactionClient {

    private final PayWechatWxPayClientHolder payWechatWxPayClientHolder;
    private final PayWechatInterfaceLogRecorder interfaceLogRecorder;

    public PayCombineTransactionResultDTO jsapiPrepay(PayCombineJsapiRequestDTO request) {
        try {
            PayWechatConfig config = resolveConfig(request.getPlatformTenantId());
            JSONObject body = buildJsapiBody(config, request);
            JSONObject wxResp = executeLogged(
                    PayWechatInterfaceLogScenario.COMBINE_TRANSACTIONS_JSAPI,
                    RequestMethod.POST,
                    WxApiType.COMBINE_TRANSACTIONS_JS.getType(),
                    body.toString(),
                    request.getCombineOutTradeNo(),
                    request.getPlatformTenantId(),
                    "合单 JSAPI 下单失败",
                    config);
            return toJsapiResult(wxResp, body.toString());
        } catch (Exception ex) {
            return fail(ex.getMessage());
        }
    }

    public PayCombineTransactionResultDTO queryByOutTradeNo(String platformTenantId, String combineOutTradeNo) {
        try {
            PayWechatConfig config = resolveConfig(platformTenantId);
            String suffix = String.format(WxApiType.COMBINE_TRANSACTIONS_QUERY.getType(), combineOutTradeNo);
            JSONObject wxResp = executeLogged(
                    PayWechatInterfaceLogScenario.COMBINE_TRANSACTIONS_QUERY,
                    RequestMethod.GET,
                    suffix,
                    null,
                    combineOutTradeNo,
                    platformTenantId,
                    "合单查询失败",
                    config);
            PayCombineTransactionResultDTO result = new PayCombineTransactionResultDTO();
            result.setCombineState(wxResp.getStr("combine_state"));
            result.setSuccess(true);
            result.setRawResponse(wxResp.toString());
            return result;
        } catch (Exception ex) {
            return fail(ex.getMessage());
        }
    }

    public PayCombineTransactionResultDTO close(PayCombineCloseRequestDTO request) {
        try {
            PayWechatConfig config = resolveConfig(request.getPlatformTenantId());
            JSONObject body = new JSONObject();
            body.set("combine_appid", StrUtil.blankToDefault(request.getCombineAppId(), config.getAppId()));
            body.set("combine_mchid", StrUtil.blankToDefault(request.getCombineMchId(), config.getMchId()));
            String suffix = String.format(WxApiType.COMBINE_TRANSACTIONS_CLOSE.getType(), request.getCombineOutTradeNo());
            JSONObject wxResp = executeLogged(
                    PayWechatInterfaceLogScenario.COMBINE_TRANSACTIONS_CLOSE,
                    RequestMethod.POST,
                    suffix,
                    body.toString(),
                    request.getCombineOutTradeNo(),
                    request.getPlatformTenantId(),
                    "合单关单失败",
                    config);
            PayCombineTransactionResultDTO result = new PayCombineTransactionResultDTO();
            result.setSuccess(true);
            result.setRawResponse(JSONUtil.createObj().set("request", body).set("response", wxResp).toString());
            return result;
        } catch (Exception ex) {
            return fail(ex.getMessage());
        }
    }

    private static JSONObject buildJsapiBody(PayWechatConfig config, PayCombineJsapiRequestDTO request) {
        JSONObject body = new JSONObject();
        body.set("combine_appid", config.getAppId());
        body.set("combine_mchid", config.getMchId());
        body.set("combine_out_trade_no", request.getCombineOutTradeNo());
        if (StrUtil.isNotBlank(request.getNotifyUrl())) {
            body.set("notify_url", request.getNotifyUrl());
        }
        if (StrUtil.isNotBlank(request.getTimeExpire())) {
            body.set("time_expire", request.getTimeExpire());
        }
        JSONObject payer = new JSONObject();
        payer.set("openid", request.getPayerOpenId());
        body.set("combine_payer_info", payer);
        JSONArray subOrders = new JSONArray();
        List<PayCombineSubOrderDTO> orders = request.getSubOrders();
        if (orders != null) {
            for (PayCombineSubOrderDTO sub : orders) {
                if (sub == null) {
                    continue;
                }
                JSONObject item = new JSONObject();
                item.set("mchid", StrUtil.blankToDefault(sub.getMchId(), config.getMchId()));
                item.set("sub_mchid", sub.getSubMchId());
                item.set("out_trade_no", sub.getOutTradeNo());
                item.set("description", StrUtil.blankToDefault(sub.getDescription(), "合单子单"));
                JSONObject amount = new JSONObject();
                amount.set("total_amount", sub.getTotalAmountFen());
                amount.set("currency", "CNY");
                item.set("amount", amount);
                if (StrUtil.isNotBlank(sub.getAttach())) {
                    item.set("attach", sub.getAttach());
                }
                if (Boolean.TRUE.equals(sub.getProfitSharing()) || sub.getSubsidyAmountFen() != null) {
                    JSONObject settleInfo = new JSONObject();
                    if (Boolean.TRUE.equals(sub.getProfitSharing())) {
                        settleInfo.set("profit_sharing", true);
                    }
                    if (sub.getSubsidyAmountFen() != null && sub.getSubsidyAmountFen() > 0) {
                        settleInfo.set("subsidy_amount", sub.getSubsidyAmountFen());
                    }
                    item.set("settle_info", settleInfo);
                }
                subOrders.add(item);
            }
        }
        body.set("sub_orders", subOrders);
        return body;
    }

    private PayWechatConfig resolveConfig(String platformTenantId) {
        String tenantId = StrUtil.blankToDefault(platformTenantId, PayChannelTenantSupport.PLATFORM_TENANT_ID);
        PayWechatConfig config = payWechatWxPayClientHolder.requireActiveConfig(tenantId);
        if (!PayWechatCredentialSupport.isWxEnabled(config)) {
            throw new IllegalStateException("未启用真实微信 V3：请在后台开启「调用真实微信」");
        }
        return config;
    }

    private JSONObject executeLogged(
            String scenarioType,
            RequestMethod method,
            String urlSuffix,
            String requestBody,
            String businessId,
            String tenantId,
            String action,
            PayWechatConfig config) throws Exception {
        String requestUrl = WxDomain.CHINA.getType() + urlSuffix;
        long startMs = System.currentTimeMillis();
        InterfaceLogEntity logEntity = interfaceLogRecorder.begin(
                scenarioType, method.name(), requestUrl, requestBody, businessId,
                "configTenantId=" + StrUtil.blankToDefault(tenantId, ""));
        try {
            IJPayHttpResponse response = method == RequestMethod.GET
                    ? v3Get(config, urlSuffix)
                    : v3Post(config, urlSuffix, StrUtil.blankToDefault(requestBody, ""));
            int status = response != null ? response.getStatus() : 0;
            if (status >= 200 && status < 300) {
                interfaceLogRecorder.completeSuccess(logEntity, response, startMs);
            } else {
                interfaceLogRecorder.completeHttpFail(logEntity, response, startMs, buildHttpErrorMessage(response, action));
            }
            JSONObject result = parseResponse(response, action);
            interfaceLogRecorder.saveAsync(logEntity);
            return result;
        } catch (Exception e) {
            interfaceLogRecorder.completeError(logEntity, startMs, e.getMessage());
            interfaceLogRecorder.saveAsync(logEntity);
            throw e;
        }
    }

    private static PayCombineTransactionResultDTO toJsapiResult(JSONObject wxResp, String requestBody) {
        PayCombineTransactionResultDTO result = new PayCombineTransactionResultDTO();
        result.setPrepayId(wxResp.getStr("prepay_id"));
        result.setSuccess(StrUtil.isNotBlank(result.getPrepayId()));
        result.setRawResponse(JSONUtil.createObj().set("request", requestBody).set("response", wxResp).toString());
        return result;
    }

    private static PayCombineTransactionResultDTO fail(String message) {
        PayCombineTransactionResultDTO result = new PayCombineTransactionResultDTO();
        result.setSuccess(false);
        result.setErrorMsg(message);
        return result;
    }

    private static String buildHttpErrorMessage(IJPayHttpResponse response, String action) {
        if (response == null) {
            return action + "：无响应";
        }
        String body = response.getBody();
        if (StringUtils.hasText(body) && JSONUtil.isTypeJSON(body)) {
            JSONObject err = JSONUtil.parseObj(body);
            String msg = err.getStr("message", body);
            if (err.containsKey("code")) {
                return err.getStr("code") + ": " + msg;
            }
            return msg;
        }
        return action + "（HTTP " + response.getStatus() + "）";
    }

    private IJPayHttpResponse v3Post(PayWechatConfig config, String urlSuffix, String body) throws Exception {
        return WxPayApi.v3(RequestMethod.POST, WxDomain.CHINA.getType(), urlSuffix,
                config.getMchId(),
                PayWechatCredentialSupport.resolveMerchantSerialNo(config),
                config.getPublicKeyId(),
                config.getCertPrivateKeyPath().trim(),
                body);
    }

    private IJPayHttpResponse v3Get(PayWechatConfig config, String urlSuffix) throws Exception {
        return WxPayApi.v3(RequestMethod.GET, WxDomain.CHINA.getType(), urlSuffix,
                config.getMchId(),
                PayWechatCredentialSupport.resolveMerchantSerialNo(config),
                config.getPublicKeyId(),
                config.getCertPrivateKeyPath().trim(),
                "");
    }

    private static JSONObject parseResponse(IJPayHttpResponse response, String action) {
        if (response == null) {
            throw new IllegalStateException(action + "：无响应");
        }
        int status = response.getStatus();
        String body = response.getBody();
        if (status >= 200 && status < 300) {
            return StringUtils.hasText(body) ? JSONUtil.parseObj(body) : new JSONObject();
        }
        String msg = body;
        if (StringUtils.hasText(body) && JSONUtil.isTypeJSON(body)) {
            JSONObject err = JSONUtil.parseObj(body);
            msg = err.getStr("message", body);
            if (err.containsKey("code")) {
                msg = err.getStr("code") + ": " + msg;
            }
        }
        throw new IllegalStateException(action + "（HTTP " + status + "）：" + msg);
    }
}
