package com.ruoyi.school.course.service;

import cn.hutool.core.date.DateUnit;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.page.RespPage;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.core.api.APIResponse;
import com.ruoyi.core.api.ApiResEnums;
import com.ruoyi.core.constants.SysConstant;
import com.ruoyi.core.utils.DateUtil;
import com.ruoyi.school.course.domain.*;
import com.ruoyi.school.course.domain.req.time.ReqSearchClaTime;
import com.ruoyi.school.course.domain.resp.time.*;
import com.ruoyi.school.course.enums.ClaTimeStatusEnums;
import com.ruoyi.school.course.mapper.ScBookCourseLogMapper;
import com.ruoyi.school.course.mapper.ScBookCourseMapper;
import com.ruoyi.school.course.mapper.ScClaTimeMapper;
import com.ruoyi.school.member.domain.ScMemberCardTypes;
import com.ruoyi.school.member.mapper.ScMemberCardTypesMapper;
import com.ruoyi.school.room.domain.ScRoom;
import com.ruoyi.school.room.mapper.ScRoomMapper;
import com.ruoyi.school.room.service.IScRoomService;
import com.ruoyi.school.student.domain.req.ReqClaTimeAttend;
import com.ruoyi.school.student.domain.resp.RespCourseClaStudent;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;


@Service
@Transactional
public class BusinessClaTimeService {

    @Autowired
    private IScClaTimeService claTimeService;
    @Autowired
    private ScClaTimeMapper claTimeMapper;
    @Autowired
    private IScRoomService roomService;
    @Autowired
    private IScCourseService courseService;
    @Autowired
    private ScBookCourseMapper bookCourseMapper;
    @Autowired
    private ScRoomMapper roomMapper;
    @Autowired
    private ScBookCourseLogMapper bookCourseLogMapper;

    @Autowired
    private ScMemberCardTypesMapper cardTypesMapper;
    /**
     * 按周获取课表
     *
     * @param reqSearchClaTime
     * @return
     */
    public APIResponse searchListForCalendar(ReqSearchClaTime reqSearchClaTime) {
        String beginDate = reqSearchClaTime.getBeginDate();
        String endDate = reqSearchClaTime.getEndDate();
        Long deptId = reqSearchClaTime.getDeptId();

        if (StringUtils.isAnyEmpty(beginDate, endDate)) {
            return APIResponse.toOkResponse();
        }

        DateTime cycleBegin = DateUtil.yyyMMddDayBegin(beginDate);
        DateTime cycleEnd = DateUtil.yyyMMddDayEnd(endDate);

        if (1 != cycleBegin.getDayOfWeek() || 7 != cycleEnd.getDayOfWeek()) {
            return APIResponse.toOkResponse();
        }

        // 1. 查询真实课表数据
        List<RespClaTimeCalendar> respClaTimeList = claTimeMapper.selectListForCalendar(reqSearchClaTime);

        // 2. 根据返回的 开始时间(startHour) + 时长(claDuration) 自动生成时间段 MAP
        Map<Integer, String> AUTO_CLA_TIME_MAP = generateTimeMapFromData(respClaTimeList);

        // 3. 使用自动生成的时间段构建日历结构
        Map<Integer, Map<Integer, List<RespClaTimeCalendar>>> claTimeCalendarMap = Maps.newHashMap();
        AUTO_CLA_TIME_MAP.forEach((claTimeKey, claTimeValue) -> {
            Map<Integer, List<RespClaTimeCalendar>> weekDayMap = Maps.newHashMap();
            SysConstant.WEEK_DAY_MAP.forEach((weekDayKey, weekDay) -> {
                weekDayMap.put(weekDayKey, Lists.newArrayList());
            });
            claTimeCalendarMap.put(claTimeKey, weekDayMap);
        });
        // ======================================================
        // 日历数据  格式为：时间->星期->课程
//        Map<Integer, Map<Integer, List<RespClaTimeCalendar>>> claTimeCalendarMap = Maps.newHashMap();
//        SysConstant.CLA_TIME_MAP.forEach((claTimeKey, claTimeValue) -> {
//            Map<Integer, List<RespClaTimeCalendar>> weekDayMap = Maps.newHashMap();
//            SysConstant.WEEK_DAY_MAP.forEach((weekDayKey, weekDay) -> {
//                List<RespClaTimeCalendar> claTimeArrayList = Lists.newArrayList();
//                weekDayMap.put(weekDayKey, claTimeArrayList);
//            });
//            claTimeCalendarMap.put(claTimeKey, weekDayMap);
//        });
//
//
//        List<RespClaTimeCalendar> respClaTimeList = claTimeMapper.selectListForCalendar(reqSearchClaTime);

        // 将排课信息 入到 claTimeCalendarMap
        respClaTimeList.forEach(item -> {
            Integer weekDay = item.getWeekDay();
            Integer startHour = item.getStartHour();

            if (claTimeCalendarMap.containsKey(startHour)) {
                claTimeCalendarMap.get(startHour).get(weekDay).add(item);
            } else if (claTimeCalendarMap.containsKey(startHour - 1)) {
                // 每两个小时 一个上课时段，所以-1
                claTimeCalendarMap.get(startHour - 1).get(weekDay).add(item);
            }
        });


        RespBusinessClaTimeCalendar timeCalendar = new RespBusinessClaTimeCalendar();
        // 每行数据
        List<ClaTimeContainer> claTimeContainerList = Lists.newArrayList();
        // 星期排课数量
        Map<Integer, Integer> columnTitleTimeCountMap = Maps.newHashMap();

        // 每行数据
        claTimeCalendarMap.forEach((claTimeKey, claTimeMap) -> {
            ClaTimeContainer claTimeContainer = new ClaTimeContainer();
            claTimeContainer.setTime(AUTO_CLA_TIME_MAP.get(claTimeKey));

            Map<Integer, List<ClaTimeCalendarItem>> claTimeWeekDayMap = Maps.newHashMap();
            claTimeMap.forEach((weekDayKey, list) -> {

                // 排课数量
                if (columnTitleTimeCountMap.containsKey(weekDayKey)) {
                    columnTitleTimeCountMap.put(weekDayKey, columnTitleTimeCountMap.get(weekDayKey) + list.size());
                } else {
                    columnTitleTimeCountMap.put(weekDayKey, list.size());
                }

                List<ClaTimeCalendarItem> timeItemList = list.stream().map(item -> {
                    ClaTimeCalendarItem claTimeItem = new ClaTimeCalendarItem();
                    claTimeItem.transfer(item);
                    return claTimeItem;
                }).collect(Collectors.toList());
                claTimeWeekDayMap.put(weekDayKey, timeItemList);
            });
            claTimeContainer.setClaTimeWeekDayMap(claTimeWeekDayMap);
            claTimeContainerList.add(claTimeContainer);
        });
//        Collections.sort(claTimeContainerList, (o1, o2) -> {
//            int a = Integer.parseInt(o1.getTime().substring(0, 2));
//            int b = Integer.parseInt(o2.getTime().substring(0, 2));
//            if (a > b) {
//                return 1;
//            } else if (a < b) {
//                return -1;
//            } else {
//                return 0;
//            }
//        });
        // 按小时正序排序
        Collections.sort(claTimeContainerList, Comparator.comparingInt(o -> {
            String time = o.getTime();
            return Integer.parseInt(time.substring(0, 2));
        }));

        timeCalendar.setClaTimeContainer(claTimeContainerList);


        // 标题
        List<ClaTimeColumnTitle> columnTitles = Lists.newArrayList();
        while (cycleBegin.isBefore(cycleEnd)) {
            int dayOfWeek = cycleBegin.getDayOfWeek();

            ClaTimeColumnTitle claTimeColumnTitle = new ClaTimeColumnTitle();
            claTimeColumnTitle.setWeekName(SysConstant.WEEK_DAY_MAP.get(dayOfWeek));
            claTimeColumnTitle.setDay(cycleBegin.toString("MM-dd"));
            claTimeColumnTitle.setCount(columnTitleTimeCountMap.get(dayOfWeek));

            columnTitles.add(claTimeColumnTitle);
            cycleBegin = cycleBegin.plusDays(1);
        }
        timeCalendar.setColumnTitles(columnTitles);

        return APIResponse.toAPIResponse(timeCalendar);
    }

    /**
     * 获取最近几天的排课
     *
     * @param reqSearchClaTime
     * @return
     */
    public List<RespClaTimeCalendar> searchRecentDayTimeList(ReqSearchClaTime reqSearchClaTime) {
        return claTimeMapper.selectListForCalendar(reqSearchClaTime);
    }

    public APIResponse searchCourseClaStudent(ReqSearchClaTime reqSearchClaTime){
        String courseTimeId=reqSearchClaTime.getCourseTimeId();
        //预约的会员
        List<RespCourseClaStudent> scStudentList= claTimeMapper.selectStudenForClaTime(courseTimeId);
        return APIResponse.toAPIResponse(scStudentList);
    }


    /**
     * 获取会员的排课
     *
     * @param
     * @return
     */
    public APIResponse searchMemberId(ReqSearchClaTime reqSearchClaTime) {

        //会员卡 会员卡项 课程种类
        // 课程id
        List<ScCourse> courseList= cardTypesMapper.getCourseIdByMemberId(reqSearchClaTime.getStudentId());
        List<ScMemberCardTypes> memberCardTypes= cardTypesMapper.getCourseIdsByMemberId(reqSearchClaTime.getStudentId());

        List<Long> ids= courseList.stream().map(it -> it.getCourseId()).collect(Collectors.toList());
        HashSet<Long> courseIds=new HashSet<>();
        courseIds.addAll(ids);
        for (ScMemberCardTypes type : memberCardTypes) {
                if (type.getCourseTypes()!=null){
                    courseIds.addAll(Arrays.asList(type.getCourseTypes()));
                }
        }
        if (courseIds.isEmpty()){
            return APIResponse.toExceptionResponse("会员名下无课程");
        }
       reqSearchClaTime.setCourseIds(courseIds);
       return searchListForCalendar(reqSearchClaTime);
    }

    /**
     * 获取上课记录
     *
     * @param searchClaTime
     * @return
     */
    public RespPage<RespClaTime> selectListForAttend(ReqSearchClaTime searchClaTime) {
        RespPage<RespClaTime> page = new RespPage<>(1, 10);
        List<RespClaTime> claTimeList = claTimeMapper.selectListForAttend(searchClaTime, page);
        page.setRows(claTimeList);
        return page;
    }

    /**
     * 添加
     *
     * @param claTime
     * @return
     */
    public APIResponse addClaTime(ScClaTime claTime) {
        if (!claTime.checkAddParam()) {
            return APIResponse.toExceptionResponse(ApiResEnums.PARAM_FAIL);
        }
        // 教室
        if (null != claTime.getRoomId()) {
            ScRoom room = roomService.getById(claTime.getRoomId());
            claTime.setRoomName(room.getRoomName());
        }

        ScCourse course=courseService.getById(claTime.getCourseId());
        claTime.setSource("3");
        LoginUser loginUser = SecurityUtils.getLoginUser();
        claTime.setTeacherFee(course.getClaFee());
        claTime.setCreateUser(loginUser.getUserId());
        claTime.setCreateTime(new Date());
        claTime.setTenantId(SecurityUtils.getLoginUser().getNowTenantId());
        claTimeService.save(claTime);
        return APIResponse.toOkResponse();
    }

    /**
     * 详情
     *
     * @param courseTimeId
     * @return
     */
    public APIResponse detailById(Long courseTimeId) {
        if (null == courseTimeId) {
            return APIResponse.toAPIResponse(null);
        }
//        ScClaTime claTime = claTimeService.getById(courseTimeId);
        ScClaTimeVo scClaTimeVo =claTimeMapper.serlectByClaTimeId(courseTimeId);


//        ScClaTimeVo scClaTimeVo = new ScClaTimeVo();
//        BeanUtil.copyProperties(claTime,scClaTimeVo);

        return APIResponse.toAPIResponse(scClaTimeVo);
    }

    /**
     * 更新
     *
     * @param claTime
     * @return
     */
    public APIResponse updateClaTime(ScClaTimeVo claTime) {
        if (null == claTime.getCourseTimeId()) {
            return APIResponse.toExceptionResponse(ApiResEnums.PARAM_FAIL);
        }
        if (!claTime.checkUpdateParam()) {
            return APIResponse.toExceptionResponse(ApiResEnums.PARAM_FAIL);
        }

        if (
         claTimeService.checkTeacherConflict(claTime.getTeacherId(),
                 claTime.getClaDate(),
                 claTime.getStartTime(),
                 claTime.getEndTime(),
                 claTime.getCourseTimeId())
                        ||
         claTimeService.checkClassroomConflict(new Long(claTime.getRoomId()),
                 claTime.getClaDate(),
                 claTime.getEndTime(),
                 claTime.getEndTime(),
                 claTime.getCourseTimeId())
        ){
            throw new RuntimeException("存在教师或教室冲突，请重新选择上课时间");
        }

        LoginUser loginUser = SecurityUtils.getLoginUser();
        //是否有会员预约
        Integer bookCount= claTimeService.selectBookCount(null, claTime.getCourseTimeId());
        if(bookCount.intValue()>0){
            return APIResponse.toExceptionResponse("无法修改：有会员预约，请联系会员取消预约后再作修改！");

        }
        UpdateWrapper uw = new UpdateWrapper();
        uw.eq("course_time_id", claTime.getCourseTimeId());
        uw.eq("status", ClaTimeStatusEnums.WAIT_CLASS.getStatus());
        // 教室
        if (null != claTime.getRoomId()) {
            ScRoom room = roomService.getById(claTime.getRoomId());
            uw.set("room_name", room.getRoomName());
        }
        uw.set("cla_date", claTime.getClaDate());
        uw.set("cla_color", claTime.getClaColor());
        uw.set("font_size", claTime.getFontSize());
        uw.set("font_color", claTime.getFontColor());
        uw.set("start_time", claTime.getStartTime());
        uw.set("end_time", claTime.getEndTime());
        uw.set("teacher_id", claTime.getTeacherId());
        uw.set("room_id", claTime.getRoomId());
        uw.set("class_theme", claTime.getClassTheme());
        uw.set("last_update_user", loginUser.getUserId());
        uw.set("last_update_time", new Date());

        boolean update = claTimeService.update(uw);
        if(!update) {
            return APIResponse.toExceptionResponse("调课失败,请稍后重试");
        }

        return APIResponse.toOkResponse();
    }

    /**
     * 删除
     *
     * @param courseTimeId
     * @return
     */
    public APIResponse deleteById(Long courseTimeId) {
        if (null == courseTimeId) {
            return APIResponse.toExceptionResponse(ApiResEnums.FAIL_WAIT_A_MINUTE);
        }
        ScClaTime claTime = claTimeService.getById(courseTimeId);
        String status = claTime.getStatus();
        if ("2".equals(status)) {
            return APIResponse.toExceptionResponse("已上课,无法删除");
        }
        //是否有会员预约
        Integer bookCount= claTimeService.selectBookCount(null, claTime.getCourseTimeId().toString());
        if(bookCount.intValue()>0){
            return APIResponse.toExceptionResponse("无法删除：相关课表中有会员预约，请联系会员取消预约后再进行操作！");

        }
        claTimeService.removeById(courseTimeId);

        return APIResponse.toOkResponse();
    }

    //上课
    public APIResponse confirmCla(ReqClaTimeAttend reqClaTimeAttend){

        //本节课的预约
        List<ScBookCourse> bookCourses=bookCourseMapper.selectList(new QueryWrapper<ScBookCourse>()
                .in("book_status",0,1,2)
                .eq("course_time_id",reqClaTimeAttend.getCourseTimeId())
        );
        if (bookCourses.size()<1)throw new RuntimeException("无会员预约！");
        //判断预约状态（未确认）
//        Long needCheck= bookCourses.stream()
//                .filter(l-> StrUtil.containsAny("0,1",l.getBookStatus()+"")).count();
//        if (needCheck>0){
//            return new RestResponse().setSuccess(false).setMessage("请检查会员预约状态！");
//        }

        //判断是否有会员未签到
        Long notCheckIn=bookCourses.stream().filter(l->l.getCheckIn()==0).count();
        if (notCheckIn>0){
            return APIResponse.toExceptionResponse("请确认会员签到情况，如无法到课请联系会员取消预约！");
        }
        //签到会员
        List<ScBookCourse> checkIn=bookCourses.stream().filter(l->l.getCheckIn()==1).collect(Collectors.toList());

        ScClaTime claTime=claTimeMapper.selectById(reqClaTimeAttend.getCourseTimeId());
        //签到数量是否少于开课人数
        if (claTime.getLessCnt()>checkIn.size()){
            return APIResponse.toExceptionResponse("会员数量到不到开课标准！");
        }

        //教室使用时间及费用
        Date startTimeDate= cn.hutool.core.date.DateUtil.parseTime(reqClaTimeAttend.getRealStartTime()+":00");
        Date endTimeDate= cn.hutool.core.date.DateUtil.parseTime(reqClaTimeAttend.getRealEndTime()+":00");

        BigDecimal venueFee=new BigDecimal(0);
        double roomUsageTime=0.0;

        if (ObjectUtil.isNotEmpty(claTime.getRoomId())){
            ScRoom room= roomMapper.selectById(reqClaTimeAttend.getRoomId());
            //上课时间段  >0.5小时，按1小时计
            roomUsageTime= cn.hutool.core.date.DateUtil.between(startTimeDate, endTimeDate, DateUnit.MINUTE)/ 60.0;
            int c=(int)roomUsageTime;
            double d=roomUsageTime-c;
            if (d > 0.5) {
                roomUsageTime= c + 1.0;
            } else if (d > 0) {
                roomUsageTime= c + 0.5;
            }
            venueFee= room.getVenueFee().add(new BigDecimal(roomUsageTime));
        }

        //更改预约状态
        bookCourseMapper.update(null,new UpdateWrapper<ScBookCourse>()
                .eq("course_time_id",reqClaTimeAttend.getCourseTimeId())
                .eq("check_in","1")
                .eq("book_status",2)
                .set("book_status","4"));
        //添加预约状态记录
        for (ScBookCourse bookCours : checkIn) {
            addBookLogFromApp(4,bookCours.getId());
        }
        claTime.setRealClaDate(reqClaTimeAttend.getRealClaDate());
        claTime.setRealStartTime(reqClaTimeAttend.getRealStartTime());
        claTime.setRealEndTime(reqClaTimeAttend.getRealEndTime());
//        claTime.setTeacherId(reqClaTimeAttend.getTeacherId());
//        claTime.setRoomId(reqClaTimeAttend.getRoomId());
        claTime.setMemo(reqClaTimeAttend.getMemo());
//        claTime.setClassTheme(reqClaTimeAttend.getClassTheme());
        claTime.setVenueFee(venueFee);
        claTime.setRoomUsageTime(roomUsageTime);
        claTime.setStatus("2");
        claTime.setLastUpdateTime(new Date());
        claTimeMapper.updateById(claTime);

        return APIResponse.toOkResponse();
    }

    void addBookLogFromApp(int bookStatus,Long bookId){
        ScBookCourseLog log=new ScBookCourseLog();
        log.setBookStatus(bookStatus);
        log.setUpdateTime(new Date());
        log.setUpdateUser(SecurityUtils.getUserId());
        log.setBookId(bookId);
        bookCourseLogMapper.insert(log);
    }
    /**
     * 根据课表真实数据，自动生成【2小时间隔、不重叠、有序】的时间段 MAP
     * 规则：
     * 1. 每个时间段固定 2 小时
     * 2. 自动去重、不重叠、时间连续
     * 3. 只包含数据库中实际存在的上课时段
     */
    private Map<Integer, String> generateTimeMapFromData(List<RespClaTimeCalendar> respClaTimeList) {
        // TreeMap 自动按 key 升序排列
        Map<Integer, String> timeMap = new TreeMap<>();
        if (CollectionUtils.isEmpty(respClaTimeList)) {
            timeMap=SysConstant.CLA_TIME_MAP;
            return timeMap;
        }

        // 第一步：收集所有【合法的2小时制开始小时】（8、10、12、14、16、18...）
        Set<Integer> validStartHours = new HashSet<>();
        for (RespClaTimeCalendar item : respClaTimeList) {
            Integer startHour = item.getStartHour();
            if (startHour == null) continue;

            // 计算属于哪个2小时时段（核心：向下取整到偶数整点）
            int sectionStart = (startHour / 2) * 2;
            // 限制在合理范围 0~24
            if (sectionStart >= 0 && sectionStart <= 22) {
                validStartHours.add(sectionStart);
            }
        }

        // 第二步：生成 2小时间隔、不重叠 的时间段
        for (Integer start : validStartHours) {
            int end = start + 2;
            String startTime = String.format("%02d:00", start);
            String endTime = String.format("%02d:00", end);
            timeMap.put(start, startTime + " ~ " + endTime);
        }

        return timeMap;
    }
}
