package com.ruoyi.system.service.impl;

import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.system.domain.PosOrderLinePayment;
import com.ruoyi.system.mapper.PosOrderLinePaymentMapper;
import com.ruoyi.system.service.IPosOrderLinePaymentService;
import org.springframework.stereotype.Service;

import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Set;

@Service
public class PosOrderLinePaymentServiceImpl implements IPosOrderLinePaymentService {

    private static final Set<String> TERMINAL_RELEASE_STATES =
            Set.of("CANCELLED_OR_EXPIRED", "FAILED");
    private final PosOrderLinePaymentMapper paymentMapper;

    public PosOrderLinePaymentServiceImpl(PosOrderLinePaymentMapper paymentMapper) {
        this.paymentMapper = paymentMapper;
    }

    @Override
    public PosOrderLinePayment createRequesting(String ddId, String lineOrderId, Long credentialId,
                                                Long storeId, Integer amount, String currency,
                                                Date reconcileDeadline) {
        return createRequesting(ddId, lineOrderId, credentialId, storeId, amount, currency,
                "ONLINE", reconcileDeadline);
    }

    @Override
    public PosOrderLinePayment createRequesting(String ddId, String lineOrderId, Long credentialId,
                                                Long storeId, Integer amount, String currency,
                                                String paymentMode, Date reconcileDeadline) {
        if (StrUtil.isBlank(ddId) || StrUtil.isBlank(lineOrderId) || credentialId == null
                || storeId == null || amount == null || amount <= 0 || StrUtil.isBlank(currency)
                || !("ONLINE".equals(paymentMode) || "OFFLINE".equals(paymentMode))) {
            throw new ServiceException("Invalid LINE Pay request intent");
        }
        Date now = new Date();
        PosOrderLinePayment row = new PosOrderLinePayment();
        row.setDdId(ddId);
        row.setLineOrderId(lineOrderId);
        row.setCredentialId(credentialId);
        row.setStoreId(storeId);
        row.setAmount(amount);
        row.setCurrency(currency);
        row.setPaymentMode(paymentMode);
        row.setStatus("REQUESTING");
        row.setActiveDdId(ddId);
        row.setVersion(0L);
        row.setNextReconcileAt(now);
        row.setReconcileDeadline(reconcileDeadline);
        row.setReconcileCount(0);
        row.setStatusChangedAt(now);
        row.setCreateTime(now);
        row.setUpdateTime(now);
        paymentMapper.insert(row);
        return row;
    }

    @Override
    public PosOrderLinePayment getActiveByDdId(String ddId) {
        return StrUtil.isBlank(ddId) ? null : paymentMapper.selectActiveByDdId(ddId);
    }

    @Override
    public PosOrderLinePayment getByLineOrderId(String lineOrderId) {
        return StrUtil.isBlank(lineOrderId) ? null : paymentMapper.selectByLineOrderId(lineOrderId);
    }

    @Override
    public PosOrderLinePayment getByTransactionId(String transactionId) {
        return StrUtil.isBlank(transactionId) ? null : paymentMapper.selectByTransactionId(transactionId);
    }

    @Override
    public PosOrderLinePayment getById(Long id) {
        return id == null ? null : paymentMapper.selectPaymentById(id);
    }

    @Override
    public List<PosOrderLinePayment> getByDdId(String ddId) {
        return StrUtil.isBlank(ddId) ? Collections.emptyList() : paymentMapper.selectByDdId(ddId);
    }

    @Override
    public List<PosOrderLinePayment> getCancelledPaidWithoutRefund(int batchSize) {
        return paymentMapper.selectCancelledPaidWithoutRefund(Math.max(1, Math.min(batchSize, 20)));
    }

    @Override
    public int markTerminal(Long id, Long version, String expectedStatus, String targetStatus) {
        if (id == null || version == null || !TERMINAL_RELEASE_STATES.contains(targetStatus)) {
            return 0;
        }
        return paymentMapper.markTerminal(id, version, expectedStatus, targetStatus, new Date());
    }

    @Override
    public int markRequestSucceeded(Long id, Long version, String transactionId,
                                    String paymentUrlWeb, String paymentUrlApp,
                                    Date nextReconcileAt, Date reconcileDeadline) {
        if (id == null || version == null || StrUtil.isBlank(transactionId)
                || StrUtil.isBlank(paymentUrlWeb)) {
            return 0;
        }
        return paymentMapper.markRequestSucceeded(id, version, transactionId,
                paymentUrlWeb, paymentUrlApp, nextReconcileAt, reconcileDeadline, new Date());
    }

    @Override
    public int markRequestUnknown(Long id, Long version, Date nextReconcileAt,
                                  Date reconcileDeadline) {
        if (id == null || version == null) {
            return 0;
        }
        return paymentMapper.markRequestUnknown(id, version, nextReconcileAt,
                reconcileDeadline, new Date());
    }

    @Override
    public int markOfflineWaitingAuth(Long id, Long version, String transactionId,
                                      Date nextReconcileAt, Date reconcileDeadline) {
        if (id == null || version == null) {
            return 0;
        }
        return paymentMapper.markOfflineWaitingAuth(id, version, transactionId,
                nextReconcileAt, reconcileDeadline, new Date());
    }

    @Override
    public int markOfflineEvidence(Long id, Long version, String expectedStatus, String targetStatus,
                                   String transactionId, String paymentProvider,
                                   Integer capturedAmount, Date payTime) {
        if (id == null || version == null || StrUtil.isBlank(expectedStatus)
                || !("AMOUNT_MISMATCH".equals(targetStatus) || "MANUAL_REVIEW".equals(targetStatus))
                || StrUtil.isBlank(transactionId) || capturedAmount == null || capturedAmount <= 0) {
            return 0;
        }
        return paymentMapper.markOfflineEvidence(id, version, expectedStatus, targetStatus,
                transactionId, paymentProvider, capturedAmount,
                payTime == null ? new Date() : payTime, new Date());
    }

    @Override
    public int rescheduleOffline(Long id, Long version, String expectedStatus,
                                 Date nextReconcileAt, Date reconcileDeadline) {
        if (id == null || version == null || StrUtil.isBlank(expectedStatus)) {
            return 0;
        }
        return paymentMapper.rescheduleOffline(id, version, expectedStatus,
                nextReconcileAt, reconcileDeadline, new Date());
    }

    @Override
    public int claimConfirm(Long id, Long version, String expectedStatus,
                            String leaseOwner, Date leaseUntil) {
        if (id == null || version == null || StrUtil.isBlank(expectedStatus)
                || StrUtil.isBlank(leaseOwner) || leaseUntil == null) {
            return 0;
        }
        return paymentMapper.claimConfirm(id, version, expectedStatus, leaseOwner, leaseUntil, new Date());
    }

    @Override
    public int markAuthDoneOrderCancelled(Long id, Long version, String expectedStatus,
                                          Date nextReconcileAt, Date reconcileDeadline) {
        if (id == null || version == null) {
            return 0;
        }
        return paymentMapper.markAuthDoneOrderCancelled(id, version, expectedStatus,
                nextReconcileAt, reconcileDeadline, new Date());
    }

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

    @Override
    public int markPaid(Long id, Long version, String expectedStatus,
                        String transactionId, String paymentProvider, Date payTime) {
        return id == null || version == null || StrUtil.isBlank(expectedStatus)
                || StrUtil.isBlank(transactionId) ? 0
                : paymentMapper.markPaid(id, version, expectedStatus, transactionId, paymentProvider,
                payTime == null ? new Date() : payTime, new Date());
    }

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

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

    @Override
    public int claimReconcileLease(Long id, Long version, String leaseOwner,
                                   Date leaseUntil, Date now) {
        if (id == null || version == null || StrUtil.isBlank(leaseOwner) || leaseUntil == null) {
            return 0;
        }
        return paymentMapper.claimReconcileLease(id, version, leaseOwner,
                leaseUntil, now == null ? new Date() : now);
    }

    @Override
    public void applyActiveKeyInvariant(PosOrderLinePayment payment) {
        if (payment != null && TERMINAL_RELEASE_STATES.contains(payment.getStatus())) {
            payment.setActiveDdId(null);
        }
    }
}
