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

import cn.novalon.gym.manage.groupcourse.dto.GroupCourseQueryDto;
import cn.novalon.gym.manage.groupcourse.entity.GroupCourseEntity;
import org.springframework.data.domain.Sort;
import org.springframework.data.r2dbc.repository.Modifying;
import org.springframework.data.r2dbc.repository.Query;
import org.springframework.data.r2dbc.repository.R2dbcRepository;
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;
import java.util.ArrayList;
import java.util.List;

@Repository
public interface GroupCourseDao extends R2dbcRepository<GroupCourseEntity, Long> {

    Mono<GroupCourseEntity> findByIdIsAndDeletedAtIsNull(Long id);

    Flux<GroupCourseEntity> findAll();

    Flux<GroupCourseEntity> findAll(Sort sort);

    Flux<GroupCourseEntity> findAllByDeletedAtIsNull();

    Flux<GroupCourseEntity> findAllByDeletedAtIsNull(Sort sort);

    @Modifying
    @Query("UPDATE group_course SET status = '1', updated_at = :updatedAt WHERE id = :id AND deleted_at IS NULL")
    Mono<Integer> cancelCourse(Long id, LocalDateTime updatedAt);

    @Modifying
    @Query("UPDATE group_course SET current_members = current_members + :delta, updated_at = :updatedAt WHERE id = :id AND deleted_at IS NULL")
    Mono<Integer> updateCurrentMembers(Long id, Integer delta, LocalDateTime updatedAt);

    @Modifying
    @Query("UPDATE group_course SET deleted_at = :deletedAt WHERE id = :id")
    Mono<Integer> softDelete(Long id, LocalDateTime deletedAt);

    Flux<GroupCourseEntity> findByCourseTypeAndDeletedAtIsNull(Long courseType);

    /**
     * 多条件查询团课（使用 DatabaseClient 构建动态 SQL）
     */
    default Flux<GroupCourseEntity> searchGroupCourses(DatabaseClient databaseClient, GroupCourseQueryDto query) {
        StringBuilder sql = new StringBuilder("SELECT * FROM group_course WHERE deleted_at IS NULL");
        List<String> conditions = new ArrayList<>();

        // 默认不查询不可预约的团课（仅查询 status = '0'）
        conditions.add("status = '0'");

        // 1. 团课名称模糊查询
        if (query.getCourseName() != null && !query.getCourseName().isEmpty()) {
            conditions.add("course_name ILIKE :courseName");
        }

        // 2. 基于团课类型查询
        if (query.getCourseType() != null) {
            conditions.add("course_type = :courseType");
        }

        // 3. 基于日期时间段查询
        if (query.getStartDate() != null) {
            conditions.add("start_time >= :startDate");
        }
        if (query.getEndDate() != null) {
            conditions.add("start_time <= :endDate");
        }

        // 4. 基于早晨/下午/夜晚时间段查询
        if (query.getTimePeriod() != null && !query.getTimePeriod().isEmpty()) {
            switch (query.getTimePeriod().toLowerCase()) {
                case "morning":
                    conditions.add("EXTRACT(HOUR FROM start_time) >= 6 AND EXTRACT(HOUR FROM start_time) < 12");
                    break;
                case "afternoon":
                    conditions.add("EXTRACT(HOUR FROM start_time) >= 12 AND EXTRACT(HOUR FROM start_time) < 18");
                    break;
                case "evening":
                    conditions.add("EXTRACT(HOUR FROM start_time) >= 18 AND EXTRACT(HOUR FROM start_time) < 24");
                    break;
                default:
                    break;
            }
        }

        sql.append(" AND ").append(String.join(" AND ", conditions));

        // 5. 价格排序 / 6. 剩余名额最多排序
        boolean hasPriceSort = query.getPriceSort() != null && !query.getPriceSort().isEmpty();
        boolean hasRemainingMost = query.getRemainingMost() != null && query.getRemainingMost();

        if (hasPriceSort || hasRemainingMost) {
            sql.append(" ORDER BY");
            List<String> orderClauses = new ArrayList<>();

            if (hasRemainingMost) {
                orderClauses.add(" (max_members - current_members) DESC");
            }

            if (hasPriceSort) {
                if ("asc".equalsIgnoreCase(query.getPriceSort())) {
                    orderClauses.add(" stored_value_amount ASC");
                } else if ("desc".equalsIgnoreCase(query.getPriceSort())) {
                    orderClauses.add(" stored_value_amount DESC");
                }
            }

            sql.append(String.join(",", orderClauses));
        } else {
            sql.append(" ORDER BY start_time ASC");
        }

        // 分页
        int page = query.getPage() != null ? query.getPage() : 0;
        int size = query.getSize() != null ? query.getSize() : 10;
        if (size < 1) size = 10;
        if (size > 100) size = 100;
        int offset = page * size;
        sql.append(" LIMIT :limit OFFSET :offset");

        DatabaseClient.GenericExecuteSpec spec = databaseClient.sql(sql.toString());

        if (query.getCourseName() != null && !query.getCourseName().isEmpty()) {
            spec = spec.bind("courseName", "%" + query.getCourseName() + "%");
        }
        if (query.getCourseType() != null) {
            spec = spec.bind("courseType", query.getCourseType());
        }
        if (query.getStartDate() != null) {
            spec = spec.bind("startDate", query.getStartDate());
        }
        if (query.getEndDate() != null) {
            spec = spec.bind("endDate", query.getEndDate());
        }
        spec = spec.bind("limit", size);
        spec = spec.bind("offset", offset);

        return spec.map((row, meta) -> {
            GroupCourseEntity entity = new GroupCourseEntity();
            entity.setId(row.get("id", Long.class));
            entity.setCourseName(row.get("course_name", String.class));
            entity.setCoachId(row.get("coach_id", Long.class));
            entity.setCourseType(row.get("course_type", Long.class));
            entity.setStartTime(row.get("start_time", LocalDateTime.class));
            entity.setEndTime(row.get("end_time", LocalDateTime.class));
            entity.setMaxMembers(row.get("max_members", Integer.class));
            entity.setCurrentMembers(row.get("current_members", Integer.class));
            String statusStr = row.get("status", String.class);
            entity.setStatus(statusStr != null ? Long.parseLong(statusStr) : null);
            entity.setLocation(row.get("location", String.class));
            entity.setCoverImage(row.get("cover_image", String.class));
            entity.setDescription(row.get("description", String.class));
            entity.setPointCardAmount(row.get("point_card_amount", Integer.class));
            entity.setStoredValueAmount(row.get("stored_value_amount", java.math.BigDecimal.class));
            entity.setCreateBy(row.get("create_by", String.class));
            entity.setUpdateBy(row.get("update_by", String.class));
            entity.setCreatedAt(row.get("created_at", LocalDateTime.class));
            entity.setUpdatedAt(row.get("updated_at", LocalDateTime.class));
            entity.setDeletedAt(row.get("deleted_at", LocalDateTime.class));
            return entity;
        }).all();
    }

    /**
     * 多条件查询团课总数
     */
    default Mono<Long> countSearchGroupCourses(DatabaseClient databaseClient, GroupCourseQueryDto query) {
        StringBuilder sql = new StringBuilder("SELECT COUNT(*) FROM group_course WHERE deleted_at IS NULL");
        List<String> conditions = new ArrayList<>();

        conditions.add("status = '0'");

        if (query.getCourseName() != null && !query.getCourseName().isEmpty()) {
            conditions.add("course_name ILIKE :courseName");
        }
        if (query.getCourseType() != null) {
            conditions.add("course_type = :courseType");
        }
        if (query.getStartDate() != null) {
            conditions.add("start_time >= :startDate");
        }
        if (query.getEndDate() != null) {
            conditions.add("start_time <= :endDate");
        }
        if (query.getTimePeriod() != null && !query.getTimePeriod().isEmpty()) {
            switch (query.getTimePeriod().toLowerCase()) {
                case "morning":
                    conditions.add("EXTRACT(HOUR FROM start_time) >= 6 AND EXTRACT(HOUR FROM start_time) < 12");
                    break;
                case "afternoon":
                    conditions.add("EXTRACT(HOUR FROM start_time) >= 12 AND EXTRACT(HOUR FROM start_time) < 18");
                    break;
                case "evening":
                    conditions.add("EXTRACT(HOUR FROM start_time) >= 18 AND EXTRACT(HOUR FROM start_time) < 24");
                    break;
                default:
                    break;
            }
        }

        sql.append(" AND ").append(String.join(" AND ", conditions));

        DatabaseClient.GenericExecuteSpec spec = databaseClient.sql(sql.toString());

        if (query.getCourseName() != null && !query.getCourseName().isEmpty()) {
            spec = spec.bind("courseName", "%" + query.getCourseName() + "%");
        }
        if (query.getCourseType() != null) {
            spec = spec.bind("courseType", query.getCourseType());
        }
        if (query.getStartDate() != null) {
            spec = spec.bind("startDate", query.getStartDate());
        }
        if (query.getEndDate() != null) {
            spec = spec.bind("endDate", query.getEndDate());
        }

        return spec.map((row, meta) -> row.get(0, Long.class)).one();
    }
}