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

import cn.novalon.gym.manage.common.dto.PageRequest;
import cn.novalon.gym.manage.common.dto.PageResponse;
import cn.novalon.gym.manage.common.util.RedisUtil;
import cn.novalon.gym.manage.groupcourse.domain.CourseLabel;
import cn.novalon.gym.manage.groupcourse.domain.GroupCourse;
import cn.novalon.gym.manage.groupcourse.domain.GroupCourseBooking;
import cn.novalon.gym.manage.groupcourse.domain.GroupCourseDetail;
import cn.novalon.gym.manage.groupcourse.domain.GroupCourseType;
import cn.novalon.gym.manage.groupcourse.dto.GroupCourseQueryDto;
import cn.novalon.gym.manage.groupcourse.enums.CourseEvent;
import cn.novalon.gym.manage.groupcourse.enums.CourseStatus;
import cn.novalon.gym.manage.groupcourse.handler.GroupCourseStateMachine;
import cn.novalon.gym.manage.groupcourse.repository.ICourseLabelRepository;
import cn.novalon.gym.manage.groupcourse.repository.IGroupCourseBookingRepository;
import cn.novalon.gym.manage.groupcourse.repository.IGroupCourseRepository;
import cn.novalon.gym.manage.groupcourse.repository.IGroupCourseTypeRepository;
import cn.novalon.gym.manage.groupcourse.service.IGroupCourseService;
import cn.novalon.gym.manage.groupcourse.util.QRCodeUtil;
import cn.novalon.gym.manage.member.entity.MemberCard;
import cn.novalon.gym.manage.member.entity.MemberCardRecord;
import cn.novalon.gym.manage.member.enums.MemberCardType;
import cn.novalon.gym.manage.member.repository.MemberCardRepository;
import cn.novalon.gym.manage.member.service.IMemberCardRecordService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.r2dbc.core.DatabaseClient;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

@Service
public class GroupCourseService implements IGroupCourseService {
    private static final Logger logger = LoggerFactory.getLogger(GroupCourseService.class);
    
    private final IGroupCourseRepository groupCourseRepository;
    private final IGroupCourseBookingRepository bookingRepository;
    private final IGroupCourseTypeRepository groupCourseTypeRepository;
    private final ICourseLabelRepository courseLabelRepository;
    private final IMemberCardRecordService memberCardRecordService;
    private final MemberCardRepository memberCardRepository;
    private final RedisUtil redisUtil;
    private final ObjectMapper objectMapper;
    private final GroupCourseStateMachine stateMachine;
    private final DatabaseClient databaseClient;
    
    private static final String CACHE_KEY_PREFIX = "group_course:page:";
    private static final String CACHE_KEY_ID_PREFIX = "group_course:id:";
    private static final String CACHE_KEY_DETAIL_PREFIX = "group_course:detail:";
    private static final long CACHE_EXPIRE_SECONDS = 300;
    
    private static final double DEFAULT_GROUP_COURSE_PRICE = 50.0;
    
    public GroupCourseService(IGroupCourseRepository groupCourseRepository, 
                              IGroupCourseBookingRepository bookingRepository,
                              IGroupCourseTypeRepository groupCourseTypeRepository,
                              ICourseLabelRepository courseLabelRepository,
                              IMemberCardRecordService memberCardRecordService,
                              MemberCardRepository memberCardRepository,
                              RedisUtil redisUtil,
                              ObjectMapper objectMapper,
                              GroupCourseStateMachine stateMachine,
                              DatabaseClient databaseClient){
        this.groupCourseRepository = groupCourseRepository;
        this.bookingRepository = bookingRepository;
        this.groupCourseTypeRepository = groupCourseTypeRepository;
        this.courseLabelRepository = courseLabelRepository;
        this.memberCardRecordService = memberCardRecordService;
        this.memberCardRepository = memberCardRepository;
        this.redisUtil = redisUtil;
        this.objectMapper = objectMapper;
        this.stateMachine = stateMachine;
        this.databaseClient = databaseClient;
    }

    @Override
    public Mono<GroupCourseDetail> findDetailById(Long id) {
        String cacheKey = CACHE_KEY_DETAIL_PREFIX + id;
        
        return redisUtil.get(cacheKey, String.class)
                .flatMap(cachedJson -> {
                    if (cachedJson != null) {
                        try {
                            GroupCourseDetail detail = objectMapper.readValue(cachedJson, GroupCourseDetail.class);
                            logger.info("缓存命中 - findDetailById: id={}", id);
                            return Mono.just(detail);
                        } catch (JsonProcessingException e) {
                            logger.warn("缓存解析失败，删除缓存 - id: {}, error: {}", id, e.getMessage());
                            return redisUtil.delete(cacheKey).then(Mono.empty());
                        }
                    }
                    return Mono.empty();
                })
                .switchIfEmpty(
                    groupCourseRepository.findByIdAndDeletedAtIsNull(id)
                            .flatMap(course -> {
                                // 查询类型信息
                                Long courseTypeId = course.getCourseType();
                                
                                if (courseTypeId == null) {
                                    // 没有类型，直接构建详情
                                    return Mono.just(buildDetail(course, null));
                                }
                                
                                // 有类型，查询类型信息
                                return groupCourseTypeRepository.findById(courseTypeId)
                                        .flatMap(type -> {
                                            // 查询标签
                                            return courseLabelRepository.findByTypeId(type.getId())
                                                    .collectList()
                                                    .map(labels -> {
                                                        type.setLabels(labels);
                                                        return buildDetail(course, type);
                                                    });
                                        })
                                        .switchIfEmpty(Mono.just(buildDetail(course, null)));
                            })
                            .flatMap(detail -> {
                                try {
                                    String jsonData = objectMapper.writeValueAsString(detail);
                                    return redisUtil.setWithExpire(cacheKey, jsonData, CACHE_EXPIRE_SECONDS)
                                            .thenReturn(detail)
                                            .doOnSuccess(d -> logger.debug("缓存已设置 - findDetailById: id={}", id));
                                } catch (JsonProcessingException e) {
                                    logger.error("缓存设置失败 - id: {}, error: {}", id, e.getMessage());
                                    return Mono.just(detail);
                                }
                            })
                            .doOnSubscribe(sub -> logger.debug("缓存未命中，查询数据库 - findDetailById: id={}", id))
                );
    }

    /**
     * 构建团课完整信息对象
     */
    private GroupCourseDetail buildDetail(GroupCourse course, GroupCourseType type) {
        GroupCourseDetail detail = new GroupCourseDetail();
        detail.setId(course.getId());
        detail.setCourseName(course.getCourseName());
        detail.setCoachId(course.getCoachId());
        detail.setCourseType(course.getCourseType());
        detail.setStartTime(course.getStartTime());
        detail.setEndTime(course.getEndTime());
        detail.setMaxMembers(course.getMaxMembers());
        detail.setCurrentMembers(course.getCurrentMembers());
        detail.setStatus(course.getStatus());
        detail.setLocation(course.getLocation());
        detail.setCoverImage(course.getCoverImage());
        detail.setDescription(course.getDescription());
        detail.setPointCardAmount(course.getPointCardAmount());
        detail.setStoredValueAmount(course.getStoredValueAmount());
        detail.setQrCodePath(course.getQrCodePath());
        detail.setCreatedAt(course.getCreatedAt());
        detail.setUpdatedAt(course.getUpdatedAt());
        
        // 设置类型信息
        if (type != null) {
            detail.setTypeInfo(type);
        }
        
        return detail;
    }

    @Override
    public Mono<GroupCourse> findById(Long id) {
        String cacheKey = CACHE_KEY_ID_PREFIX + id;
        
        return redisUtil.get(cacheKey, String.class)
                .flatMap(cachedJson -> {
                    if (cachedJson != null) {
                        try {
                            GroupCourse groupCourse = objectMapper.readValue(cachedJson, GroupCourse.class);
                            logger.info("缓存命中 - findById: id={}", id);
                            return Mono.just(groupCourse);
                        } catch (JsonProcessingException e) {
                            logger.warn("缓存解析失败，删除缓存 - id: {}, error: {}", id, e.getMessage());
                            return redisUtil.delete(cacheKey).then(Mono.empty());
                        }
                    }
                    return Mono.empty();
                })
                .switchIfEmpty(
                    groupCourseRepository.findByIdAndDeletedAtIsNull(id)
                            .flatMap(groupCourse -> {
                                try {
                                    String jsonData = objectMapper.writeValueAsString(groupCourse);
                                    return redisUtil.setWithExpire(cacheKey, jsonData, CACHE_EXPIRE_SECONDS)
                                            .thenReturn(groupCourse)
                                            .doOnSuccess(gc -> logger.debug("缓存已设置 - findById: id={}", id));
                                } catch (JsonProcessingException e) {
                                    logger.error("缓存设置失败 - id: {}, error: {}", id, e.getMessage());
                                    return Mono.just(groupCourse);
                                }
                            })
                            .doOnSubscribe(sub -> logger.debug("缓存未命中，查询数据库 - findById: id={}", id))
                );
    }

    @Override
    public Flux<GroupCourse> findAll() {
        return groupCourseRepository.findAll();
    }

    @Override
    public Flux<GroupCourse> findAll(boolean includeDeleted) {
        if(includeDeleted){
            return groupCourseRepository.findAll();
        }else{
            return groupCourseRepository.findByDeletedAtIsNull();
        }
    }

    @Override
    public Mono<PageResponse<GroupCourse>> findByPage(PageRequest pageRequest, boolean includeDeleted) {
        int page = pageRequest.getPage();
        int size = pageRequest.getSize();
        String sort = pageRequest.getSort();
        String order = pageRequest.getOrder();
        String keyword = pageRequest.getKeyword() != null ? pageRequest.getKeyword() : "";
        
        String cacheKey = CACHE_KEY_PREFIX + page + ":" + size + ":" + includeDeleted + ":" + sort + ":" + order + ":" + keyword;

        return redisUtil.get(cacheKey, String.class)
                .flatMap(cachedJson -> {
                    if (cachedJson != null) {
                        try {
                            PageResponse<GroupCourse> pageResponse = objectMapper.readValue(cachedJson, 
                                objectMapper.getTypeFactory().constructParametricType(PageResponse.class, GroupCourse.class));
                            logger.info("缓存命中 - findByPage: key={}", cacheKey);
                            return Mono.just(pageResponse);
                        } catch (JsonProcessingException e) {
                            logger.warn("缓存解析失败，删除缓存 - key: {}, error: {}", cacheKey, e.getMessage());
                            return redisUtil.delete(cacheKey).then(Mono.empty());
                        }
                    }
                    return Mono.empty();
                })
                .switchIfEmpty(
                    Mono.defer(() -> {
                        logger.debug("缓存未命中，查询数据库 - findByPage: key={}", cacheKey);
                        Mono<PageResponse<GroupCourse>> resultMono;
                        if (includeDeleted) {
                            resultMono = groupCourseRepository.findByPage(pageRequest);
                        } else {
                            resultMono = groupCourseRepository.findByPageAndNotDeleted(pageRequest);
                        }
                        
                        return resultMono.flatMap(pageResponse -> {
                            try {
                                String jsonData = objectMapper.writeValueAsString(pageResponse);
                                return redisUtil.setWithExpire(cacheKey, jsonData, CACHE_EXPIRE_SECONDS)
                                        .thenReturn(pageResponse)
                                        .doOnSuccess(pr -> logger.debug("缓存已设置 - findByPage: key={}", cacheKey));
                            } catch (JsonProcessingException e) {
                                logger.error("缓存设置失败 - key: {}, error: {}", cacheKey, e.getMessage());
                                return Mono.just(pageResponse);
                            }
                        });
                    })
                );
    }

    @Override
    public Mono<GroupCourse> create(GroupCourse groupCourse) {
        return groupCourseRepository.save(groupCourse)
                .flatMap(course -> {
                    try {
                        // 生成二维码内容：团课基础信息JSON
                        Map<String, Object> qrCodeContent = new HashMap<>();
                        qrCodeContent.put("id", course.getId());
                        qrCodeContent.put("courseName", course.getCourseName());
                        qrCodeContent.put("coachId", course.getCoachId());
                        qrCodeContent.put("courseType", course.getCourseType());
                        qrCodeContent.put("startTime", course.getStartTime());
                        qrCodeContent.put("endTime", course.getEndTime());
                        qrCodeContent.put("maxMembers", course.getMaxMembers());
                        qrCodeContent.put("location", course.getLocation());
                        qrCodeContent.put("description", course.getDescription());
                        qrCodeContent.put("createdAt", course.getCreatedAt());
                        
                        String jsonContent = objectMapper.writeValueAsString(qrCodeContent);
                        
                        // 生成二维码并上传到阿里云OSS
                        String qrCodeUrl = QRCodeUtil.generateQRCodeAndUploadToOSS(jsonContent);
                        course.setQrCodePath(qrCodeUrl);
                        
                        logger.info("团课二维码上传到OSS成功 - id={}, qrCodeUrl={}", course.getId(), qrCodeUrl);
                        
                        // 更新团课信息，保存二维码路径
                        return groupCourseRepository.update(course)
                                .doOnSuccess(updatedCourse -> logger.info("团课创建成功 - id={}, name={}", updatedCourse.getId(), updatedCourse.getCourseName()));
                    } catch (Exception e) {
                        logger.error("团课二维码生成失败 - id={}, error: {}", course.getId(), e.getMessage(), e);
                        // 即使二维码生成失败，也返回成功创建的团课
                        return Mono.just(course);
                    }
                })
                .flatMap(course -> clearCache().thenReturn(course))
                .doOnError(error -> logger.error("团课创建失败 - error: {}", error.getMessage()));
    }

    @Override
    public Mono<GroupCourse> update(Long id, GroupCourse groupCourse) {
        return groupCourseRepository.findByIdAndDeletedAtIsNull(id)
                .switchIfEmpty(Mono.error(new RuntimeException("团课不存在")))
                .flatMap(existing -> {
                    if (groupCourse.getCourseName() != null) {
                        existing.setCourseName(groupCourse.getCourseName());
                    }
                    if (groupCourse.getCoachId() != null) {
                        existing.setCoachId(groupCourse.getCoachId());
                    }
                    if (groupCourse.getCourseType() != null) {
                        existing.setCourseType(groupCourse.getCourseType());
                    }
                    if (groupCourse.getStartTime() != null) {
                        existing.setStartTime(groupCourse.getStartTime());
                    }
                    if (groupCourse.getEndTime() != null) {
                        existing.setEndTime(groupCourse.getEndTime());
                    }
                    if (groupCourse.getMaxMembers() != null) {
                        existing.setMaxMembers(groupCourse.getMaxMembers());
                    }
                    if (groupCourse.getStatus() != null) {
                        existing.setStatus(groupCourse.getStatus());
                    }
                    if (groupCourse.getLocation() != null) {
                        existing.setLocation(groupCourse.getLocation());
                    }
                    if (groupCourse.getCoverImage() != null) {
                        existing.setCoverImage(groupCourse.getCoverImage());
                    }
                    if (groupCourse.getDescription() != null) {
                        existing.setDescription(groupCourse.getDescription());
                    }
                    if (groupCourse.getPointCardAmount() != null) {
                        existing.setPointCardAmount(groupCourse.getPointCardAmount());
                    }
                    if (groupCourse.getStoredValueAmount() != null) {
                        existing.setStoredValueAmount(groupCourse.getStoredValueAmount());
                    }
                    if (groupCourse.getQrCodePath() != null) {
                        existing.setQrCodePath(groupCourse.getQrCodePath());
                    }
                    return groupCourseRepository.update(existing);
                })
                .doOnSuccess(course -> logger.info("团课更新成功 - id={}", id))
                .flatMap(course -> clearCache().thenReturn(course))
                .doOnError(error -> logger.error("团课更新失败 - id={}, error: {}", id, error.getMessage()));
    }

    @Override
    public Mono<GroupCourse> cancel(Long id) {
        return groupCourseRepository.findByIdAndDeletedAtIsNull(id)
                .switchIfEmpty(Mono.error(new RuntimeException("团课不存在")))
                .flatMap(course -> {
                    // 使用状态机验证状态转换
                    return stateMachine.validateTransition(course, CourseEvent.CANCEL)
                            .then(Mono.just(course));
                })
                .flatMap(course -> {
                    LocalDateTime now = LocalDateTime.now();
                    // 检查当前时间是否超过课程开始前24小时
                    // 如果当前时间 > (课程开始时间 - 24小时)，则不允许取消
                    if (now.isAfter(course.getStartTime().minusHours(24))) {
                        return Mono.error(new RuntimeException("课程取消需提前24小时"));
                    }
                    
                    // 取消课程并返还权益
                    return groupCourseRepository.cancel(id)
                            .flatMap(canceledCourse -> {
                                // 为所有预约该课程的会员返还权益
                                return refundBookingMembers(canceledCourse.getId())
                                        .then(Mono.just(canceledCourse));
                            });
                })
                .doOnSuccess(course -> logger.info("团课取消成功 - id={}", id))
                .flatMap(course -> clearCache().thenReturn(course))
                .doOnError(error -> logger.error("团课取消失败 - id={}, error: {}", id, error.getMessage()));
    }

    /**
     * 为预约课程的所有会员返还权益
     * 
     * @param courseId 课程ID
     * @return 空Mono
     */
    private Mono<Void> refundBookingMembers(Long courseId) {
        logger.info("开始为课程预约会员返还权益: courseId={}", courseId);
        
        return groupCourseRepository.findByIdAndDeletedAtIsNull(courseId)
                .switchIfEmpty(Mono.error(new RuntimeException("课程不存在")))
                .flatMap(course -> {
                    Integer pointCardAmount = course.getPointCardAmount() != null ? course.getPointCardAmount() : 1;
                    Double storedValueAmount = course.getStoredValueAmount() != null ? 
                            course.getStoredValueAmount().doubleValue() : DEFAULT_GROUP_COURSE_PRICE;
                    
                    logger.info("课程权益信息: courseId={}, pointCardAmount={}, storedValueAmount={}", 
                            courseId, pointCardAmount, storedValueAmount);
                    
                    return bookingRepository.findByCourseId(courseId)
                            .filter(booking -> "0".equals(booking.getStatus())) // 只处理已预约状态的记录
                            .flatMap(booking -> {
                                Long memberCardRecordId = booking.getMemberCardRecordId();
                                if (memberCardRecordId == null) {
                                    logger.warn("预约记录没有会员卡记录ID: bookingId={}", booking.getId());
                                    return Mono.empty();
                                }
                                
                                // 根据会员卡类型返还权益
                                return refundCardUsage(booking.getId(), memberCardRecordId, pointCardAmount, storedValueAmount)
                                        .doOnSuccess(unused -> logger.info("会员权益返还成功: bookingId={}, memberId={}", 
                                                booking.getId(), booking.getMemberId()))
                                        .doOnError(error -> logger.error("会员权益返还失败: bookingId={}, memberId={}, error={}", 
                                                booking.getId(), booking.getMemberId(), error.getMessage()))
                                        .onErrorResume(e -> Mono.empty()); // 单个会员返还失败不影响其他会员
                            })
                            .then();
                });
    }

    /**
     * 根据会员卡类型返还权益
     * 
     * @param bookingId 预约ID
     * @param recordId 会员卡记录ID
     * @param pointCardAmount 次卡返还次数
     * @param storedValueAmount 储值卡返还金额
     * @return 空Mono
     */
    private Mono<Void> refundCardUsage(Long bookingId, Long recordId, Integer pointCardAmount, Double storedValueAmount) {
        return memberCardRecordService.findById(recordId)
                .switchIfEmpty(Mono.error(new RuntimeException("会员卡记录不存在")))
                .flatMap(record -> {
                    return memberCardRepository.findById(record.getMemberCardId())
                            .flatMap(card -> {
                                MemberCardType cardType = MemberCardType.valueOf(card.getMemberCardType());
                                
                                Mono<Void> refundOperation;
                                switch (cardType) {
                                    case COUNT_CARD:
                                        // 次卡返还次数（基于课程设置的pointCardAmount）
                                        logger.debug("返还次卡次数: recordId={}, amount={}", recordId, pointCardAmount);
                                        refundOperation = memberCardRecordService.renewCard(recordId, pointCardAmount, 0.0, record.getExpireTime())
                                                .then(Mono.empty());
                                        break;
                                    case STORED_VALUE_CARD:
                                        // 储值卡返还金额（基于课程设置的storedValueAmount）
                                        logger.debug("返还储值卡金额: recordId={}, amount={}", recordId, storedValueAmount);
                                        refundOperation = memberCardRecordService.renewCard(recordId, 0, storedValueAmount, record.getExpireTime())
                                                .then(Mono.empty());
                                        break;
                                    case TIME_CARD:
                                        // 时长卡不返还，仅修改预约记录状态
                                        logger.debug("时长卡不返还权益，仅更新预约状态: bookingId={}", bookingId);
                                        refundOperation = Mono.empty();
                                        break;
                                    default:
                                        return Mono.error(new RuntimeException("不支持的会员卡类型: " + cardType));
                                }
                                
                                // 无论哪种卡类型，都需要更新预约记录状态为已取消
                                return refundOperation.then(bookingRepository.updateStatus(bookingId, "1"))
                                        .flatMap(rows -> {
                                            if (rows == 0) {
                                                logger.warn("更新预约状态失败: bookingId={}", bookingId);
                                            }
                                            return Mono.empty();
                                        });
                            });
                });
    }

    @Override
    public Mono<GroupCourse> signIn(Long courseId, Long memberId) {
        return groupCourseRepository.findByIdAndDeletedAtIsNull(courseId)
                .switchIfEmpty(Mono.error(new RuntimeException("团课不存在或已删除")))
                .flatMap(course -> {
                    // 校验1：团课已取消
                    if (course.getStatus().equals(CourseStatus.CANCELLED.getValue())) {
                        return Mono.error(new RuntimeException("团课已取消，无法签到"));
                    }
                    
                    // 校验2：团课已结束
                    if (course.getStatus().equals(CourseStatus.ENDED.getValue())) {
                        return Mono.error(new RuntimeException("团课已结束，无法签到"));
                    }
                    
                    // 校验3：签到时间校验（开课前2小时内才能签到）
                    LocalDateTime now = LocalDateTime.now();
                    LocalDateTime signInStart = course.getStartTime().minusHours(2);
                    if (now.isBefore(signInStart)) {
                        return Mono.error(new RuntimeException("未到签到时间，请在开课前2小时内签到"));
                    }
                    if (now.isAfter(course.getEndTime())) {
                        return Mono.error(new RuntimeException("团课已结束，无法签到"));
                    }
                    
                    // 校验4：课程已满员
                    if (course.getCurrentMembers() >= course.getMaxMembers()) {
                        return Mono.error(new RuntimeException("课程已满员，无法签到"));
                    }
                    
                    // 校验5：用户今日是否已到店签到（直接查询sign_in_record表）
                    LocalDateTime todayStart = LocalDateTime.now().toLocalDate().atStartOfDay();
                    LocalDateTime todayEnd = todayStart.plusDays(1);
                    return databaseClient.sql("SELECT sign_in_status FROM sign_in_record WHERE member_id = :memberId AND sign_in_time >= :startTime AND sign_in_time < :endTime AND is_delete = false ORDER BY sign_in_time DESC LIMIT 1")
                            .bind("memberId", memberId)
                            .bind("startTime", todayStart)
                            .bind("endTime", todayEnd)
                            .map(row -> row.get("sign_in_status", String.class))
                            .one()
                            .switchIfEmpty(Mono.error(new RuntimeException("请先完成到店签到")))
                            .flatMap(status -> {
                                if (!"SUCCESS".equals(status)) {
                                    return Mono.error(new RuntimeException("到店签到未成功，请重新签到"));
                                }
                                // 校验6：用户已预约此课程（有效预约，状态为0-已预约）
                                return bookingRepository.findValidBooking(courseId, memberId)
                                        .switchIfEmpty(Mono.error(new RuntimeException("您未预约此课程，无法签到")))
                                        .flatMap(booking -> {
                                            return groupCourseRepository.updateCurrentMembers(courseId, 1)
                                                    .flatMap(updatedCourse -> {
                                                        return bookingRepository.updateStatus(booking.getId(), "2")
                                                                .thenReturn(updatedCourse);
                                                    });
                                        });
                            });
                })
                .doOnSuccess(course -> logger.info("团课签到成功 - courseId={}, memberId={}", courseId, memberId))
                .flatMap(course -> clearCache().thenReturn(course))
                .doOnError(error -> logger.error("团课签到失败 - courseId={}, memberId={}, error: {}", courseId, memberId, error.getMessage()));
    }

    @Override
    public Mono<Void> delete(Long id) {
        // 先查询课程状态，只有已取消的课程才能删除
        return groupCourseRepository.findByIdAndDeletedAtIsNull(id)
                .switchIfEmpty(Mono.error(new RuntimeException("团课不存在")))
                .flatMap(course -> {
                    // 检查课程状态是否为已取消（状态码1）
                    if (course.getStatus() == null || !course.getStatus().equals(CourseStatus.CANCELLED.getValue())) {
                        return Mono.error(new RuntimeException("只有已取消的课程才能删除，当前状态: " + 
                                (course.getStatus() != null ? course.getStatus() : "未知")));
                    }
                    
                    // 删除课程
                    return groupCourseRepository.deleteById(id)
                            .doOnSuccess(v -> logger.info("团课删除成功 - id={}", id))
                            .then(clearCache())
                            .doOnError(error -> logger.error("团课删除失败 - id={}, error: {}", id, error.getMessage()));
                });
    }

    @Override
    public Mono<PageResponse<GroupCourse>> searchGroupCourses(GroupCourseQueryDto query) {
        logger.info("多条件查询团课 - courseName={}, courseType={}, startDate={}, endDate={}, timePeriod={}, priceSort={}, remainingMost={}", 
                query.getCourseName(), query.getCourseType(), query.getStartDate(), query.getEndDate(), 
                query.getTimePeriod(), query.getPriceSort(), query.getRemainingMost());
        
        return groupCourseRepository.searchGroupCourses(query)
                .doOnSuccess(result -> logger.info("多条件查询结果 - total={}, page={}, size={}", 
                        result.getTotalElements(), result.getCurrentPage(), result.getPageSize()))
                .doOnError(error -> logger.error("多条件查询失败 - error: {}", error.getMessage()));
    }

    private Mono<Void> clearCache() {
        return redisUtil.deleteByPattern(CACHE_KEY_PREFIX + "*")
                .then(redisUtil.deleteByPattern(CACHE_KEY_ID_PREFIX + "*"))
                .then(redisUtil.deleteByPattern(CACHE_KEY_DETAIL_PREFIX + "*")).then();
    }
}
