package com.ruoyi.system.service.impl;

import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.system.domain.PosOrderLinePayment;
import com.ruoyi.system.domain.PosOrderLineRefund;
import com.ruoyi.system.mapper.PosOrderLineRefundMapper;
import com.ruoyi.system.service.IPosOrderLineRefundService;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

@Service
public class PosOrderLineRefundServiceImpl implements IPosOrderLineRefundService {

    private final PosOrderLineRefundMapper refundMapper;

    public PosOrderLineRefundServiceImpl(PosOrderLineRefundMapper refundMapper) {
        this.refundMapper = refundMapper;
    }

    @Override
    public PosOrderLineRefund getByPaymentId(Long paymentId) {
        return paymentId == null ? null : refundMapper.selectByPaymentId(paymentId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public PosOrderLineRefund createIfAbsent(PosOrderLinePayment payment, String source) {
        if (payment == null || payment.getId() == null
                || !("PAID".equals(payment.getStatus())
                || "AMOUNT_MISMATCH".equals(payment.getStatus()))
                || payment.getTransactionId() == null) {
            throw new ServiceException("Only a captured LINE Pay payment can be refunded");
        }
        PosOrderLineRefund existing = refundMapper.selectByPaymentId(payment.getId());
        if (existing != null) {
            return existing;
        }
        Date now = new Date();
        PosOrderLineRefund row = new PosOrderLineRefund();
        row.setPaymentId(payment.getId());
        row.setDdId(payment.getDdId());
        row.setCredentialId(payment.getCredentialId());
        row.setTransactionId(payment.getTransactionId());
        row.setAmount("AMOUNT_MISMATCH".equals(payment.getStatus())
                ? payment.getCapturedAmount() : payment.getAmount());
        if (row.getAmount() == null || row.getAmount() <= 0) {
            throw new ServiceException("Captured LINE Pay amount is unavailable");
        }
        row.setStatus("CREATED");
        row.setSource(source);
        row.setVersion(0L);
        row.setReconcileCount(0);
        row.setNextReconcileAt(now);
        // The application service starts the deadline only after an uncertain gateway outcome.
        row.setReconcileDeadline(null);
        row.setCreateTime(now);
        row.setUpdateTime(now);
        try {
            refundMapper.insert(row);
            return row;
        } catch (DuplicateKeyException duplicate) {
            PosOrderLineRefund concurrent = refundMapper.selectByPaymentId(payment.getId());
            if (concurrent != null) {
                return concurrent;
            }
            throw duplicate;
        }
    }

    @Override
    public int claimProcessing(Long id, Long version, String expectedStatus,
                               String leaseOwner, Date leaseUntil) {
        return refundMapper.claimProcessing(id, version, expectedStatus,
                leaseOwner, leaseUntil, new Date());
    }

    @Override
    public int markUnknown(Long id, Long version, Date nextReconcileAt, Date reconcileDeadline) {
        return refundMapper.markUnknown(id, version, nextReconcileAt,
                reconcileDeadline, new Date());
    }

    @Override
    public int markRetryWait(Long id, Long version, Date nextReconcileAt) {
        return id == null || version == null ? 0
                : refundMapper.markRetryWait(id, version, nextReconcileAt, new Date());
    }

    @Override
    public int markFailed(Long id, Long version) {
        return id == null || version == null ? 0 : refundMapper.markFailed(id, version, new Date());
    }

    @Override
    public int recoverExpiredProcessing(Long id, Long version, Date nextReconcileAt,
                                        Date reconcileDeadline) {
        return id == null || version == null ? 0 : refundMapper.recoverExpiredProcessing(
                id, version, nextReconcileAt, reconcileDeadline, new Date());
    }

    @Override
    public int rescheduleUnknown(Long id, Long version, Date nextReconcileAt,
                                 Date reconcileDeadline) {
        return id == null || version == null ? 0 : refundMapper.rescheduleUnknown(
                id, version, nextReconcileAt, reconcileDeadline, new Date());
    }

    @Override
    public int markRefunded(Long id, Long version, String refundTransactionId, Date refundTime) {
        return refundMapper.markRefunded(id, version, refundTransactionId,
                refundTime, new Date());
    }

    @Override
    public int markRefundedFromEvidence(Long id, Long version, String expectedStatus,
                                        String refundTransactionId, Date refundTime) {
        return id == null || version == null ? 0
                : refundMapper.markRefundedFromEvidence(id, version, expectedStatus,
                refundTransactionId, refundTime, new Date());
    }

    @Override
    public List<PosOrderLineRefund> scanDue(Date now, int batchSize) {
        return refundMapper.scanDue(now == null ? new Date() : now,
                Math.max(1, Math.min(batchSize, 20)));
    }

    @Override
    public int markManualReview(Long id, Long version, String expectedStatus) {
        return refundMapper.markManualReview(id, version, expectedStatus, new Date());
    }

    @Override
    public int markManualReviewFromEvidence(Long id, Long version, String expectedStatus) {
        return id == null || version == null ? 0
                : refundMapper.markManualReviewFromEvidence(
                id, version, expectedStatus, new Date());
    }
}
