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

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import org.springframework.util.StringUtils;

/**
 * 将前端 applyment4sub 形草稿 JSON 转换为电商进件 {@code POST /v3/ecommerce/applyments/} 请求体。
 * <p>字段映射对齐文档 4012713017；前端草稿结构保持 applyment4sub 嵌套，提交时由本类完成扁平化与重命名。</p>
 *
 * @author jeesharp
 * @since 2026-06-12
 */
public final class PayEcommerceApplymentPayloadAssembler {

    private PayEcommerceApplymentPayloadAssembler() {
    }

    /**
     * @param draftJson     applyment4sub 形草稿 JSON
     * @param outRequestNo  业务申请编号（本地 business_code）
     */
    public static String assemble(String draftJson, String outRequestNo) {
        if (!StringUtils.hasText(draftJson) || !JSONUtil.isTypeJSON(draftJson)) {
            throw new IllegalArgumentException("进件草稿 JSON 不合法");
        }
        if (StrUtil.isBlank(outRequestNo)) {
            throw new IllegalArgumentException("业务申请编号不能为空");
        }
        JSONObject draft = JSONUtil.parseObj(draftJson);
        JSONObject root = new JSONObject();
        root.set("out_request_no", outRequestNo.trim());

        JSONObject subjectInfo = draft.getJSONObject("subject_info");
        String subjectType = subjectInfo != null ? subjectInfo.getStr("subject_type") : null;
        root.set("organization_type", mapOrganizationType(subjectType));

        if (subjectInfo != null && Boolean.TRUE.equals(subjectInfo.getBool("finance_institution"))) {
            root.set("finance_institution", true);
            copyIfPresent(root, subjectInfo, "finance_institution_info");
        } else {
            root.set("finance_institution", false);
        }

        JSONObject businessLicense = mapBusinessLicenseInfo(subjectInfo, subjectType);
        if (businessLicense != null && !businessLicense.isEmpty()) {
            root.set("business_license_info", businessLicense);
        }

        JSONObject identityInfo = subjectInfo != null ? subjectInfo.getJSONObject("identity_info") : null;
        if (identityInfo != null) {
            String idDocType = identityInfo.getStr("id_doc_type");
            if (StringUtils.hasText(idDocType)) {
                root.set("id_doc_type", mapIdDocType(idDocType));
            }
            copyIfPresent(root, identityInfo, "id_holder_type");
            copyIfPresent(root, identityInfo, "authorize_letter_copy");

            JSONObject idCard = identityInfo.getJSONObject("id_card_info");
            if (idCard != null && !idCard.isEmpty()) {
                root.set("id_card_info", mapIdCardInfo(idCard));
            }
            JSONObject idDoc = identityInfo.getJSONObject("id_doc_info");
            if (idDoc != null && !idDoc.isEmpty()) {
                root.set("id_doc_info", mapIdDocInfo(idDoc));
            }
        }

        if (subjectInfo != null && subjectInfo.containsKey("ubo_info_list")) {
            JSONArray uboSrc = subjectInfo.getJSONArray("ubo_info_list");
            if (uboSrc != null && !uboSrc.isEmpty()) {
                root.set("ubo_info_list", mapUboInfoList(uboSrc));
            }
        }

        JSONObject contactSrc = draft.getJSONObject("contact_info");
        if (contactSrc != null) {
            root.set("contact_info", mapContactInfo(contactSrc));
        }

        JSONObject bankSrc = draft.getJSONObject("bank_account_info");
        if (bankSrc != null) {
            root.set("account_info", mapAccountInfo(bankSrc));
        }

        JSONObject businessInfo = draft.getJSONObject("business_info");
        if (businessInfo != null) {
            String shortname = businessInfo.getStr("merchant_shortname");
            if (StringUtils.hasText(shortname)) {
                root.set("merchant_shortname", shortname);
            }
            JSONObject salesScene = buildSalesSceneInfo(businessInfo, shortname);
            if (salesScene != null && !salesScene.isEmpty()) {
                root.set("sales_scene_info", salesScene);
            }
        }

        JSONObject settlementSrc = draft.getJSONObject("settlement_info");
        if (settlementSrc != null) {
            root.set("settlement_info", mapSettlementInfo(settlementSrc));
            JSONArray quals = settlementSrc.getJSONArray("qualifications");
            if (quals != null && !quals.isEmpty()) {
                root.set("qualifications", quals.toString());
            }
        }

        mapAdditionFields(root, draft);

        return root.toString();
    }

    /**
     * 个体/企业营业执照或机关/事业单位/社会组织登记证书，统一映射为 {@code business_license_info}。
     */
    private static JSONObject mapBusinessLicenseInfo(JSONObject subjectInfo, String subjectType) {
        if (subjectInfo == null || subjectType == null) {
            return null;
        }
        if (isLicenseSubject(subjectType)) {
            JSONObject src = subjectInfo.getJSONObject("business_license_info");
            if (src == null || src.isEmpty()) {
                return null;
            }
            JSONObject dest = new JSONObject();
            copyLicenseMediaFields(dest, src);
            copyString(dest, src, "merchant_name");
            copyString(dest, src, "legal_person");
            copyString(dest, src, "company_address");
            setBusinessTime(dest, src.getStr("period_begin"), src.getStr("period_end"));
            return dest;
        }
        if (isCertificateSubject(subjectType)) {
            JSONObject src = subjectInfo.getJSONObject("certificate_info");
            if (src == null || src.isEmpty()) {
                return null;
            }
            JSONObject dest = new JSONObject();
            String copy = firstNonBlank(src.getStr("cert_copy"), src.getStr("business_license_copy"));
            if (StringUtils.hasText(copy)) {
                dest.set("business_license_copy", copy);
            }
            String number = firstNonBlank(src.getStr("cert_number"), src.getStr("business_license_number"));
            if (StringUtils.hasText(number)) {
                dest.set("business_license_number", number);
            }
            copyString(dest, src, "cert_type");
            copyString(dest, src, "merchant_name");
            copyString(dest, src, "legal_person");
            copyString(dest, src, "company_address");
            setBusinessTime(dest, src.getStr("period_begin"), src.getStr("period_end"));
            return dest;
        }
        return null;
    }

    private static void copyLicenseMediaFields(JSONObject dest, JSONObject src) {
        String copy = firstNonBlank(src.getStr("business_license_copy"), src.getStr("license_copy"));
        if (StringUtils.hasText(copy)) {
            dest.set("business_license_copy", copy);
        }
        String number = firstNonBlank(src.getStr("business_license_number"), src.getStr("license_number"));
        if (StringUtils.hasText(number)) {
            dest.set("business_license_number", number);
        }
    }

    private static void setBusinessTime(JSONObject dest, String begin, String end) {
        if (!StringUtils.hasText(begin) && !StringUtils.hasText(end)) {
            return;
        }
        JSONArray arr = new JSONArray();
        arr.add(StringUtils.hasText(begin) ? begin.trim() : "");
        arr.add(StringUtils.hasText(end) ? end.trim() : "长期");
        dest.set("business_time", arr.toString());
    }

    private static JSONArray mapUboInfoList(JSONArray src) {
        JSONArray dest = new JSONArray();
        for (int i = 0; i < src.size(); i++) {
            Object raw = src.get(i);
            if (!(raw instanceof JSONObject item)) {
                continue;
            }
            JSONObject mapped = new JSONObject();
            String docType = item.getStr("ubo_id_doc_type");
            if (StringUtils.hasText(docType)) {
                mapped.set("ubo_id_doc_type", mapIdDocType(docType));
            }
            copyString(mapped, item, "ubo_id_doc_copy");
            copyString(mapped, item, "ubo_id_doc_copy_back");
            copyString(mapped, item, "ubo_id_doc_name");
            copyString(mapped, item, "ubo_id_doc_number");
            copyString(mapped, item, "ubo_id_doc_address");
            String begin = firstNonBlank(item.getStr("ubo_id_doc_period_begin"), item.getStr("ubo_period_begin"));
            String end = firstNonBlank(item.getStr("ubo_id_doc_period_end"), item.getStr("ubo_period_end"));
            if (StringUtils.hasText(begin)) {
                mapped.set("ubo_id_doc_period_begin", begin);
            }
            if (StringUtils.hasText(end)) {
                mapped.set("ubo_id_doc_period_end", end);
            }
            dest.add(mapped);
        }
        return dest;
    }

    private static JSONObject mapContactInfo(JSONObject src) {
        JSONObject dest = new JSONObject();
        String contactType = src.getStr("contact_type");
        if (StringUtils.hasText(contactType)) {
            dest.set("contact_type", mapContactType(contactType));
        }
        copyString(dest, src, "contact_name");
        copyString(dest, src, "mobile_phone");
        String idDocType = src.getStr("contact_id_doc_type");
        if (StringUtils.hasText(idDocType)) {
            dest.set("contact_id_doc_type", mapIdDocType(idDocType));
        }
        copyString(dest, src, "contact_id_doc_copy");
        copyString(dest, src, "contact_id_doc_copy_back");
        String periodBegin = firstNonBlank(src.getStr("contact_id_doc_period_begin"), src.getStr("contact_period_begin"));
        String periodEnd = firstNonBlank(src.getStr("contact_id_doc_period_end"), src.getStr("contact_period_end"));
        if (StringUtils.hasText(periodBegin)) {
            dest.set("contact_id_doc_period_begin", periodBegin);
        }
        if (StringUtils.hasText(periodEnd)) {
            dest.set("contact_id_doc_period_end", periodEnd);
        }
        String idNumber = src.getStr("contact_id_number");
        if (StringUtils.hasText(idNumber)) {
            dest.set("contact_id_card_number", idNumber);
        } else {
            copyString(dest, src, "contact_id_card_number");
        }
        return dest;
    }

    private static JSONObject mapAccountInfo(JSONObject src) {
        JSONObject dest = new JSONObject();
        String accountType = src.getStr("bank_account_type");
        if (StringUtils.hasText(accountType)) {
            dest.set("bank_account_type", mapBankAccountType(accountType));
        }
        copyString(dest, src, "account_bank");
        copyString(dest, src, "account_name");
        copyString(dest, src, "bank_branch_id");
        copyString(dest, src, "bank_name");
        copyString(dest, src, "account_number");
        return dest;
    }

    private static JSONObject mapIdCardInfo(JSONObject src) {
        JSONObject dest = new JSONObject();
        copyString(dest, src, "id_card_copy");
        copyString(dest, src, "id_card_national");
        copyString(dest, src, "id_card_name");
        copyString(dest, src, "id_card_number");
        String begin = firstNonBlank(src.getStr("card_period_begin"), src.getStr("id_card_valid_time_begin"));
        if (StringUtils.hasText(begin)) {
            dest.set("id_card_valid_time_begin", begin);
        }
        String end = firstNonBlank(src.getStr("card_period_end"), src.getStr("id_card_valid_time"));
        if (StringUtils.hasText(end)) {
            dest.set("id_card_valid_time", end);
        }
        return dest;
    }

    private static JSONObject mapIdDocInfo(JSONObject src) {
        JSONObject dest = new JSONObject();
        copyString(dest, src, "id_doc_copy");
        copyString(dest, src, "id_doc_copy_back");
        copyString(dest, src, "id_doc_name");
        copyString(dest, src, "id_doc_number");
        copyString(dest, src, "id_doc_address");
        String begin = src.getStr("doc_period_begin");
        if (StringUtils.hasText(begin)) {
            dest.set("doc_period_begin", begin);
        }
        String end = src.getStr("doc_period_end");
        if (StringUtils.hasText(end)) {
            dest.set("doc_period_end", end);
        }
        return dest;
    }

    private static JSONObject mapSettlementInfo(JSONObject src) {
        JSONObject dest = new JSONObject();
        String settlementId = src.getStr("settlement_id");
        if (StringUtils.hasText(settlementId)) {
            if (settlementId.matches("\\d+")) {
                dest.set("settlement_id", Integer.parseInt(settlementId));
            } else {
                dest.set("settlement_id", settlementId);
            }
        }
        copyString(dest, src, "qualification_type");
        return dest;
    }

    private static JSONObject buildSalesSceneInfo(JSONObject businessInfo, String merchantShortname) {
        JSONObject salesInfo = businessInfo.getJSONObject("sales_info");
        if (salesInfo == null) {
            return null;
        }
        JSONObject dest = new JSONObject();
        String storeName = firstNonBlank(
                salesInfo.getStr("store_name"),
                salesInfo.getJSONObject("biz_store_info") != null
                        ? salesInfo.getJSONObject("biz_store_info").getStr("biz_store_name")
                        : null,
                merchantShortname,
                businessInfo.getStr("merchant_shortname")
        );
        if (StringUtils.hasText(storeName)) {
            dest.set("store_name", storeName);
        }

        JSONObject mini = salesInfo.getJSONObject("mini_program_info");
        if (mini != null) {
            copyString(dest, mini, "mini_program_sub_appid");
            if (!dest.containsKey("store_qr_code")) {
                JSONArray pics = mini.getJSONArray("mini_program_pics");
                if (pics != null && !pics.isEmpty()) {
                    String firstPic = pics.getStr(0);
                    if (StringUtils.hasText(firstPic)) {
                        dest.set("store_qr_code", firstPic);
                    }
                }
            }
        }

        JSONObject store = salesInfo.getJSONObject("biz_store_info");
        if (store != null) {
            copyString(dest, store, "store_qr_code");
            copyString(dest, store, "store_url");
        }
        JSONObject mp = salesInfo.getJSONObject("mp_info");
        if (mp != null && !dest.containsKey("store_qr_code")) {
            JSONArray mpPics = mp.getJSONArray("mp_pics");
            if (mpPics != null && !mpPics.isEmpty()) {
                String firstPic = mpPics.getStr(0);
                if (StringUtils.hasText(firstPic)) {
                    dest.set("store_qr_code", firstPic);
                }
            }
        }
        JSONObject web = salesInfo.getJSONObject("web_info");
        if (web != null && !dest.containsKey("store_url")) {
            String domain = web.getStr("domain");
            if (StringUtils.hasText(domain)) {
                dest.set("store_url", domain.startsWith("http") ? domain : "https://" + domain);
            }
        }
        return dest;
    }

    private static void mapAdditionFields(JSONObject root, JSONObject draft) {
        JSONObject addition = draft.getJSONObject("addition_info");
        if (addition == null || addition.isEmpty()) {
            return;
        }
        JSONArray pics = addition.getJSONArray("business_addition_pics");
        if (pics == null) {
            pics = addition.getJSONArray("addition_pics");
        }
        if (pics != null && !pics.isEmpty()) {
            root.set("business_addition_pics", pics.toString());
        }
        String desc = firstNonBlank(addition.getStr("business_addition_desc"), addition.getStr("addition_desc"));
        if (StringUtils.hasText(desc)) {
            root.set("business_addition_desc", desc);
        }
    }

    private static void copyIfPresent(JSONObject dest, JSONObject src, String key) {
        if (src.containsKey(key) && src.get(key) != null) {
            dest.set(key, src.get(key));
        }
    }

    private static void copyString(JSONObject dest, JSONObject src, String key) {
        String val = src.getStr(key);
        if (StringUtils.hasText(val)) {
            dest.set(key, val);
        }
    }

    private static String firstNonBlank(String... values) {
        if (values == null) {
            return null;
        }
        for (String value : values) {
            if (StringUtils.hasText(value)) {
                return value.trim();
            }
        }
        return null;
    }

    private static boolean isLicenseSubject(String subjectType) {
        return "SUBJECT_TYPE_INDIVIDUAL".equals(subjectType) || "SUBJECT_TYPE_ENTERPRISE".equals(subjectType);
    }

    private static boolean isCertificateSubject(String subjectType) {
        return "SUBJECT_TYPE_GOVERNMENT".equals(subjectType)
                || "SUBJECT_TYPE_INSTITUTIONS".equals(subjectType)
                || "SUBJECT_TYPE_OTHERS".equals(subjectType);
    }

    private static String mapOrganizationType(String subjectType) {
        if (subjectType == null) {
            throw new IllegalArgumentException("主体类型 subject_type 不能为空");
        }
        return switch (subjectType) {
            case "SUBJECT_TYPE_ENTERPRISE" -> "2";
            case "SUBJECT_TYPE_INSTITUTIONS" -> "3";
            case "SUBJECT_TYPE_INDIVIDUAL" -> "4";
            case "SUBJECT_TYPE_OTHERS" -> "1708";
            case "SUBJECT_TYPE_GOVERNMENT" -> "2502";
            case "SUBJECT_TYPE_MICRO" -> "2401";
            case "SUBJECT_TYPE_PERSONAL_SELLER" -> "2500";
            default -> throw new IllegalArgumentException("不支持的主体类型: " + subjectType);
        };
    }

    private static String mapContactType(String contactType) {
        return switch (contactType) {
            case "LEGAL" -> "65";
            case "SUPER" -> "66";
            default -> contactType;
        };
    }

    private static String mapBankAccountType(String bankAccountType) {
        return switch (bankAccountType) {
            case "BANK_ACCOUNT_TYPE_CORPORATE" -> "74";
            case "BANK_ACCOUNT_TYPE_PERSONAL" -> "75";
            default -> bankAccountType;
        };
    }

    private static String mapIdDocType(String idDocType) {
        return switch (idDocType) {
            case "IDENTIFICATION_TYPE_IDCARD" -> "IDENTIFICATION_TYPE_MAINLAND_IDCARD";
            case "IDENTIFICATION_TYPE_HONGKONG_PASSPORT" -> "IDENTIFICATION_TYPE_HONGKONG";
            case "IDENTIFICATION_TYPE_MACAO_PASSPORT" -> "IDENTIFICATION_TYPE_MACAO";
            default -> idDocType;
        };
    }
}
