package com.qmth.exam.reserve.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qmth.boot.core.concurrent.service.ConcurrentService;
import com.qmth.boot.core.exception.StatusException;
import com.qmth.exam.reserve.bean.applytask.CurrentApplyTaskVO;
import com.qmth.exam.reserve.bean.examsite.ExamSiteCacheBean;
import com.qmth.exam.reserve.bean.login.LoginUser;
import com.qmth.exam.reserve.bean.org.OrgInfo;
import com.qmth.exam.reserve.bean.timeperiod.TimePeriodExamSiteBean;
import com.qmth.exam.reserve.bean.timeperiod.TimePeriodExamSiteInfo;
import com.qmth.exam.reserve.bean.timeperiod.TimePeriodExamSiteReq;
import com.qmth.exam.reserve.bean.timeperiod.TimePeriodExamSiteVo;
import com.qmth.exam.reserve.cache.CacheConstants;
import com.qmth.exam.reserve.cache.impl.ApplyTaskCacheService;
import com.qmth.exam.reserve.cache.impl.ExamSiteCacheService;
import com.qmth.exam.reserve.cache.impl.OrgCacheService;
import com.qmth.exam.reserve.dao.TimePeriodExamRoomDao;
import com.qmth.exam.reserve.entity.ExamRoomEntity;
import com.qmth.exam.reserve.entity.TimePeriodEntity;
import com.qmth.exam.reserve.entity.TimePeriodExamRoomEntity;
import com.qmth.exam.reserve.enums.Role;
import com.qmth.exam.reserve.service.ExamRoomService;
import com.qmth.exam.reserve.service.ExamSiteService;
import com.qmth.exam.reserve.service.TimePeriodExamRoomService;
import com.qmth.exam.reserve.service.TimePeriodService;
import com.qmth.exam.reserve.util.DateUtil;
import com.qmth.exam.reserve.util.UnionUtil;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;

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

@Service
public class TimePeriodExamRoomServiceImpl extends ServiceImpl<TimePeriodExamRoomDao, TimePeriodExamRoomEntity> implements TimePeriodExamRoomService {

    private static final Logger log = LoggerFactory.getLogger(TimePeriodExamRoomServiceImpl.class);


    @Autowired
    private ApplyTaskCacheService applyTaskCacheService;

    @Autowired
    private OrgCacheService orgCacheService;

    @Autowired
    private TimePeriodService timePeriodService;

    @Autowired
    private ExamSiteCacheService examSiteCacheService;

    @Autowired
    private ExamRoomService examRoomService;

    @Autowired
    private ConcurrentService concurrentService;

    @Autowired
    private ExamSiteService examSiteService;

    @Autowired
    private TimePeriodExamRoomService timePeriodExamRoomService;


    @Override
    public List<TimePeriodExamSiteVo> ListDetail(LoginUser loginUser, Long examRoomId) {
        //获取当前机构
        OrgInfo org = orgCacheService.currentOrg();
        if (org == null) {
            log.warn("[考场排班设置列表]未找到当前机构");
            return Collections.emptyList();
        }

        //判断考场是否存在
        ExamRoomEntity examRoom = examRoomService.getById(examRoomId);
        if (examRoom == null) {
            log.warn("[考场排班设置列表]未找到考场:{}", examRoomId);
            return Collections.emptyList();
        }

        //教学点管理员权限限制
        ExamSiteCacheBean examSite = examSiteCacheService.getExamSiteById(examRoom.getExamSiteId());
        if (examSite == null) {
            log.warn("[考场排班设置列表]考场:{}未找到所属考点", examRoomId);
            return Collections.emptyList();
        }
        if (loginUser.getRole().equals(Role.TEACHING) && !loginUser.getCategoryId().equals(examSite.getCategoryId())) {
            return Collections.emptyList();
        }

        //获取当前任务
        CurrentApplyTaskVO curApplyTask = applyTaskCacheService.currentApplyTask(org.getOrgId());
        if (curApplyTask == null) {
            log.warn("[考场排班设置列表]机构:{},当前未有启用的任务", org.getOrgId());
            return Collections.emptyList();
        }

        // 所有的预约日期
        List<String> dateList = timePeriodService.listTimePeriodDate(curApplyTask.getTaskId());
        if (CollectionUtils.isEmpty(dateList)) {
            log.warn("[考场排班设置列表]当前任务:{}未设置预约日期", curApplyTask.getTaskId());
            return Collections.emptyList();
        }

        //教学点管理员设置的所有的考点时段
        List<TimePeriodExamSiteBean> timePeriodExamRoomList = timePeriodService.listTimePeriodByExamRoomId(curApplyTask.getTaskId(), examRoomId);
        //学校管理员设置的所有预约时段
        List<TimePeriodExamSiteBean> timePeriodList = timePeriodService.listTimePeriodByTask(curApplyTask.getTaskId());
        List<TimePeriodExamSiteBean> resultList;
        if (CollectionUtils.isEmpty(timePeriodExamRoomList)) {
            resultList = timePeriodList;
        } else {
            //取并集
            resultList = UnionUtil.unionByAttribute(timePeriodExamRoomList, timePeriodList, TimePeriodExamSiteBean::getTimePeriodId);
        }

        //按日期封装
        List<TimePeriodExamSiteVo> list = new ArrayList<>();
        for (String date : dateList) {
            TimePeriodExamSiteVo timePeriodVo = new TimePeriodExamSiteVo();
            timePeriodVo.setDateStr(getDateStr(date));
            timePeriodVo.setTimePeriodList(filterTimePeriod(date, resultList, curApplyTask.getAllowApplyCancelDays()));
            list.add(timePeriodVo);
        }
        return list;
    }

    private String getDateStr(String date) {
        if (StringUtils.isEmpty(date)) {
            return "";
        }
        String[] dateArr = date.split("-");
        if (dateArr.length != 3) {
            return "";
        }
        return dateArr[1] + "月" + dateArr[2] + "日";
    }


    private List<TimePeriodExamSiteInfo> filterTimePeriod(String date, List<TimePeriodExamSiteBean> timePeriodList, Integer canCancelDay) {
        // 参数校验
        if (timePeriodList == null || date == null || canCancelDay == null || canCancelDay < 0) {
            return new ArrayList<>();
        }

        List<TimePeriodExamSiteInfo> resultList = new ArrayList<>();

        // 过滤符合条件的时间段
        List<TimePeriodExamSiteBean> filteredTimePeriodList = timePeriodList.stream()
                .filter(item -> DateUtil.getShortDateByLongTime(item.getStartTime()).equals(date))
                .sorted(Comparator.comparing(TimePeriodExamSiteBean::getStartTime))
                .collect(Collectors.toList());

        // 计算可取消时间范围
        Long longToday = DateUtil.getLongTimeByDate(DateUtil.formatShortSplitDateString(new Date()) + " 00:00:00");
        Date today = new Date(longToday);
        Date otherDay = DateUtil.addValues(today, Calendar.DAY_OF_MONTH, canCancelDay);
        long longOtherDay = DateUtil.getLongTimeByDate(DateUtil.formatShortSplitDateString(otherDay) + " 23:59:59");

        // 当前时间
        long now = System.currentTimeMillis();

        for (TimePeriodExamSiteBean time : filteredTimePeriodList) {
            // 检查时间是否有效
            if (time.getStartTime() == null || time.getEndTime() == null) {
                continue; // 跳过无效时间段
            }

            TimePeriodExamSiteInfo bean = new TimePeriodExamSiteInfo();
            bean.setId(time.getId());
            bean.setTimePeriodId(time.getTimePeriodId());
            bean.setEnable(time.getEnable());
            bean.setTimePeriodStr(DateUtil.getStartToEndTime(time.getStartTime(), time.getEndTime()));

            // 判断是否可编辑
            boolean isEditable = isTimeEditable(now, time.getEndTime(), longOtherDay, time.getStartTime());
            bean.setEditable(isEditable);

            resultList.add(bean);
        }

        return resultList;
    }

    private boolean isTimeEditable(long now, long endTime, long longOtherDay, long startTime) {
        return !(endTime < now || startTime < longOtherDay);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void save(Long userId, Long examRoomId, List<TimePeriodExamSiteReq> timePeriodExamRoomList) {
        if (concurrentService.isLocked(CacheConstants.LOCK_AUTO_APPLY)) {
            log.warn("[考场排班保存]系统自动预约中，不允许操作考场排班！lockKey:{}", CacheConstants.LOCK_AUTO_APPLY);
            throw new StatusException("系统正在自动预约中，不允许修改");
        }

        if (CollectionUtils.isEmpty(timePeriodExamRoomList)) {
            log.warn("[考场排班设置]时间段列表为空");
            throw new StatusException("保存失败，时间段列表为空");
        }

        try {
            // 获取当前机构
            OrgInfo org = orgCacheService.currentOrg();
            if (org == null) {
                log.warn("[考场排班设置]未找到当前机构");
                throw new StatusException("保存失败，未找到当前机构");
            }

            // 获取当前任务
            CurrentApplyTaskVO curApplyTask = applyTaskCacheService.currentApplyTask(org.getOrgId());
            if (curApplyTask == null) {
                log.warn("[考场排班设置]机构:{},当前未有启用的任务", org.getOrgId());
                throw new StatusException("保存失败，未有启用的任务");
            }

            // 校验考场信息
            ExamRoomEntity examRoom = examRoomService.getById(examRoomId);
            if (examRoom == null || !examRoom.getEnable()) {
                log.warn("[考场排班设置]未找到或已被禁用的考场:{}", examRoomId);
                throw new StatusException("保存失败，考场不存在或已被禁用");
            }

            // 校验考点信息
            ExamSiteCacheBean examSiteCacheBean = examSiteCacheService.getExamSiteById(examRoom.getExamSiteId());
            if (examSiteCacheBean == null) {
                log.warn("[考场排班设置]未找到考点:{}", examRoom.getExamSiteId());
                throw new StatusException("保存失败，未找到考点");
            }

            String lockKey = String.format(CacheConstants.LOCK_EXAM_SITE_CAPACITY, examRoom.getExamSiteId());
            if (concurrentService.isLocked(lockKey)) {
                log.warn("[考场排班设置]考点剩余可约数量更新中，不允许操作修改！lockKey:{}", lockKey);
                throw new StatusException("系统正在更新可预约数量，不允许保存");
            }

            // 获取已有时段数据
            List<TimePeriodExamSiteBean> existingTimePeriods = timePeriodService.listTimePeriodByExamRoomId(curApplyTask.getTaskId(), examRoomId);
            Map<Long, TimePeriodExamSiteBean> existingMap = existingTimePeriods.stream()
                    .collect(Collectors.toMap(TimePeriodExamSiteBean::getId, t -> t));

            // 过滤出可编辑的数据
            List<TimePeriodExamSiteReq> editableList = timePeriodExamRoomList.stream()
                    .filter(TimePeriodExamSiteReq::getEditable)
                    .collect(Collectors.toList());

            if (editableList.isEmpty()) {
                log.warn("[考场排班设置]无可编辑数据");
                return;
            }

            // 构建实体列表
            List<TimePeriodExamRoomEntity> entityList = editableList.stream()
                    .map(req -> createTimePeriodExamRoomEntity(req, examRoomId, userId))
                    .collect(Collectors.toList());

            // 分离新增和更新
            List<TimePeriodExamRoomEntity> toBeSaved = new ArrayList<>();
            List<TimePeriodExamRoomEntity> toBeUpdated = new ArrayList<>();

            entityList.forEach(e -> {
                if (e.getId() == null) {
                    toBeSaved.add(e);
                } else {
                    toBeUpdated.add(e);
                }
            });

            // 检查是否可编辑
            List<Long> timePeriodIdsToEdit = new ArrayList<>();
            toBeSaved.forEach(e -> timePeriodIdsToEdit.add(e.getTimePeriodId()));
            toBeUpdated.forEach(e -> {
                TimePeriodExamSiteBean old = existingMap.get(e.getId());
                if (old != null && !old.getEnable().equals(e.getEnable())) {
                    timePeriodIdsToEdit.add(e.getTimePeriodId());
                }
            });
            canEdit(timePeriodIdsToEdit, curApplyTask);

            // 待更新、保存时段ID
            Set<Long> updatedTimePeriodIds = new HashSet<>();
            toBeSaved.forEach(e -> updatedTimePeriodIds.add(e.getTimePeriodId()));
            toBeUpdated.forEach(e -> updatedTimePeriodIds.add(e.getTimePeriodId()));

            // 保存前先记录旧容量
            Map<Long, Integer> oldCapacityMap = new HashMap<>();
            for (Long timePeriodId : updatedTimePeriodIds) {
                int capacity = examSiteService.getExamSiteTimePeriodCapacity(examSiteCacheBean.getExamSiteId(), timePeriodId);
                oldCapacityMap.put(timePeriodId, capacity);
            }

            // 批量保存或更新
            if (!toBeSaved.isEmpty()) {
                saveBatch(toBeSaved);
            }
            if (!toBeUpdated.isEmpty()) {
                updateBatchById(toBeUpdated);
            }

            // 提交事务后刷新缓存
            TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
                @Override
                public void afterCommit() {
                    for (Long timePeriodId : updatedTimePeriodIds) {
                        int oldCapacity = oldCapacityMap.getOrDefault(timePeriodId, 0);;
                        int newCapacity = examSiteService.getExamSiteTimePeriodCapacity(examSiteCacheBean.getExamSiteId(), timePeriodId);
                        applyTaskCacheService.refreshApplyAvailableCountCache(
                                examRoom.getExamSiteId(),
                                timePeriodId,
                                oldCapacity,
                                newCapacity
                        );
                    }
                }
            });

        } catch (Exception e) {
            log.error("[考场排班设置]保存失败，原因:{}", e.getMessage(), e);
            throw new StatusException(e.getMessage());
        }
    }

    @Override
    public List<ExamRoomEntity> listExamRoom(Long examSiteId, Long timePeriodId, Boolean enable) {
        return getBaseMapper().listExamRoom(examSiteId, timePeriodId, enable);
    }

    @Override
    public List<TimePeriodExamRoomEntity> listExamRoom(Long examRoomId) {
        LambdaQueryWrapper<TimePeriodExamRoomEntity> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(TimePeriodExamRoomEntity::getExamRoomId, examRoomId);
        return list(wrapper);
    }

    @Override
    public List<TimePeriodExamRoomEntity> listByExamRoomIdsAndTimePeriodId(List<Long> examRoomIds, Long timePeriodId) {
        return getBaseMapper().listByExamRoomIdsAndTimePeriodId(examRoomIds, timePeriodId);
    }

    // 校验是否可编辑
    private void canEdit(List<Long> timePeriodExamRoomList, CurrentApplyTaskVO curApplyTask) {
        if (CollectionUtils.isNotEmpty(timePeriodExamRoomList)) {
            Long longToday = DateUtil.getLongTimeByDate(DateUtil.formatShortSplitDateString(new Date()) + " 00:00:00");
            Date today = new Date(longToday);
            Date otherDay = DateUtil.addValues(today, Calendar.DAY_OF_MONTH, curApplyTask.getAllowApplyCancelDays());
            long longOtherDay = DateUtil.getLongTimeByDate(DateUtil.formatShortSplitDateString(otherDay) + " 23:59:59");
            // 当前时间
            long now = System.currentTimeMillis();
            List<TimePeriodEntity> timePeriodEntities = timePeriodService.listByIds(timePeriodExamRoomList);
            Map<Long, TimePeriodEntity> timePeriodEntityMap = timePeriodEntities.stream()
                    .collect(Collectors.toMap(TimePeriodEntity::getId, tpe -> tpe));

            for (Long timePeriodId : timePeriodExamRoomList) {
                TimePeriodEntity timePeriod = timePeriodEntityMap.get(timePeriodId);
                if (!isTimeEditable(now, timePeriod.getEndTime(), longOtherDay, timePeriod.getStartTime())) {
                    throw new StatusException("保存失败，" + DateUtil.getShortDateByLongTime(timePeriod.getStartTime()) + "可编辑时间范围已过");
                }
            }
        }
    }

    private TimePeriodExamRoomEntity createTimePeriodExamRoomEntity(TimePeriodExamSiteReq item, Long examRoomId, Long userId) {
        TimePeriodExamRoomEntity entity = new TimePeriodExamRoomEntity();
        BeanUtils.copyProperties(item, entity);
        entity.setExamRoomId(examRoomId);
        entity.setOperateId(userId);
        return entity;
    }

}
