package com.ruoyi.app.omgpay;

import com.ruoyi.app.omgpay.dto.OmgNotifyRequest;
import com.ruoyi.system.omgpay.domain.OmgPaymentAttempt;
import com.ruoyi.system.omgpay.service.IOmgPaymentAttemptService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.util.List;

/** Verifies OMG browser-return forms and renders navigation only; it never mutates payment state. */
@Service
public class OmgPaymentClientReturnService {
    private static final Logger log = LoggerFactory.getLogger(OmgPaymentClientReturnService.class);
    private static final List<String> PAYMENT_REQUIRED = List.of(
            "MerchantID", "MerchantTradeNo", "StoreID", "RtnCode", "RtnMsg", "TradeNo",
            "TradeAmt", "PaymentDate", "PaymentType", "PaymentTypeChargeFee", "TradeDate",
            "SimulatePaid", "CustomField1", "CustomField2", "CustomField3", "CustomField4",
            "CheckMacValue");
    private final IOmgPaymentAttemptService attempts;
    private final OmgCheckMacSigner signer;
    private final OmgPaymentReturnPageRenderer renderer;

    public OmgPaymentClientReturnService(IOmgPaymentAttemptService attempts,
                                         OmgCheckMacSigner signer,
                                         OmgPaymentReturnPageRenderer renderer) {
        this.attempts = attempts;
        this.signer = signer;
        this.renderer = renderer;
    }

    public OmgPaymentReturnPage paymentResult(OmgNotifyRequest request) {
        return verifiedReturn(request, PAYMENT_REQUIRED);
    }

    private OmgPaymentReturnPage verifiedReturn(OmgNotifyRequest request,
                                                List<String> requiredFields) {
        String merchantTradeNo = request == null ? null : request.value("MerchantTradeNo");
        OmgPaymentAttempt attempt = findAttempt(merchantTradeNo);
        if (request == null || !request.isValid() || attempt == null
                || !requiredFields.stream().allMatch(request::contains)
                || !identityMatches(request, attempt) || !signatureMatches(request, attempt)) {
            log.warn("OMG client return rejected source=payment-result, merchantTradeNo={}, reason=trust_validation_failed",
                    OmgPaymentCreateService.maskMerchantTradeNo(merchantTradeNo));
            return renderer.error(400, "無法驗證返回資料，請回到 App 查詢訂單");
        }
        log.info("OMG client return accepted source=payment-result, orderId={}, merchantTradeNo={}, rtnCode={}",
                OmgPaymentController.safeLogOrderId(attempt.getDdId()),
                OmgPaymentCreateService.maskMerchantTradeNo(merchantTradeNo), request.value("RtnCode"));
        return renderer.success(attempt.getDdId());
    }

    private boolean signatureMatches(OmgNotifyRequest request, OmgPaymentAttempt attempt) {
        String actual = request.value("CheckMacValue");
        if (actual == null || !actual.matches("(?i)[0-9a-f]{64}")) {
            return false;
        }
        try {
            String expected = signer.sign(request.signingFields(),
                    attempt.getHashKeySnapshot(), attempt.getHashIvSnapshot());
            return OmgPaymentNotifyService.secureEquals(expected, actual);
        } catch (IllegalArgumentException exception) {
            return false;
        }
    }

    private static boolean identityMatches(OmgNotifyRequest request, OmgPaymentAttempt attempt) {
        return attempt.getMerchantId().equals(request.value("MerchantID"))
                && attempt.getMerchantTradeNo().equals(request.value("MerchantTradeNo"))
                && String.valueOf(attempt.getAmount()).equals(request.value("TradeAmt"));
    }

    private OmgPaymentAttempt findAttempt(String merchantTradeNo) {
        if (merchantTradeNo == null || !merchantTradeNo.matches("[A-Z0-9]{1,20}")) {
            return null;
        }
        return attempts.selectByMerchantTradeNo(merchantTradeNo);
    }
}
