package com.liquidnet.common.third.sqb.biz;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.liquidnet.common.third.sqb.config.SqbConfig;
import com.liquidnet.common.third.sqb.param.callback.CallbackParams;
import com.liquidnet.common.third.sqb.param.request.*;
import com.liquidnet.common.third.sqb.param.response.*;
import com.liquidnet.common.third.sqb.param.response.data.*;
import com.liquidnet.commons.lang.util.JsonUtils;
import com.liquidnet.commons.lang.util.MD5Utils;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * 收钱吧对接公共工具类
 *
 * @author liquidnet
 */
@Slf4j
@Component
public class SqbBiz {

    @Getter
    private final SqbConfig sqbConfig;
    private final CommonRequest.Seller cachedSeller;

    public SqbBiz(SqbConfig properties) {
        this.sqbConfig = properties;
        if (this.sqbConfig == null) {
            throw new IllegalArgumentException("找不到收钱吧应用配置");
        }
        // 商户信息来自配置，整个生命周期内不变，提前构建并缓存
        this.cachedSeller = new CommonRequest.Seller();
        this.cachedSeller.setMerchantId(properties.getMerchantId());
        this.cachedSeller.setMerchantUserId(properties.getMerchantUserId());
        this.cachedSeller.setRole(properties.getRole());
    }

    private static final ObjectMapper objectMapper;
    private static final RestTemplate restTemplate;

    static {
        // 1. 初始化 RestTemplate 并强制设置 String 转换器为 UTF-8
        restTemplate = new RestTemplate();
        restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));

        // 2. 初始化紧凑型 ObjectMapper (签名专用：无空格、无换行、不含空字段)
        objectMapper = new ObjectMapper();
        objectMapper.configure(SerializationFeature.INDENT_OUTPUT, false);
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
    }

    /**
     * 创建结算明细
     *
     * @param request 创建结算明细请求参数
     * @return 收钱吧响应结果；调用失败时返回 null
     */
    public SettlementCreateData createSettlement(SettlementCreateRequest request) {
        return executeShouQianBaRequest("/optimus/core/order/createCheckoutItems", "创建结算明细", request, SettlementCreateResponse.class);
    }

    /**
     * 创建结算明细
     *
     * @param mallSn        商城ID
     * @param signature     商城密钥
     * @param userId        正在用户ID
     * @param checkoutItems 商品
     * @return
     */
    public SettlementCreateData createSettlement(String mallSn, String signature, String userId, List<SettlementCreateRequest.CheckoutItem> checkoutItems) {
        return createSettlement(mallSn, signature, userId, checkoutItems, null);
    }

    /**
     * @param totalAmountFen 纯金额结算时传总金额（分）；商品结算(type=1 的 checkoutItems) 须传 null，勿与 checkoutItems 同时带 amount
     */
    public SettlementCreateData createSettlement(String mallSn, String signature, String userId,
                                                 List<SettlementCreateRequest.CheckoutItem> checkoutItems,
                                                 Long totalAmountFen) {
        SettlementCreateRequest request = new SettlementCreateRequest();
        request.setAppid(sqbConfig.getAppId());
        request.setMallID(buildMall(mallSn, signature));
        request.setSeller(cachedSeller);
        request.setBuyer(buildBuyer(userId));
        request.setCheckoutItems(checkoutItems);
        if (totalAmountFen != null) {
            request.setAmount(totalAmountFen);
        }
        return createSettlement(request);
    }

    /**
     * 创建订单
     */
    public OrderCreateData createOrder(OrderCreateRequest request) {
        return executeShouQianBaRequest("/optimus/core/order/placeOrder", "创建订单", request, OrderCreateResponse.class);
    }

    /**
     * 创建订单
     *
     * @param mallSn          商城ID
     * @param signature       商城密钥
     * @param checkoutItemsId 创建结算明细 返回的 结算项ID
     * @param userId          正在用户ID
     * @param requestId       请求ID
     * @param subject         标题
     * @return
     */
    public OrderCreateData createOrder(String mallSn, String signature, String checkoutItemsId, String userId, String requestId, String subject) {
        OrderCreateRequest orderCreateRequest = new OrderCreateRequest();
        orderCreateRequest.setAppid(sqbConfig.getAppId());
        orderCreateRequest.setMallID(buildMall(mallSn, signature));
        orderCreateRequest.setSeller(cachedSeller);
        orderCreateRequest.setCheckoutItemsId(checkoutItemsId);
        orderCreateRequest.setBuyer(buildBuyer(userId));
        orderCreateRequest.setRequestId(requestId);
        orderCreateRequest.setSubject(subject);
        return createOrder(orderCreateRequest);
    }

    /**
     * 创建收单
     */
    public AcquiringCreateData createAcquiring(AcquiringCreateRequest request) {
        return executeShouQianBaRequest("/optimus/core/order/createAcquiring", "创建收单环节", request, AcquiringCreateResponse.class);
    }

    /**
     * 查询收银台
     */
    public CashierQueryData queryCashier(CashierQueryRequest request) {
        return executeShouQianBaRequest("/optimus/core/cashier/queryCashierPage", "查询收银台", request, CashierQueryResponse.class);
    }

    /**
     * 查询收银台
     *
     * @param acquiringSn        收单号
     * @param acquiringSignature 收单密钥
     * @param userId             正在用户ID
     * @return
     */
    public CashierQueryData queryCashier(String acquiringSn, String acquiringSignature, String userId) {
        CashierQueryRequest.PaymentEnv paymentEnv = new CashierQueryRequest.PaymentEnv();
        paymentEnv.setClient("wechat");

        CashierQueryRequest cashierQueryRequest = new CashierQueryRequest();
        cashierQueryRequest.setAppid(sqbConfig.getAppId());
        cashierQueryRequest.setSeller(cachedSeller);
        cashierQueryRequest.setPaymentMode(4);
        cashierQueryRequest.setPaymentEnv(paymentEnv);
        cashierQueryRequest.setPayer(new CommonRequest.Payer(userId));
        cashierQueryRequest.setAcquiringInfo(new CommonRequest.Acquiring(acquiringSn, acquiringSignature));

        return queryCashier(cashierQueryRequest);
    }

    /**
     * 创建微信预支付订单
     */
    public CreateWechatPrepayOrderData createWechatPrepayOrder(CreateWechatPrepayOrderRequest request) {
        return executeShouQianBaRequest("/optimus/core/cashier/proxyPreCreatedPay", "创建微信预支付订单", request, CreateWechatPrepayOrderResponse.class);
    }

    /**
     * 创建微信预支付订单
     *
     * @param acquiringSn       收单号
     * @param signature         收单签名
     * @param userId            正在用户ID
     * @param amount            支付金额（分，字符串，如 "3" 表示 0.03 元）
     * @param requestSn         支付请求号
     * @param payTool           支付工具代码
     * @param channelExt        通道扩展参数 {"sub_appid":"wx36e68952a6"}
     * @param selectedSignature 支付工具签名
     * @param seq               序列号 上一步seq（查询收银台）
     * @return
     */
    public CreateWechatPrepayOrderData createWechatPrepayOrder(String acquiringSn,
                                                               String signature,
                                                               String userId,
                                                               String identity,
                                                               String amount,
                                                               String requestSn,
                                                               CashierQueryData.PayTool payTool,
                                                               Map<String, Object> channelExt,
                                                               String selectedSignature,
                                                               String seq) {
        CreateWechatPrepayOrderRequest orderRequest = new CreateWechatPrepayOrderRequest();
        orderRequest.setAppid(sqbConfig.getAppId());
        orderRequest.setSeller(cachedSeller);
        orderRequest.setAcquiringSn(acquiringSn);
        orderRequest.setSignature(signature);
        orderRequest.setUsingPayTools(buildUsingPayTools(requestSn, payTool, channelExt, amount, identity));
        orderRequest.setSelectedSignature(selectedSignature);
        orderRequest.setSeq(seq);
        return createWechatPrepayOrder(orderRequest);
    }

    /**
     * 查询券码
     */
    public List<CouponQueryData> queryCoupon(CouponQueryRequest request) {
        return executeShouQianBaRequest("/optimus/core/voucher/queryOrderVoucherList", "查询优惠券", request, CouponQueryResponse.class);
    }

    /**
     * 查询券码
     *
     * @param sn        订单ID
     * @param signature 订单密码
     * @return
     */
    public List<CouponQueryData> queryCoupon(String sn, String signature) {
        CouponQueryRequest couponQueryRequest = new CouponQueryRequest();
        couponQueryRequest.setAppid(sqbConfig.getAppId());
        couponQueryRequest.setSeller(cachedSeller);
        couponQueryRequest.setOrderID(new CommonRequest.OrderInfo(sn, signature));
        return queryCoupon(couponQueryRequest);
    }

    /**
     * 券码状态同步
     */
    public boolean syncCouponStatus(CouponStatusSyncRequest request) {
        if (request != null && request.getSeller() == null) {
            request.setSeller(cachedSeller);
        }
        CouponStatusSyncResponse response = executeRequestAndGetResponse("/optimus/core/voucher/modifyOrderVoucherList", "同步优惠券状态", request, CouponStatusSyncResponse.class);
        return response != null && Boolean.TRUE.equals(response.getSuccess());
    }

    /**
     * 券码状态同步
     *
     * @param redeemMerchantId      用户ID（自定义，核销用户id）
     * @param voucherNos            券号
     * @param redeemExternalOrderSn 外部单号（${AppCode}+id，AppCode需要分配）
     * @param clientSn              核销终端号（自定义）
     * @param status                券使用状态（0未核销，1已核销）
     * @return
     */
    public boolean syncCouponStatus(String redeemMerchantId, List<String> voucherNos, String redeemExternalOrderSn, String clientSn, Byte status) {
        CouponStatusSyncRequest couponStatusSyncRequest = new CouponStatusSyncRequest();
        couponStatusSyncRequest.setAppid(sqbConfig.getAppId());
        couponStatusSyncRequest.setSeller(cachedSeller);
        couponStatusSyncRequest.setVoucherNos(voucherNos);
        couponStatusSyncRequest.setRedeemSource("EXTERN");
        couponStatusSyncRequest.setRedeemExternalOrderSn(redeemExternalOrderSn);
        couponStatusSyncRequest.setRedeemMerchantId(redeemMerchantId);
        couponStatusSyncRequest.setClientSn(clientSn);
        couponStatusSyncRequest.setStatus(status);
        return syncCouponStatus(couponStatusSyncRequest);
    }

    /**
     * 券退款
     */
    public CouponRefundData refundCoupon(CouponRefundRequest request) {
        return executeShouQianBaRequest("/optimus/core/aftersale/applyRefund", "退款优惠券", request, CouponRefundResponse.class);
    }

    /**
     * @param sn           单号（前置操作后拿到）
     * @param signature    密码（前置操作后拿到）
     * @param applyAmount  退款金额（分），须与订单实付/收单金额口径一致
     * @param type         退款类型（1商品 2金额）
     * @param item
     * @param refundReason 退款原因
     * @param requestId    退款请求id（${AppCode}+id）
     * @return
     */
    public CouponRefundData refundCoupon(String sn,
                                         String signature,
                                         Long applyAmount,
                                         Byte type,
                                         List<CouponRefundRequest.RefundItem> item,
                                         String refundReason,
                                         String requestId) {
        CouponRefundRequest couponRefundRequest = new CouponRefundRequest();
        couponRefundRequest.setAppid(sqbConfig.getAppId());
        couponRefundRequest.setSeller(cachedSeller);
        couponRefundRequest.setOrderID(new CommonRequest.OrderInfo(sn, signature));
        couponRefundRequest.setRefundInfo(new CouponRefundRequest.RefundInfo(applyAmount, type, item, refundReason));
        couponRefundRequest.setRequestId(requestId);
        couponRefundRequest.setRequestSource("EXTERN");
        return refundCoupon(couponRefundRequest);
    }

    /**
     * 商城列表接口
     */
    public List<MallListQueryData> queryMallList(MallListQueryRequest request) {
        return executeShouQianBaRequest("/optimus/core/open/mall/queryMallList", "查询门店列表", request, MallListQueryResponse.class);
    }

    /**
     * 商城列表接口
     *
     * @param endCursor 结束游标 上一页的结束游标（首次查询传 null）
     * @param count     查询数量 每页返回的最大订单数
     * @return
     */
    public List<MallListQueryData> queryMallList(String endCursor, Integer count) {
        MallListQueryRequest.Filter filter = new MallListQueryRequest.Filter();
        filter.setSeller(cachedSeller);

        MallListQueryRequest.Cursor cursor = new MallListQueryRequest.Cursor();
        cursor.setCursorField("id");
        cursor.setEndCursor(endCursor);
        cursor.setCount(count);

        MallListQueryRequest.Sort sort = new MallListQueryRequest.Sort();
        sort.setSortField("id");
        sort.setSort("DESC");

        MallListQueryRequest mallListQueryRequest = new MallListQueryRequest();
        mallListQueryRequest.setAppid(sqbConfig.getAppId());
        mallListQueryRequest.setFilter(filter);
        mallListQueryRequest.setCursor(cursor);
        mallListQueryRequest.setSort(sort);
        return queryMallList(mallListQueryRequest);
    }

    /**
     * 商城商品接口
     */
    public List<MallProductsQueryData> queryMallProducts(MallProductsQueryRequest request) {
        return executeShouQianBaRequest("/optimus/core/open/mall/queryMallProducts", "查询门店商品", request, MallProductsQueryResponse.class);
    }

    /**
     * 商城商品接口
     *
     * @param mallSn    商城ID
     * @param signature 商城密码
     * @return
     */
    public List<MallProductsQueryData> queryMallProducts(String mallSn, String signature) {
        MallProductsQueryRequest request = new MallProductsQueryRequest();
        request.setAppid(sqbConfig.getAppId());
        request.setSeller(cachedSeller);
        request.setMallID(new CommonRequest.Mall(mallSn, signature));
        return queryMallProducts(request);
    }

    /**
     * 发送HTTP请求并返回Response对象
     */
    private <T extends BaseResponse<?>> T executeRequestAndGetResponse(
            String apiPath, String businessName, Object request, Class<T> responseClass) {
        final String url = sqbConfig.getBaseApi() + apiPath;
        log.info("[收钱吧] {}, 请求URL: {}, 请求参数: {}", businessName, url, JsonUtils.toJson(request));

        try {
            String requestBody = objectMapper.writeValueAsString(request);
//            log.info("request body: {}", requestBody);

            // 构建请求头（添加签名参数）
            final String sign = getSign(requestBody);
            log.info("request header: {}", sign);

            MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
            headers.add("Authorization", sign);
            headers.add("Content-Type", "application/json");
            headers.add("X-env-flag", "41508");

            // 4. 发送请求
            HttpEntity<String> entity = new HttpEntity<>(requestBody, headers);
            ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.POST, entity, String.class);

            String responseStr = responseEntity.getBody();
            log.info("[收钱吧] {}, 响应体: {}", businessName, responseStr);

            // 转换响应报文
            T response = objectMapper.readValue(responseStr, responseClass);
            if (response != null && !Boolean.TRUE.equals(response.getSuccess())) {
                log.error("[收钱吧] {}业务失败, code: {}, msg: {}", businessName,
                        response.getCode(), response.getMsg());
            }
            return response;
        } catch (Exception e) {
            log.error("[收钱吧] {}发生异常, 请求参数: {}", businessName, request, e);
            return null;
        }
    }

    /**
     * 封装收钱吧接口调用，提取公共逻辑并发起请求，默认返回Data
     */
    private <D, T extends BaseResponse<D>> D executeShouQianBaRequest(
            String apiPath, String businessName, Object request, Class<T> responseClass) {
        T response = executeRequestAndGetResponse(apiPath, businessName, request, responseClass);
        if (response != null && Boolean.TRUE.equals(response.getSuccess())) {
            return response.getData();
        }
        return null;
    }


    private String getSign(String bodyJsonStr) {
        try {
            // 签名算法: sign = MD5( CONCAT( body + appkey ) )
            String rawStr = bodyJsonStr + sqbConfig.getAppKey();
            String sign = MD5Utils.md5(rawStr);

            // 签名首部: Authorization: appid + " " + sign
            return sqbConfig.getAppId() + " " + sign;
        } catch (Exception e) {
            log.error("[收钱吧] 生成签名发生异常", e);
            return "";
        }
    }

    /**
     * 收钱吧回调推送验签
     */
    public boolean verifySignature(CallbackParams callbackParams) {
        // 签名原串 = eventId + timestamp + nonce + content（必须字符串拼接；Long+Long 会先按数字相加）
        String plaintext = String.valueOf(callbackParams.getEventId())
                + String.valueOf(callbackParams.getTimestamp())
                + callbackParams.getNonce()
                + callbackParams.getContent();
        return verifySignatureSHA256WithRSA(plaintext, callbackParams.getSignature(), sqbConfig.getPublicKey());
    }

    private CommonRequest.Mall buildMall(String mallSn, String signature) {
        CommonRequest.Mall mall = new CommonRequest.Mall();
        mall.setMallSn(mallSn);
        mall.setSignature(signature);
        return mall;
    }

    private CommonRequest.Buyer buildBuyer(String userId) {
        CommonRequest.Buyer buyer = new CommonRequest.Buyer();
        buyer.setBuyerId(userId);
        return buyer;
    }

    private List<CreateWechatPrepayOrderRequest.UsingPayTool> buildUsingPayTools(String requestSn,
                                                                                 CashierQueryData.PayTool payTool,
                                                                                 Map<String, Object> channelExt,
                                                                                 String amount,
                                                                                 String identity) {
        CreateWechatPrepayOrderRequest.UsingPayTool tool = new CreateWechatPrepayOrderRequest.UsingPayTool();
        tool.setId(payTool.getId());
        tool.setPayTool(Integer.valueOf(payTool.getCode()));
        tool.setPayMode(4); // TODO 需要确认
        tool.setAmount(amount);
        tool.setIdentity(identity);
        tool.setAmountComposition(payTool.getAmountComposition());
        tool.setRequestSn(requestSn);
        tool.setChannelExt(channelExt);
        return Collections.singletonList(tool);
    }


    private boolean verifySignatureSHA256WithRSA(String plaintext, String inputSignature, String pubKey) {
        return verifySignatureSHA256WithRSA(plaintext.getBytes(StandardCharsets.UTF_8), inputSignature, pubKey);
    }

    private boolean verifySignatureSHA256WithRSA(byte[] plaintextByte, String inputSignature, String pubKey) {
        try {
            X509EncodedKeySpec bobPubKeySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(pubKey));
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            PublicKey publicKey = keyFactory.generatePublic(bobPubKeySpec);
            byte[] signed = Base64.getDecoder().decode(inputSignature);
            Signature signature = Signature.getInstance("SHA256WithRSA");
            signature.initVerify(publicKey);
            signature.update(plaintextByte);
            return signature.verify(signed);
        } catch (Exception e) {
            log.error("[收钱吧] 验签异常, signature={}, pubKey={}", inputSignature, pubKey, e);
            return false;
        }
    }
}
