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

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;

import java.nio.charset.StandardCharsets;

/**
 * 微信支付 V3 {@code attach} 字段规约：微信限制 UTF-8 最多 128 字节。
 *
 * @author jeesharp
 * @since 2026-05-25
 */
public final class WechatPayAttachSupport {

    /** 微信附加数据最大字节数（V3 统一下单） */
    public static final int MAX_ATTACH_BYTES = 128;

    private WechatPayAttachSupport() {
    }

    /**
     * 财务主单链路 attach：仅保留回调必需字段，禁止带入 tradeOrderId 等大 JSON。
     *
     * @param attachBizType 回调路由用短码：mall / hotel / scenic
     * @param bizOrderId    业务订单主键
     * @param payOrderId    财务支付单主键 {@code biz_finance_pay_order.id}
     * @param memberId      会员主键（超长时自动省略）
     */
    public static String buildTravelAttach(String attachBizType, String bizOrderId, String payOrderId, String memberId) {
        JSONObject obj = JSONUtil.createObj();
        if (StrUtil.isNotBlank(attachBizType)) {
            obj.set("bizType", attachBizType.trim());
        }
        if (StrUtil.isNotBlank(bizOrderId)) {
            obj.set("orderId", bizOrderId.trim());
        }
        if (StrUtil.isNotBlank(payOrderId)) {
            obj.set("payOrderId", payOrderId.trim());
        }
        String json = obj.toString();
        if (StrUtil.isNotBlank(memberId)) {
            JSONObject withMember = JSONUtil.parseObj(json);
            withMember.set("memberId", memberId.trim());
            json = shrinkToMaxBytes(withMember);
        }
        return ensureWithinLimit(json);
    }

    /**
     * 将 {@link com.jeesharp.business.modules.finance.paymentrecord.enums.PayRecordBizSceneEnum} 编码映射为 attach 短 bizType。
     */
    public static String resolveAttachBizType(String bizSceneCode) {
        if (StrUtil.isBlank(bizSceneCode)) {
            return "";
        }
        return switch (bizSceneCode.trim()) {
            case "MALL_ORDER" -> "mall";
            case "HOTEL_BOOKING" -> "hotel";
            case "SCENIC_TICKET" -> "scenic";
            case "FOOD_MEAL_ORDER" -> "foodMeal";
            case "FOOD_COMPANY_MEAL_ORDER" -> "companyMeal";
            case "FOOD_ORDER" -> "food";
            case "STUDY_ORDER" -> "study";
            case "YEAR_CARD" -> "yearcard";
            case "LEISURE_BOOKING" -> "leisure";
            case "VILLAGE_BOOKING" -> "village";
            case "GUIDE_SERVICE" -> "guide";
            case "CULTURE_ENROLL" -> "culture";
            default -> bizSceneCode.length() > 32 ? bizSceneCode.substring(0, 32) : bizSceneCode;
        };
    }

    /**
     * 下单前校验；超长时尝试仅保留 bizType、orderId、payOrderId、memberId 后重试。
     */
    public static String ensureWithinLimit(String attach) {
        if (StrUtil.isBlank(attach)) {
            return "";
        }
        String trimmed = attach.trim();
        if (utf8ByteLength(trimmed) <= MAX_ATTACH_BYTES) {
            return trimmed;
        }
        if (JSONUtil.isTypeJSON(trimmed)) {
            JSONObject src = JSONUtil.parseObj(trimmed);
            JSONObject compact = JSONUtil.createObj();
            copyIfPresent(src, compact, "bizType");
            copyIfPresent(src, compact, "orderId");
            copyIfPresent(src, compact, "payOrderId");
            copyIfPresent(src, compact, "memberId");
            String shrunk = shrinkToMaxBytes(compact);
            if (utf8ByteLength(shrunk) <= MAX_ATTACH_BYTES) {
                return shrunk;
            }
        }
        throw new IllegalArgumentException(
            "微信 attach 超过 " + MAX_ATTACH_BYTES + " 字节（当前 "
                + utf8ByteLength(trimmed) + "），请缩短业务单号或减少附加字段");
    }

    private static String shrinkToMaxBytes(JSONObject obj) {
        if (utf8ByteLength(obj.toString()) <= MAX_ATTACH_BYTES) {
            return obj.toString();
        }
        if (obj.containsKey("memberId")) {
            obj.remove("memberId");
            if (utf8ByteLength(obj.toString()) <= MAX_ATTACH_BYTES) {
                return obj.toString();
            }
        }
        if (obj.containsKey("bizType")) {
            obj.remove("bizType");
            if (utf8ByteLength(obj.toString()) <= MAX_ATTACH_BYTES) {
                return obj.toString();
            }
        }
        return obj.toString();
    }

    private static void copyIfPresent(JSONObject src, JSONObject target, String key) {
        if (src.containsKey(key) && StrUtil.isNotBlank(src.getStr(key))) {
            target.set(key, src.getStr(key).trim());
        }
    }

    public static int utf8ByteLength(String value) {
        return value.getBytes(StandardCharsets.UTF_8).length;
    }
}
