package cn.novalon.gym.manage.datacount.dao;

import org.springframework.r2dbc.core.DatabaseClient;
import org.springframework.stereotype.Repository;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.time.LocalDateTime;

/**
 * 数据统计 DAO - 使用 DatabaseClient 执行跨表聚合查询
 *
 * @author system
 * @date 2026-06-09
 */
@Repository
public class DataStatisticsDao {

    private final DatabaseClient databaseClient;

    public DataStatisticsDao(DatabaseClient databaseClient) {
        this.databaseClient = databaseClient;
    }

    /**
     * 统计指定时间范围内新增会员数
     */
    public Mono<Long> countNewMembers(LocalDateTime startTime, LocalDateTime endTime) {
        return databaseClient.sql("SELECT COUNT(*) FROM member_user WHERE created_at >= :startTime AND created_at < :endTime AND is_deleted = false")
                .bind("startTime", startTime)
                .bind("endTime", endTime)
                .map(row -> row.get(0, Long.class))
                .one();
    }

    /**
     * 统计总会员数
     */
    public Mono<Long> countTotalMembers() {
        return databaseClient.sql("SELECT COUNT(*) FROM member_user WHERE is_deleted = false")
                .map(row -> row.get(0, Long.class))
                .one();
    }

    /**
     * 统计指定时间范围内的签到次数
     */
    public Mono<Long> countSignIns(LocalDateTime startTime, LocalDateTime endTime) {
        return databaseClient.sql("SELECT COUNT(*) FROM sign_in_record WHERE sign_in_time >= :startTime AND sign_in_time < :endTime AND is_delete = false")
                .bind("startTime", startTime)
                .bind("endTime", endTime)
                .map(row -> row.get(0, Long.class))
                .one();
    }

    /**
     * 统计指定时间范围内的成功签到次数
     */
    public Mono<Long> countSuccessSignIns(LocalDateTime startTime, LocalDateTime endTime) {
        return databaseClient.sql("SELECT COUNT(*) FROM sign_in_record WHERE sign_in_time >= :startTime AND sign_in_time < :endTime AND sign_in_status = 'SUCCESS' AND is_delete = false")
                .bind("startTime", startTime)
                .bind("endTime", endTime)
                .map(row -> row.get(0, Long.class))
                .one();
    }

    /**
     * 统计指定时间范围内签到的独立会员数
     */
    public Mono<Long> countDistinctSignInMembers(LocalDateTime startTime, LocalDateTime endTime) {
        return databaseClient.sql("SELECT COUNT(DISTINCT member_id) FROM sign_in_record WHERE sign_in_time >= :startTime AND sign_in_time < :endTime AND is_delete = false")
                .bind("startTime", startTime)
                .bind("endTime", endTime)
                .map(row -> row.get(0, Long.class))
                .one();
    }

    /**
     * 统计指定时间范围内的预约数
     */
    public Mono<Long> countBookings(LocalDateTime startTime, LocalDateTime endTime) {
        return databaseClient.sql("SELECT COUNT(*) FROM group_course_booking WHERE booking_time >= :startTime AND booking_time < :endTime AND deleted_at IS NULL")
                .bind("startTime", startTime)
                .bind("endTime", endTime)
                .map(row -> row.get(0, Long.class))
                .one();
    }

    /**
     * 统计指定时间范围内的取消预约数
     */
    public Mono<Long> countCancelBookings(LocalDateTime startTime, LocalDateTime endTime) {
        return databaseClient.sql("SELECT COUNT(*) FROM group_course_booking WHERE booking_time >= :startTime AND booking_time < :endTime AND status = '1' AND deleted_at IS NULL")
                .bind("startTime", startTime)
                .bind("endTime", endTime)
                .map(row -> row.get(0, Long.class))
                .one();
    }

    /**
     * 统计指定时间范围内的出席预约数
     */
    public Mono<Long> countAttendBookings(LocalDateTime startTime, LocalDateTime endTime) {
        return databaseClient.sql("SELECT COUNT(*) FROM group_course_booking WHERE booking_time >= :startTime AND booking_time < :endTime AND status = '2' AND deleted_at IS NULL")
                .bind("startTime", startTime)
                .bind("endTime", endTime)
                .map(row -> row.get(0, Long.class))
                .one();
    }

    /**
     * 统计指定时间范围内的缺席预约数
     */
    public Mono<Long> countAbsentBookings(LocalDateTime startTime, LocalDateTime endTime) {
        return databaseClient.sql("SELECT COUNT(*) FROM group_course_booking WHERE booking_time >= :startTime AND booking_time < :endTime AND status = '3' AND deleted_at IS NULL")
                .bind("startTime", startTime)
                .bind("endTime", endTime)
                .map(row -> row.get(0, Long.class))
                .one();
    }

    /**
     * 统计指定时间范围内有预约的独立会员数
     */
    public Mono<Long> countDistinctBookingMembers(LocalDateTime startTime, LocalDateTime endTime) {
        return databaseClient.sql("SELECT COUNT(DISTINCT member_id) FROM group_course_booking WHERE booking_time >= :startTime AND booking_time < :endTime AND deleted_at IS NULL")
                .bind("startTime", startTime)
                .bind("endTime", endTime)
                .map(row -> row.get(0, Long.class))
                .one();
    }

    /**
     * 统计指定时间范围内取消预约的独立会员数
     */
    public Mono<Long> countDistinctCancelMembers(LocalDateTime startTime, LocalDateTime endTime) {
        return databaseClient.sql("SELECT COUNT(DISTINCT member_id) FROM group_course_booking WHERE booking_time >= :startTime AND booking_time < :endTime AND status = '1' AND deleted_at IS NULL")
                .bind("startTime", startTime)
                .bind("endTime", endTime)
                .map(row -> row.get(0, Long.class))
                .one();
    }

    /**
     * 按签到类型统计次数
     */
    public Flux<SignInTypeCount> countSignInsByType(LocalDateTime startTime, LocalDateTime endTime) {
        return databaseClient.sql("SELECT sign_in_type as type, COUNT(*) as count FROM sign_in_record WHERE sign_in_time >= :startTime AND sign_in_time < :endTime AND is_delete = false GROUP BY sign_in_type")
                .bind("startTime", startTime)
                .bind("endTime", endTime)
                .map(row -> {
                    SignInTypeCount result = new SignInTypeCount();
                    result.setType(row.get("type", String.class));
                    result.setCount(row.get("count", Long.class));
                    return result;
                })
                .all();
    }

    /**
     * 签到类型计数结果
     */
    public static class SignInTypeCount {
        private String type;
        private Long count;

        public String getType() {
            return type;
        }

        public void setType(String type) {
            this.type = type;
        }

        public Long getCount() {
            return count;
        }

        public void setCount(Long count) {
            this.count = count;
        }
    }
}