package com.red.circle.order.app.command.pay.web;

import com.red.circle.component.pay.paymax.PayMaxUtils;
import com.red.circle.order.app.common.InAppPurchaseCommon;
import com.red.circle.order.app.common.MiFaPayMysqlOrderSupport;
import com.red.circle.order.app.service.MiFaPayConfig;
import com.red.circle.order.app.service.MiFaPayConfigService;
import com.red.circle.order.domain.order.MiFaPayReceivePaymentNotice;
import com.red.circle.order.infra.database.mongo.order.InAppPurchaseCollectionReceiptService;
import com.red.circle.order.infra.database.mongo.order.entity.InAppPurchaseDetails;
import com.red.circle.order.infra.database.rds.entity.order.OrderUserPurchasePay;
import com.red.circle.order.infra.database.rds.service.order.OrderUserPurchasePayNoticeService;
import com.red.circle.order.infra.database.rds.service.order.OrderUserPurchasePayService;
import com.red.circle.tool.core.text.StringUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Objects;

/**
 * MiFaPay 支付回调通知处理
 *
 * @author system
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class MiFaPayServerNoticeReceivePaymentCmdExe {

    private static final String SUCCESS = "SUCCESS";
    private static final String FAIL = "FAIL";

    private final MiFaPayConfigService miFaPayConfigService;
    private final InAppPurchaseCommon inAppPurchaseCommon;
    private final InAppPurchaseCollectionReceiptService inAppPurchaseCollectionReceiptService;
    private final OrderUserPurchasePayNoticeService orderUserPurchasePayNoticeService;
    private final OrderUserPurchasePayService orderUserPurchasePayService;

    /**
     * 处理MiFaPay支付回调
     *
     * @param body 请求体
     * @return SUCCESS 或 FAIL
     */
    public String execute(byte[] body) {
        //  参数校验
        if (Objects.isNull(body) || body.length == 0) {
            log.error("MiFaPay回调参数为空");
            return FAIL;
        }

        String bodyParam = new String(body, StandardCharsets.UTF_8);
        log.warn("MiFaPay回调通知: body={}", bodyParam);

        //  解析回调数据
        MiFaPayReceivePaymentNotice notice = MiFaPayReceivePaymentNotice.serializable(bodyParam);
        if (Objects.isNull(notice) || Objects.isNull(notice.getNoticeData())) {
            log.error("MiFaPay回调数据解析失败: {}", bodyParam);
            return FAIL;
        }

        MiFaPayReceivePaymentNotice.MiFaPayNoticeData noticeData = notice.getNoticeData();

        OrderUserPurchasePay mysqlOrder = getMysqlOrder(noticeData.getOrderId());
        if (Objects.isNull(mysqlOrder)) {
            log.error("MiFaPay回调未找到MySQL订单: orderId={}", noticeData.getOrderId());
            return FAIL;
        }

        //  验证签名（对data字段验签）
        if (StringUtils.isBlank(notice.getSign()) || StringUtils.isBlank(notice.getData())) {
            log.error("MiFaPay回调签名或data为空");
            return FAIL;
        }

        MiFaPayConfig config = miFaPayConfigService.getConfig(
                mysqlOrder.getFactoryCode(), mysqlOrder.getEvn(), mysqlOrder.getSysOrigin());
        boolean validSign = PayMaxUtils.validSignRsa(
                notice.getData(),
                notice.getSign(),
                config.getPlatformRsaPublicKey()
        );

        if (!validSign) {
            log.error("MiFaPay回调验签失败: sign={}, data={}", notice.getSign(), notice.getData());
            return FAIL;
        }

        return handleMysqlOrder(notice, noticeData, mysqlOrder);
    }

    private String handleMysqlOrder(MiFaPayReceivePaymentNotice notice,
                                    MiFaPayReceivePaymentNotice.MiFaPayNoticeData noticeData,
                                    OrderUserPurchasePay mysqlOrder) {
        safeAddNotice(
                mysqlOrder.getId(),
                buildNoticeEventId(noticeData, notice),
                "mifapay-" + noticeData.getOrderStatus(),
                notice,
                mysqlOrder.getUpdateUser()
        );

        if (!Objects.equals(mysqlOrder.getPayStatus(), MiFaPayMysqlOrderSupport.PAY_STATUS_PREPAYMENT)) {
            log.warn("MiFaPay订单已处理(MySQL): orderId={}, status={}",
                    mysqlOrder.getId(), mysqlOrder.getPayStatus());
            return SUCCESS;
        }

        InAppPurchaseDetails order = MiFaPayMysqlOrderSupport.toInAppPurchaseDetails(mysqlOrder);

        if (noticeData.isSuccess()) {
            if (order.receiptTypeEqReceipt()) {
                inAppPurchaseCommon.inAppPurchaseReceipt(order,
                        () -> orderUserPurchasePayService.updateSuccess(mysqlOrder.getId()));
                log.info("MiFaPay收款单处理成功(MySQL): orderId={}", mysqlOrder.getId());
                return SUCCESS;
            }

            if (order.receiptTypeEqPayment()) {
                inAppPurchaseCommon.inAppPurchasePayment(order,
                        () -> orderUserPurchasePayService.updateSuccess(mysqlOrder.getId()));
                log.info("MiFaPay付款单处理成功(MySQL): orderId={}", mysqlOrder.getId());
                return SUCCESS;
            }

            log.error("MiFaPay订单类型错误(MySQL): orderId={}", mysqlOrder.getId());
            return FAIL;
        }

        if (noticeData.isFailed()) {
            if (order.receiptTypeEqReceipt() && StringUtils.isNotBlank(mysqlOrder.getReferenceId())) {
                inAppPurchaseCollectionReceiptService.updateStatusSuccessFail(
                        mysqlOrder.getReferenceId());
            }
            orderUserPurchasePayService.updateFail(
                    mysqlOrder.getId(),
                    String.format("MiFaPay支付失败: %s", noticeData.getOrderStatus())
            );
            log.info("MiFaPay支付失败处理完成(MySQL): orderId={}", mysqlOrder.getId());
            return SUCCESS;
        }

        log.error("MiFaPay未知订单状态(MySQL): orderId={}, status={}",
                mysqlOrder.getId(), noticeData.getOrderStatus());
        return SUCCESS;
    }

    private OrderUserPurchasePay getMysqlOrder(String orderId) {
        Long mysqlOrderId = MiFaPayMysqlOrderSupport.parseOrderId(orderId);
        return mysqlOrderId == null ? null : orderUserPurchasePayService.getById(mysqlOrderId);
    }

    private void safeAddNotice(Long purchasePayId, String eventId, String noticeType, Object noticeData,
                               Long operationUserId) {
        try {
            orderUserPurchasePayNoticeService.addNotice(
                    purchasePayId, eventId, noticeType, noticeData, operationUserId);
        } catch (Exception e) {
            log.error("MiFaPay回调通知落库失败，继续执行支付主流程: purchasePayId={}, eventId={}, noticeType={}",
                    purchasePayId, eventId, noticeType, e);
        }
    }

    private String buildNoticeEventId(MiFaPayReceivePaymentNotice.MiFaPayNoticeData noticeData,
                                      MiFaPayReceivePaymentNotice notice) {
        String orderId = noticeData.getOrderId();
        String bankOrderNo = noticeData.getBankOrderNo();
        String mbOrderId = noticeData.getMbOrderId();
        if (StringUtils.isNotBlank(orderId) && StringUtils.isNotBlank(bankOrderNo)) {
            return "mifapay:" + orderId + ":" + bankOrderNo;
        }
        if (StringUtils.isNotBlank(orderId) && StringUtils.isNotBlank(mbOrderId)) {
            return "mifapay:" + orderId + ":" + mbOrderId;
        }
        if (StringUtils.isNotBlank(orderId)) {
            return "mifapay:" + orderId;
        }
        if (StringUtils.isNotBlank(bankOrderNo)) {
            return "mifapay:bank:" + bankOrderNo;
        }
        if (StringUtils.isNotBlank(mbOrderId)) {
            return "mifapay:mb:" + mbOrderId;
        }
        return "mifapay:sign:" + sha256Hex(Objects.toString(notice.getSign(), ""));
    }

    private String sha256Hex(String value) {
        try {
            MessageDigest digest = MessageDigest.getInstance("SHA-256");
            byte[] hash = digest.digest(value.getBytes(StandardCharsets.UTF_8));
            StringBuilder hex = new StringBuilder(hash.length * 2);
            for (byte b : hash) {
                String item = Integer.toHexString(b & 0xff);
                if (item.length() == 1) {
                    hex.append('0');
                }
                hex.append(item);
            }
            return hex.toString();
        } catch (Exception e) {
            log.error("MiFaPay回调sign生成SHA-256失败", e);
            return "hash-error";
        }
    }
}
