package com.ruoyi.course.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import com.ruoyi.course.domain.Classroom;
import com.ruoyi.course.domain.Clazz;
import com.ruoyi.course.domain.dto.ClassroomAssignmentDTO;
import com.ruoyi.course.mapper.ClazzMapper;
import com.ruoyi.course.mapper.ClassroomMapper;
import com.ruoyi.course.service.IClassroomService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <p>
 * 教室信息表 服务实现类
 * </p>
 *
 * @author xu
 * @since 2025-12-14
 */
@Service
@Slf4j
public class ClassroomServiceImpl extends ServiceImpl<ClassroomMapper, Classroom> implements IClassroomService {



    @Autowired
    private ClazzMapper clazzMapper;

    @Autowired
    private ClassroomMapper classroomMapper;

    /**
     * 验证班级-教室绑定完整性
     *
     * @return 验证结果
     */
    @Override
    public Map<String, Object> validateClassroomAssignments() {
        log.info("开始验证班级-教室绑定完整性...");

        Map<String, Object> result = new HashMap<>();
        List<String> warnings = new ArrayList<>();
        List<String> errors = new ArrayList<>();
        List<ClassroomAssignmentDTO> assignments = new ArrayList<>();

        // 1. 获取所有启用的班级
        LambdaQueryWrapper<Clazz> classWrapper = new LambdaQueryWrapper<>();
        classWrapper.eq(Clazz::getStatus, 1);
        List<Clazz> classes = clazzMapper.selectList(classWrapper);

        log.info("系统中共有 {} 个启用的班级", classes.size());

        // 2. 分类统计
        int classesWithClassroom = 0;
        int classesWithoutClassroom = 0;
        int invalidClassroomAssignments = 0;

        for (Clazz clazz : classes) {
            ClassroomAssignmentDTO dto = new ClassroomAssignmentDTO();
            dto.setClassId(clazz.getId());
            dto.setClassName(clazz.getClassName());
            dto.setGrade(clazz.getGrade());
            dto.setClassType(clazz.getClassType());
            dto.setStudentCount(clazz.getStudentCount());

            // 检查是否绑定了教室
            if (clazz.getClassroomId() == null) {
                classesWithoutClassroom++;
                dto.setHasClassroom(false);
                dto.setValidationStatus("未分配教室");
                warnings.add(String.format("班级 %s (%s) 未分配固定教室",
                        clazz.getClassName(), clazz.getGrade()));
            } else {
                classesWithClassroom++;
                dto.setClassroomId(clazz.getClassroomId());
                dto.setHasClassroom(true);

                // 验证教室是否存在且可用
                Classroom classroom = classroomMapper.selectById(clazz.getClassroomId());
                if (classroom == null) {
                    invalidClassroomAssignments++;
                    dto.setValidationStatus("教室不存在");
                    errors.add(String.format("班级 %s 分配的教室ID %d 不存在",
                            clazz.getClassName(), clazz.getClassroomId()));
                } else {
                    dto.setClassroomName(classroom.getRoomName());
                    dto.setClassroomType(classroom.getRoomType());
                    dto.setClassroomCapacity(classroom.getCapacity());

                    // 检查教室是否可用
                    if (classroom.getIsAvailable() != 1) {
                        invalidClassroomAssignments++;
                        dto.setValidationStatus("教室不可用");
                        errors.add(String.format("班级 %s 分配的教室 %s 不可用",
                                clazz.getClassName(), classroom.getRoomName()));
                    }
                    // 检查教室容量是否足够
                    else if (classroom.getCapacity() < clazz.getStudentCount()) {
                        dto.setValidationStatus("教室容量不足");
                        warnings.add(String.format("班级 %s (%d人) 分配的教室 %s 容量不足 (容量: %d)",
                                clazz.getClassName(), clazz.getStudentCount(),
                                classroom.getRoomName(), classroom.getCapacity()));
                    } else {
                        dto.setValidationStatus("正常");
                    }
                }
            }

            assignments.add(dto);
        }

        // 3. 构建结果
        result.put("totalClasses", classes.size());
        result.put("classesWithClassroom", classesWithClassroom);
        result.put("classesWithoutClassroom", classesWithoutClassroom);
        result.put("invalidClassroomAssignments", invalidClassroomAssignments);
        result.put("assignments", assignments);
        result.put("warnings", warnings);
        result.put("errors", errors);
        result.put("isValid", errors.isEmpty());

        log.info("班级-教室绑定验证完成:");
        log.info("  总班级数: {}", classes.size());
        log.info("  已分配教室: {}", classesWithClassroom);
        log.info("  未分配教室: {}", classesWithoutClassroom);
        log.info("  无效分配: {}", invalidClassroomAssignments);

        if (!errors.isEmpty()) {
            log.error("存在错误，需要处理:");
            errors.forEach(log::error);
        }

        if (!warnings.isEmpty()) {
            log.warn("存在警告:");
            warnings.forEach(log::warn);
        }

        return result;
    }

    /**
     * 为未分配教室的班级自动分配教室
     *
     * @param grade 指定年级（可选，null表示所有年级）
     * @return 分配结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Object> autoAssignClassrooms(String grade) {
        log.info("开始为班级自动分配教室, 年级: {}", grade != null ? grade : "全部");

        Map<String, Object> result = new HashMap<>();
        List<String> assignments = new ArrayList<>();
        List<String> failures = new ArrayList<>();

        int assignedCount = 0;
        int skippedCount = 0;
        int failedCount = 0;

        // 1. 获取未分配教室的班级
        LambdaQueryWrapper<Clazz> classWrapper = new LambdaQueryWrapper<>();
        classWrapper.eq(Clazz::getStatus, 1)
                .isNull(Clazz::getClassroomId);

        if (grade != null) {
            classWrapper.eq(Clazz::getGrade, grade);
        }

        List<Clazz> classes = clazzMapper.selectList(classWrapper);

        log.info("找到 {} 个未分配教室的班级", classes.size());

        if (classes.isEmpty()) {
            result.put("message", "没有需要分配教室的班级");
            return result;
        }

        // 2. 获取可用的教室（按类型分组）
        LambdaQueryWrapper<Classroom> classroomWrapper = new LambdaQueryWrapper<>();
        classroomWrapper.eq(Classroom::getIsAvailable, 1)
                .orderByAsc(Classroom::getCapacity); // 优先分配小教室

        List<Classroom> availableClassrooms = classroomMapper.selectList(classroomWrapper);

        // 按类型分组教室
        Map<String, List<Classroom>> classroomsByType = availableClassrooms.stream()
                .collect(Collectors.groupingBy(Classroom::getRoomType));

        // 3. 为每个班级分配教室
        for (Clazz clazz : classes) {
            try {
                // 确定需要的教室类型
                String requiredRoomType = getRequiredRoomTypeForClass(clazz);

                // 获取该类型的可用教室
                List<Classroom> suitableClassrooms = classroomsByType.get(requiredRoomType);
                if (suitableClassrooms == null || suitableClassrooms.isEmpty()) {
                    String message = String.format("班级 %s (%s) 没有可用的 %s 类型教室",
                            clazz.getClassName(), clazz.getGrade(), requiredRoomType);
                    failures.add(message);
                    failedCount++;
                    log.warn(message);
                    continue;
                }

                // 选择容量合适且最小的教室
                Classroom selectedClassroom = null;
                for (Classroom classroom : suitableClassrooms) {
                    // 容量要足够，但不要太大浪费资源
                    if (classroom.getCapacity() >= clazz.getStudentCount()) {
                        selectedClassroom = classroom;
                        break;
                    }
                }

                if (selectedClassroom == null) {
                    String message = String.format("班级 %s (%d人) 没有容量足够的 %s 教室",
                            clazz.getClassName(), clazz.getStudentCount(), requiredRoomType);
                    failures.add(message);
                    failedCount++;
                    log.warn(message);
                    continue;
                }

                // 分配教室
                clazz.setClassroomId(selectedClassroom.getId());
                clazzMapper.updateById(clazz);

                // 从可用列表中移除已分配的教室
                suitableClassrooms.remove(selectedClassroom);

                String assignment = String.format("班级 %s (%s, %d人) -> 教室 %s (%s, 容量: %d)",
                        clazz.getClassName(), clazz.getGrade(), clazz.getStudentCount(),
                        selectedClassroom.getRoomName(), selectedClassroom.getRoomType(),
                        selectedClassroom.getCapacity());

                assignments.add(assignment);
                assignedCount++;

                log.info(assignment);

            } catch (Exception e) {
                String error = String.format("为班级 %s 分配教室时发生错误: %s",
                        clazz.getClassName(), e.getMessage());
                failures.add(error);
                failedCount++;
                log.error(error, e);
            }
        }

        // 4. 统计结果
        skippedCount = classes.size() - assignedCount - failedCount;

        result.put("totalClasses", classes.size());
        result.put("assignedCount", assignedCount);
        result.put("failedCount", failedCount);
        result.put("skippedCount", skippedCount);
        result.put("assignments", assignments);
        result.put("failures", failures);
        result.put("success", failedCount == 0);

        log.info("自动分配教室完成:");
        log.info("  成功分配: {}", assignedCount);
        log.info("  分配失败: {}", failedCount);
        log.info("  跳过分配: {}", skippedCount);

        return result;
    }

    /**
     * 根据班级类型确定需要的教室类型
     */
    private String getRequiredRoomTypeForClass(Clazz clazz) {
        // 普通班和特长班的行政班教室都是普通教室
        // 实验班可能需要实验室，这里根据实际情况调整
        switch (clazz.getClassType()) {
            case "实验班":
                return "实验室"; // 实验班的行政班教室也可能是普通教室，根据需求调整
            case "特长班":
                // 特长班可能需要更大的教室或特殊设施
                return "多媒体教室";
            default:
                return "普通教室";
        }
    }

    /**
     * 手动为班级分配教室
     *
     * @param classId 班级ID
     * @param classroomId 教室ID
     * @return 分配结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean assignClassroomToClass(Long classId, Long classroomId) {
        log.info("为班级 {} 分配教室 {}", classId, classroomId);

        // 验证班级存在
        Clazz clazz = clazzMapper.selectById(classId);
        if (clazz == null) {
            log.error("班级不存在: {}", classId);
            throw new IllegalArgumentException("班级不存在");
        }

        // 验证教室存在且可用
        Classroom classroom = classroomMapper.selectById(classroomId);
        if (classroom == null) {
            log.error("教室不存在: {}", classroomId);
            throw new IllegalArgumentException("教室不存在");
        }

        if (classroom.getIsAvailable() != 1) {
            log.error("教室不可用: {}", classroomId);
            throw new IllegalArgumentException("教室不可用");
        }

        // 验证教室容量
        if (classroom.getCapacity() < clazz.getStudentCount()) {
            log.warn("教室 {} 容量 {} 小于班级 {} 人数 {}",
                    classroom.getRoomName(), classroom.getCapacity(),
                    clazz.getClassName(), clazz.getStudentCount());
            // 这里可以选择抛出异常或只是警告
            // throw new IllegalArgumentException("教室容量不足");
        }

        // 分配教室
        clazz.setClassroomId(classroomId);
        int updated = clazzMapper.updateById(clazz);

        if (updated > 0) {
            log.info("成功为班级 {} 分配教室 {}", clazz.getClassName(), classroom.getRoomName());
            return true;
        } else {
            log.error("为班级分配教室失败");
            return false;
        }
    }

    /**
     * 移除班级的教室分配
     *
     * @param classId 班级ID
     * @return 移除结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean removeClassroomFromClass(Long classId) {
        log.info("移除班级 {} 的教室分配", classId);

        Clazz clazz = clazzMapper.selectById(classId);
        if (clazz == null) {
            log.error("班级不存在: {}", classId);
            return false;
        }

        if (clazz.getClassroomId() == null) {
            log.info("班级 {} 本就没有分配教室", clazz.getClassName());
            return true;
        }

        clazz.setClassroomId(null);
        int updated = clazzMapper.updateById(clazz);

        if (updated > 0) {
            log.info("成功移除班级 {} 的教室分配", clazz.getClassName());
            return true;
        } else {
            log.error("移除班级教室分配失败");
            return false;
        }
    }

    /**
     * 批量分配教室（按年级）
     *
     * @param grade 年级
     * @param roomType 教室类型
     * @return 分配结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Object> batchAssignClassroomsByGrade(String grade, String roomType) {
        log.info("为 {} 年级批量分配 {} 类型教室", grade, roomType);

        Map<String, Object> result = new HashMap<>();
        List<String> assignments = new ArrayList<>();
        List<String> failures = new ArrayList<>();

        // 1. 获取该年级的所有班级
        LambdaQueryWrapper<Clazz> classWrapper = new LambdaQueryWrapper<>();
        classWrapper.eq(Clazz::getGrade, grade)
                .eq(Clazz::getStatus, 1);
        List<Clazz> classes = clazzMapper.selectList(classWrapper);

        // 2. 获取指定类型的可用教室
        LambdaQueryWrapper<Classroom> classroomWrapper = new LambdaQueryWrapper<>();
        classroomWrapper.eq(Classroom::getRoomType, roomType)
                .eq(Classroom::getIsAvailable, 1)
                .orderByAsc(Classroom::getCapacity);
        List<Classroom> classrooms = classroomMapper.selectList(classroomWrapper);

        if (classes.isEmpty()) {
            result.put("message", "没有找到符合条件的班级");
            return result;
        }

        if (classrooms.isEmpty()) {
            result.put("message", "没有找到符合条件的教室");
            return result;
        }

        int assignedCount = 0;
        int classroomIndex = 0;

        // 3. 按顺序分配教室
        for (Clazz clazz : classes) {
            // 跳过已分配教室的班级
            if (clazz.getClassroomId() != null) {
                log.info("班级 {} 已有教室分配，跳过", clazz.getClassName());
                continue;
            }

            // 如果教室用完，循环使用
            if (classroomIndex >= classrooms.size()) {
                String message = String.format("教室数量不足，已分配 %d 个班级，还有 %d 个班级未分配",
                        assignedCount, classes.size() - assignedCount);
                failures.add(message);
                log.warn(message);
                break;
            }

            Classroom classroom = classrooms.get(classroomIndex);

            // 检查教室容量
            if (classroom.getCapacity() < clazz.getStudentCount()) {
                String message = String.format("教室 %s 容量不足，跳过为班级 %s 分配",
                        classroom.getRoomName(), clazz.getClassName());
                failures.add(message);
                log.warn(message);
                classroomIndex++; // 尝试下一个教室
                continue;
            }

            try {
                // 分配教室
                clazz.setClassroomId(classroom.getId());
                clazzMapper.updateById(clazz);

                String assignment = String.format("班级 %s -> 教室 %s",
                        clazz.getClassName(), classroom.getRoomName());
                assignments.add(assignment);
                assignedCount++;

                log.info(assignment);

                // 移动到下一个教室
                classroomIndex++;

            } catch (Exception e) {
                String error = String.format("为班级 %s 分配教室失败: %s",
                        clazz.getClassName(), e.getMessage());
                failures.add(error);
                log.error(error, e);
            }
        }

        result.put("totalClasses", classes.size());
        result.put("assignedCount", assignedCount);
        result.put("availableClassrooms", classrooms.size());
        result.put("assignments", assignments);
        result.put("failures", failures);
        result.put("success", failures.isEmpty());

        log.info("批量分配完成: 共 {} 个班级，成功分配 {} 个", classes.size(), assignedCount);

        return result;
    }

    /**
     * 获取教室使用统计
     *
     * @return 统计信息
     */
    @Override
    public Map<String, Object> getClassroomUsageStatistics() {
        Map<String, Object> stats = new HashMap<>();

        // 1. 获取所有教室
        List<Classroom> allClassrooms = classroomMapper.selectList(null);

        // 2. 获取已分配教室的班级
        LambdaQueryWrapper<Clazz> classWrapper = new LambdaQueryWrapper<>();
        classWrapper.isNotNull(Clazz::getClassroomId)
                .eq(Clazz::getStatus, 1);
        List<Clazz> assignedClasses = clazzMapper.selectList(classWrapper);

        // 3. 按类型统计教室
        Map<String, Long> classroomsByType = allClassrooms.stream()
                .collect(Collectors.groupingBy(
                        Classroom::getRoomType,
                        Collectors.counting()
                ));

        // 4. 按类型统计已分配教室
        Map<String, Long> assignedClassroomsByType = assignedClasses.stream()
                .map(Clazz::getClassroomId)
                .distinct()
                .map(classroomMapper::selectById)
                .filter(classroom -> classroom != null)
                .collect(Collectors.groupingBy(
                        Classroom::getRoomType,
                        Collectors.counting()
                ));

        // 5. 计算教室使用率
        Map<String, String> usageRateByType = new HashMap<>();
        for (String type : classroomsByType.keySet()) {
            long total = classroomsByType.get(type);
            long assigned = assignedClassroomsByType.getOrDefault(type, 0L);
            double rate = total > 0 ? (double) assigned / total * 100 : 0;
            usageRateByType.put(type, String.format("%.1f%%", rate));
        }

        // 6. 统计结果
        stats.put("totalClassrooms", allClassrooms.size());
        stats.put("assignedClassrooms", assignedClasses.size());
        stats.put("availableClassrooms", allClassrooms.size() - assignedClasses.size());
        stats.put("classroomsByType", classroomsByType);
        stats.put("assignedClassroomsByType", assignedClassroomsByType);
        stats.put("usageRateByType", usageRateByType);

        // 7. 列出空闲教室
        List<Classroom> freeClassrooms = allClassrooms.stream()
                .filter(classroom -> {
                    // 检查是否有班级使用此教室
                    return assignedClasses.stream()
                            .noneMatch(clazz -> classroom.getId().equals(clazz.getClassroomId()));
                })
                .collect(Collectors.toList());

        stats.put("freeClassrooms", freeClassrooms);

        return stats;
    }

}
