package cn.novalon.gym.manage.member.service.impl;

import cn.novalon.gym.manage.common.util.HtmlEscapeUtil;
import cn.novalon.gym.manage.member.entity.RefundApplication;
import cn.novalon.gym.manage.member.enums.RefundStatus;
import cn.novalon.gym.manage.member.repository.RefundApplicationRepository;
import cn.novalon.gym.manage.member.service.IRefundApplicationService;
import cn.novalon.gym.manage.common.util.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;

import java.time.LocalDateTime;

/**
 * 退款申请服务实现
 * 
 * @author 付嘉
 * @date 2026-05-27
 */
@Slf4j
@Service
public class RefundApplicationServiceImpl implements IRefundApplicationService {

    private final RefundApplicationRepository refundApplicationRepository;
    private final RedisUtil redisUtil;

    private static final String REFUND_APPLICATION_CACHE_PREFIX = "member:refund:";
    private static final long CACHE_EXPIRE_SECONDS = 300;

    public RefundApplicationServiceImpl(RefundApplicationRepository refundApplicationRepository, RedisUtil redisUtil) {
        this.refundApplicationRepository = refundApplicationRepository;
        this.redisUtil = redisUtil;
    }

    @Override
    public Mono<RefundApplication> create(Long recordId, String reason) {
        return refundApplicationRepository.findByRecordId(recordId)
                .flatMap(existing -> {
                    if (existing != null) {
                        return Mono.error(new RuntimeException("该会员卡记录已有退款申请"));
                    }
                    return Mono.empty();
                })
                .then(Mono.defer(() -> {
                    RefundApplication application = RefundApplication.builder()
                            .recordId(recordId)
                            .reason(HtmlEscapeUtil.escape(reason))
                            .status(RefundStatus.PENDING)
                            .applyTime(LocalDateTime.now())
                            .build();

                    return refundApplicationRepository.save(application)
                            .doOnSuccess(app -> {
                                log.info("创建退款申请成功: applicationId={}, recordId={}", app.getId(), recordId);
                                clearRefundCache(recordId);
                            });
                }));
    }

    @Override
    public Mono<RefundApplication> approve(Long applicationId, Long auditorId, String remark) {
        return refundApplicationRepository.findById(applicationId)
                .switchIfEmpty(Mono.error(new RuntimeException("退款申请不存在")))
                .flatMap(application -> {
                    if (application.getStatus() != RefundStatus.PENDING) {
                        return Mono.error(new RuntimeException("退款申请状态不正确，当前状态: " + application.getStatus()));
                    }

                    return refundApplicationRepository.approve(applicationId, "APPROVED", auditorId, HtmlEscapeUtil.escape(remark))
                            .flatMap(updatedRows -> {
                                if (updatedRows == 0) {
                                    return Mono.error(new RuntimeException("批准退款申请失败"));
                                }
                                clearRefundCache(application.getRecordId());
                                log.info("批准退款申请成功: applicationId={}, auditorId={}", applicationId, auditorId);
                                return refundApplicationRepository.findById(applicationId);
                            });
                });
    }

    @Override
    public Mono<RefundApplication> reject(Long applicationId, Long auditorId, String remark) {
        return refundApplicationRepository.findById(applicationId)
                .switchIfEmpty(Mono.error(new RuntimeException("退款申请不存在")))
                .flatMap(application -> {
                    if (application.getStatus() != RefundStatus.PENDING) {
                        return Mono.error(new RuntimeException("退款申请状态不正确，当前状态: " + application.getStatus()));
                    }

                    return refundApplicationRepository.approve(applicationId, "REJECTED", auditorId, HtmlEscapeUtil.escape(remark))
                            .flatMap(updatedRows -> {
                                if (updatedRows == 0) {
                                    return Mono.error(new RuntimeException("拒绝退款申请失败"));
                                }
                                clearRefundCache(application.getRecordId());
                                log.info("拒绝退款申请成功: applicationId={}, auditorId={}", applicationId, auditorId);
                                return refundApplicationRepository.findById(applicationId);
                            });
                });
    }

    @Override
    public Mono<RefundApplication> findByRecordId(Long recordId) {
        String cacheKey = REFUND_APPLICATION_CACHE_PREFIX + recordId;
        Object cached = redisUtil.get(cacheKey);
        if (cached != null && cached instanceof RefundApplication) {
            log.debug("从缓存获取退款申请, recordId: {}", recordId);
            return Mono.just((RefundApplication) cached);
        }

        return refundApplicationRepository.findByRecordId(recordId)
                .doOnSuccess(application -> {
                    if (application != null) {
                        redisUtil.setWithExpire(cacheKey, application, CACHE_EXPIRE_SECONDS);
                    }
                });
    }

    private void clearRefundCache(Long recordId) {
        String cacheKey = REFUND_APPLICATION_CACHE_PREFIX + recordId;
        redisUtil.delete(cacheKey);
        log.debug("清除退款申请缓存, recordId: {}", recordId);
    }
}
