package com.ruoyi.system.omgpay.service.impl;

import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.MessageUtils;
import com.ruoyi.system.omgpay.domain.OmgPaymentAttempt;
import com.ruoyi.system.omgpay.domain.OmgPaymentOrderSnapshot;
import com.ruoyi.system.omgpay.mapper.OmgPaymentAttemptMapper;
import com.ruoyi.system.omgpay.service.IOmgPaymentAttemptService;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;

/**
 * Validates and persists OMG attempt facts inside the caller's creation transaction.
 */
@Service
public class OmgPaymentAttemptServiceImpl implements IOmgPaymentAttemptService {

    private static final int ATTEMPT_STATUS_CREATED = 0;

    private final OmgPaymentAttemptMapper mapper;

    public OmgPaymentAttemptServiceImpl(OmgPaymentAttemptMapper mapper) {
        this.mapper = mapper;
    }

    @Override
    public OmgPaymentOrderSnapshot selectOrderForUpdate(String ddId) {
        return StrUtil.isBlank(ddId) ? null : mapper.selectOrderForUpdate(ddId.trim());
    }

    @Override
    public OmgPaymentOrderSnapshot selectOrder(String ddId) {
        return StrUtil.isBlank(ddId) ? null : mapper.selectOrder(ddId.trim());
    }

    @Override
    public OmgPaymentAttempt selectActiveCreatedByDdIdForUpdate(String ddId) {
        return StrUtil.isBlank(ddId) ? null : mapper.selectActiveCreatedByDdIdForUpdate(ddId.trim());
    }

    @Override
    public OmgPaymentAttempt selectActiveCreatedByDdId(String ddId) {
        return StrUtil.isBlank(ddId) ? null : mapper.selectActiveCreatedByDdId(ddId.trim());
    }

    @Override
    public OmgPaymentAttempt selectPaidByDdId(String ddId) {
        return StrUtil.isBlank(ddId) ? null : mapper.selectPaidByDdId(ddId.trim());
    }

    @Override
    public OmgPaymentAttempt selectByMerchantTradeNoForUpdate(String merchantTradeNo) {
        return StrUtil.isBlank(merchantTradeNo)
                ? null : mapper.selectByMerchantTradeNoForUpdate(merchantTradeNo.trim());
    }

    @Override
    public OmgPaymentAttempt selectByMerchantTradeNo(String merchantTradeNo) {
        return StrUtil.isBlank(merchantTradeNo)
                ? null : mapper.selectByMerchantTradeNo(merchantTradeNo.trim());
    }

    @Override
    public List<OmgPaymentAttempt> scanDueCreated(Date now, int limit) {
        if (now == null || limit <= 0) {
            return List.of();
        }
        return mapper.selectDueCreated(now, Math.min(limit, 100));
    }

    @Override
    public int reserveDueQuery(Long id, Date now, Date nextQueryTime) {
        if (id == null || now == null || nextQueryTime == null || !nextQueryTime.after(now)) {
            return 0;
        }
        return mapper.reserveDueQuery(id, now, nextQueryTime);
    }

    @Override
    public OmgPaymentAttempt createCreated(String ddId, String merchantTradeNo, Long storeId,
                                           String merchantId, Integer amount,
                                           String hashKeySnapshot, String hashIvSnapshot) {
        validateCreatedFacts(ddId, merchantTradeNo, storeId, merchantId, amount,
                hashKeySnapshot, hashIvSnapshot);
        Date now = new Date();
        OmgPaymentAttempt row = new OmgPaymentAttempt();
        row.setDdId(ddId.trim());
        row.setMerchantTradeNo(merchantTradeNo.trim());
        row.setStoreId(storeId);
        row.setMerchantId(merchantId.trim());
        row.setAmount(amount);
        row.setHashKeySnapshot(hashKeySnapshot);
        row.setHashIvSnapshot(hashIvSnapshot);
        row.setAttemptStatus(ATTEMPT_STATUS_CREATED);
        row.setNextQueryTime(now);
        row.setQueryCount(0);
        row.setCreateTime(now);
        row.setUpdateTime(now);
        mapper.insertCreated(row);
        return row;
    }

    private static void validateCreatedFacts(String ddId, String merchantTradeNo, Long storeId,
                                             String merchantId, Integer amount,
                                             String hashKeySnapshot, String hashIvSnapshot) {
        if (StrUtil.isBlank(ddId)) {
            throw new ServiceException(MessageUtils.message("omg.payment.ddid.required"));
        }
        if (StrUtil.isBlank(merchantTradeNo)) {
            throw new ServiceException(MessageUtils.message("omg.payment.merchantTradeNo.required"));
        }
        if (!merchantTradeNo.trim().matches("[A-Z0-9]{1,20}")) {
            throw new ServiceException(MessageUtils.message("omg.payment.merchantTradeNo.invalid"));
        }
        if (storeId == null) {
            throw new ServiceException(MessageUtils.message("omg.payment.storeId.required"));
        }
        if (StrUtil.isBlank(merchantId)) {
            throw new ServiceException(MessageUtils.message("omg.payment.merchantId.required"));
        }
        if (!merchantId.trim().matches("[A-Za-z0-9]{1,10}")) {
            throw new ServiceException(MessageUtils.message("omg.payment.merchantId.invalid"));
        }
        if (amount == null || amount <= 0) {
            throw new ServiceException(MessageUtils.message("omg.payment.amount.invalid"));
        }
        if (StrUtil.isBlank(hashKeySnapshot) || StrUtil.isBlank(hashIvSnapshot)) {
            throw new ServiceException(MessageUtils.message("omg.payment.credential.snapshot.required"));
        }
    }

    @Override
    public int markPaid(OmgPaymentAttempt paymentFacts) {
        return mapper.markPaid(paymentFacts);
    }

    @Override
    public int markFailed(OmgPaymentAttempt paymentFacts) {
        return mapper.markFailed(paymentFacts);
    }

    @Override
    public int supersedeCreated(Long id) {
        return id == null ? 0 : mapper.supersedeCreated(id, new Date());
    }

    @Override
    public int supersedeOtherCreated(String ddId, Long paidAttemptId) {
        return mapper.supersedeOtherCreated(ddId, paidAttemptId, new Date());
    }

    @Override
    public int markOrderPaid(String ddId) {
        return mapper.markOrderPaid(ddId);
    }

    @Override
    public int countOtherPaidAttempts(String ddId, Long attemptId) {
        return mapper.countOtherPaidAttempts(ddId, attemptId);
    }
}
