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

import cn.novalon.gym.manage.groupcourse.domain.GroupCourse;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.ReactiveRedisTemplate;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;

import java.time.Duration;

/**
 * 团课Redis缓存服务
 * 
 * 负责团课信息的缓存管理和分布式锁实现
 * 
 * @author 张翔
 * @date 2026-06-01
 */
@Service
public class GroupCourseRedisService {

    private static final Logger logger = LoggerFactory.getLogger(GroupCourseRedisService.class);

    // 团课信息缓存Key前缀
    private static final String GROUP_COURSE_CACHE_PREFIX = "group_course:";
    // 团课预约锁Key前缀
    private static final String BOOKING_LOCK_PREFIX = "booking_lock:";
    // 缓存过期时间（5分钟）
    private static final Duration CACHE_EXPIRE_TIME = Duration.ofMinutes(5);
    // 锁过期时间（30秒）
    private static final Duration LOCK_EXPIRE_TIME = Duration.ofSeconds(30);

    private final ReactiveRedisTemplate<String, Object> reactiveRedisTemplate;
    private final ObjectMapper objectMapper;

    public GroupCourseRedisService(ReactiveRedisTemplate<String, Object> reactiveRedisTemplate,
                                   ObjectMapper objectMapper) {
        this.reactiveRedisTemplate = reactiveRedisTemplate;
        this.objectMapper = objectMapper;
    }

    /**
     * 获取团课缓存Key
     */
    private String getCourseCacheKey(Long courseId) {
        return GROUP_COURSE_CACHE_PREFIX + courseId;
    }

    /**
     * 获取预约锁Key
     */
    private String getBookingLockKey(Long courseId) {
        return BOOKING_LOCK_PREFIX + courseId;
    }

    /**
     * 缓存团课信息
     */
    public Mono<Void> cacheCourse(GroupCourse course) {
        String key = getCourseCacheKey(course.getId());
        try {
            String value = objectMapper.writeValueAsString(course);
            return reactiveRedisTemplate.opsForValue()
                    .set(key, value, CACHE_EXPIRE_TIME)
                    .doOnSuccess(result -> logger.debug("团课信息已缓存：courseId={}", course.getId()))
                    .then();
        } catch (JsonProcessingException e) {
            logger.error("序列化团课信息失败：courseId={}", course.getId(), e);
            return Mono.error(e);
        }
    }

    /**
     * 获取缓存的团课信息
     */
    public Mono<GroupCourse> getCachedCourse(Long courseId) {
        String key = getCourseCacheKey(courseId);
        return reactiveRedisTemplate.opsForValue()
                .get(key)
                .cast(String.class)
                .flatMap(value -> {
                    try {
                        GroupCourse course = objectMapper.readValue(value, GroupCourse.class);
                        logger.debug("从缓存获取团课信息：courseId={}", courseId);
                        return Mono.just(course);
                    } catch (JsonProcessingException e) {
                        logger.error("反序列化团课信息失败：courseId={}", courseId, e);
                        return Mono.empty();
                    }
                })
                .switchIfEmpty(Mono.fromRunnable(() -> logger.debug("缓存中未找到团课信息：courseId={}", courseId)));
    }

    /**
     * 删除团课缓存
     */
    public Mono<Void> invalidateCourseCache(Long courseId) {
        String key = getCourseCacheKey(courseId);
        return reactiveRedisTemplate.delete(key)
                .doOnSuccess(result -> logger.debug("团课缓存已删除：courseId={}", courseId))
                .then();
    }

    /**
     * 获取分布式锁
     * 
     * @param courseId 课程ID
     * @param requestId 请求ID（用于锁的唯一性校验）
     * @return 是否获取成功
     */
    public Mono<Boolean> acquireLock(Long courseId, String requestId) {
        String key = getBookingLockKey(courseId);
        return reactiveRedisTemplate.opsForValue()
                .setIfAbsent(key, requestId, LOCK_EXPIRE_TIME)
                .doOnSuccess(acquired -> {
                    if (acquired) {
                        logger.debug("获取预约锁成功：courseId={}, requestId={}", courseId, requestId);
                    } else {
                        logger.debug("获取预约锁失败：courseId={}", courseId);
                    }
                });
    }

    /**
     * 释放分布式锁
     * 
     * @param courseId 课程ID
     * @param requestId 请求ID（用于锁的唯一性校验）
     * @return 是否释放成功
     */
    public Mono<Boolean> releaseLock(Long courseId, String requestId) {
        String key = getBookingLockKey(courseId);
        return reactiveRedisTemplate.opsForValue()
                .get(key)
                .cast(String.class)
                .flatMap(storedRequestId -> {
                    if (requestId.equals(storedRequestId)) {
                        return reactiveRedisTemplate.delete(key)
                                .map(deleted -> deleted > 0)
                                .doOnSuccess(result -> logger.debug("释放预约锁成功：courseId={}, requestId={}", courseId, requestId));
                    } else {
                        logger.warn("锁归属校验失败：courseId={}, expectedRequestId={}, actualRequestId={}", 
                                courseId, requestId, storedRequestId);
                        return Mono.just(false);
                    }
                })
                .defaultIfEmpty(false);
    }

    /**
     * 获取课程预约人数（缓存）
     */
    public Mono<Integer> getBookingCount(Long courseId) {
        String key = "booking_count:" + courseId;
        return reactiveRedisTemplate.opsForValue()
                .get(key)
                .map(obj -> (Integer) obj)
                .defaultIfEmpty(0);
    }

    /**
     * 增加课程预约人数
     */
    public Mono<Long> incrementBookingCount(Long courseId) {
        String key = "booking_count:" + courseId;
        return reactiveRedisTemplate.opsForValue()
                .increment(key)
                .doOnSuccess(count -> logger.debug("预约人数增加：courseId={}, count={}", courseId, count));
    }

    /**
     * 减少课程预约人数
     */
    public Mono<Long> decrementBookingCount(Long courseId) {
        String key = "booking_count:" + courseId;
        return reactiveRedisTemplate.opsForValue()
                .decrement(key)
                .doOnSuccess(count -> logger.debug("预约人数减少：courseId={}, count={}", courseId, count));
    }

    /**
     * 设置课程预约人数（用于从数据库同步）
     */
    public Mono<Void> setBookingCount(Long courseId, Integer count) {
        String key = "booking_count:" + courseId;
        return reactiveRedisTemplate.opsForValue()
                .set(key, count)
                .doOnSuccess(result -> logger.debug("预约人数已设置：courseId={}, count={}", courseId, count))
                .then();
    }
}
