package cn.novalon.gym.manage.member.handler;

import cn.novalon.gym.manage.member.enums.CardEvent;
import cn.novalon.gym.manage.member.repository.MemberCardRecordRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

import java.time.LocalDateTime;

/**
 * 会员卡定时任务处理器
 * 
 * @author 付嘉
 * @date 2026-05-27
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class MemberCardScheduledHandler {

    private final MemberCardRecordRepository recordRepository;
    private final ExpirationReminderService expirationReminderService;
    private final MemberCardStateMachine stateMachine;
    private final DistributedLockService distributedLockService;

    /**
     * 每日凌晨2点检查过期会员卡
     */
    @Scheduled(cron = "0 0 2 * * ?")
    public void checkExpiredCards() {
        String lockKey = "scheduled:check_expired_cards";

        distributedLockService.executeWithLock("SYSTEM", "EXPIRE_CHECK",
            Mono.fromRunnable(() -> {
                log.info("开始执行会员卡过期检查任务");

                LocalDateTime now = LocalDateTime.now();

                recordRepository.findActiveRecords()
                    .filter(record -> record.getExpireTime() != null && record.getExpireTime().isBefore(now))
                    .flatMap(record ->
                            stateMachine.transition(record.getStatus(), CardEvent.EXPIRE)
                                    .flatMap(newState -> {
                                        record.setStatus(newState);
                                        return recordRepository.save(record);
                                    })
                                .doOnSuccess(r -> log.info("会员卡记录ID={} 已标记为过期", r.getMemberCardRecordId()))
                                .onErrorResume(e -> {
                                    log.error("处理会员卡过期失败: recordId={}", record.getMemberCardRecordId(), e);
                                    return Mono.empty();
                                })
                        )
                        .then()
                        .subscribe();
            })
        ).subscribe();
    }

    /**
     * 每日凌晨3点检查是否有遗漏的到期提醒（兜底机制）
     */
    @Scheduled(cron = "0 0 3 * * ?")
    public void checkAndSendExpirationReminders() {
        String lockKey = "scheduled:expiration_reminder";

        distributedLockService.executeWithLock("SYSTEM", "REMINDER_CHECK",
            Mono.fromRunnable(() -> {
                log.info("开始执行到期提醒兜底检查任务");

                LocalDateTime now = LocalDateTime.now();

                // 查询所有活跃的会员卡
                recordRepository.findActiveRecords()
                    .filter(record -> record.getExpireTime() != null)
                    .flatMap(record -> {
                        try {
                            // 计算距离到期还有几天
                            long daysBetween = java.time.Duration.between(now, record.getExpireTime()).toDays();

                            // 如果到期时间在1-7天范围内，记录日志供人工检查
                            if (daysBetween >= 1 && daysBetween <= 7) {
                                log.warn("发现到期前{}天的会员卡记录ID={}，请确认是否已发送提醒",
                                    daysBetween, record.getMemberCardRecordId());
                            }

                            return Mono.empty();
                        } catch (Exception e) {
                            log.error("检查到期提醒失败: recordId={}", record.getMemberCardRecordId(), e);
                            return Mono.empty();
                        }
                    })
                    .then()
                    .subscribe();
            })
        ).subscribe();

        log.info("到期提醒兜底检查任务完成");
    }
}
