package cn.iocoder.yudao.module.bpm.service.meeting.reserve;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.module.bpm.controller.admin.meeting.reserve.vo.OaMeetingReservePageReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.meeting.reserve.vo.OaMeetingReserveSaveReqVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.meeting.reserve.OaMeetingReserveDO;
import cn.iocoder.yudao.module.bpm.dal.mysql.meeting.reserve.OaMeetingReserveMapper;
import cn.iocoder.yudao.module.bpm.enums.MeetingReserveStatusEnum;
import cn.iocoder.yudao.module.employee.api.EmployeeApi;
import cn.iocoder.yudao.module.employee.api.dto.EmployeeRespDTO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;

/**
 * 会议室预定管理信息 Service 实现类
 *
 * @author dp
 */
@Service
@Validated
public class OaMeetingReserveServiceImpl implements OaMeetingReserveService {

    @Resource
    private OaMeetingReserveMapper oaMeetingReserveMapper;

    @Resource
    private EmployeeApi employeeApi;

    @Override
    public Long createOaMeetingReserve(OaMeetingReserveSaveReqVO createReqVO) {
        Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
        // AdminUserRespDTO user = adminUserApi.getUser(loginUserId);
        EmployeeRespDTO loginEmployee = employeeApi.getEmployeeByUserId(loginUserId);

        // 校验时间
        String startTime = createReqVO.getStartTime();
        String endTime = createReqVO.getEndTime();
        LocalDateTime now = LocalDateTime.now();
        DateTimeFormatter pattern = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
        String nowTime = now.format(pattern);
        if (startTime.compareTo(endTime) >= 0) {
            throw exception(OA_MEETING_RESERVE_START_TIME_LESS_THAN_END_TIME);
        }
        if (startTime.compareTo(nowTime) < 0) {
            throw exception(OA_MEETING_RESERVE_START_TIME_MORE_THAN_NOW);
        }
        // 校验时间是否已占用
        LambdaQueryWrapper<OaMeetingReserveDO> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(OaMeetingReserveDO::getRoomId, createReqVO.getRoomId())
                // .ne(OaMeetingReserveDO::getStatus, MeetingReserveStatusEnum.ENDED.getStatus())
                .ne(OaMeetingReserveDO::getStatus, MeetingReserveStatusEnum.CANCELED.getStatus())
                .and(wrapper -> wrapper.eq(OaMeetingReserveDO::getStartTime, startTime)
                        .or().eq(OaMeetingReserveDO::getEndTime, endTime)
                        .or().between(OaMeetingReserveDO::getStartTime, startTime, endTime)
                        .or().between(OaMeetingReserveDO::getEndTime, startTime, endTime)
                        .or(wrapper2 -> wrapper2.lt(OaMeetingReserveDO::getStartTime, startTime)
                                .gt(OaMeetingReserveDO::getEndTime, endTime))
                        );
        if (oaMeetingReserveMapper.selectCount(lambdaQueryWrapper) > 0) {
            throw exception(OA_MEETING_RESERVE_TIME_OCCUPIED);
        }
        // 插入
        OaMeetingReserveDO oaMeetingReserve = BeanUtils.toBean(createReqVO, OaMeetingReserveDO.class);
        oaMeetingReserve.setReserveUuid(IdUtil.fastSimpleUUID());
        oaMeetingReserve.setApplyEmployeeId(loginEmployee.getId());
        oaMeetingReserve.setDeptId(loginEmployee.getDeptId());
        oaMeetingReserve.setApplyEmployeePhone(loginEmployee.getPhone());
        List<Long> participantsIdList = createReqVO.getParticipants();
        if (CollUtil.isNotEmpty(participantsIdList)) {
            String participants = participantsIdList.stream().map(String::valueOf).collect(Collectors.joining(","));
            oaMeetingReserve.setParticipants(participants);
            oaMeetingReserve.setNumber(String.valueOf(participantsIdList.size()));
        } else {
            oaMeetingReserve.setParticipants(null);
            oaMeetingReserve.setNumber("0");
        }
        oaMeetingReserve.setStatus(MeetingReserveStatusEnum.WAITING.getStatus());
        oaMeetingReserveMapper.insert(oaMeetingReserve);
        // 返回
        return oaMeetingReserve.getId();
    }

    @Override
    public void updateOaMeetingReserve(OaMeetingReserveSaveReqVO updateReqVO) {
        Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
        // AdminUserRespDTO user = adminUserApi.getUser(loginUserId);
        EmployeeRespDTO loginEmployee = employeeApi.getEmployeeByUserId(loginUserId);

        // 校验存在
        OaMeetingReserveDO oaMeetingReserveDOOld = validateOaMeetingReserveExists(updateReqVO.getId());
        String startTimeOld = oaMeetingReserveDOOld.getStartTime();

        // 校验时间
        String startTime = updateReqVO.getStartTime();
        String endTime = updateReqVO.getEndTime();
        LocalDateTime now = LocalDateTime.now();
        DateTimeFormatter pattern = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
        String nowTime = now.format(pattern);
        if (startTime.compareTo(endTime) >= 0) {
            throw exception(OA_MEETING_RESERVE_START_TIME_LESS_THAN_END_TIME);
        }
        if (startTime.compareTo(nowTime) < 0) {
            throw exception(OA_MEETING_RESERVE_START_TIME_MORE_THAN_NOW);
        }

        // 会议开始前5分钟后不能修改
        LocalDateTime fiveMinutesAgo = now.plus(5, ChronoUnit.MINUTES);
        String fiveMinutesAgoTime = fiveMinutesAgo.format(pattern);
        if (fiveMinutesAgoTime.compareTo(startTimeOld) >= 0) {
            throw exception(OA_MEETING_RESERVE_START_TIME_MORE_THAN_FIVE_MINUTES);
        }

        // 校验时间是否已占用
        LambdaQueryWrapper<OaMeetingReserveDO> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(OaMeetingReserveDO::getRoomId, updateReqVO.getRoomId())
                // .ne(OaMeetingReserveDO::getStatus, MeetingReserveStatusEnum.ENDED.getStatus())
                .ne(OaMeetingReserveDO::getStatus, MeetingReserveStatusEnum.CANCELED.getStatus())
                .ne(OaMeetingReserveDO::getId, updateReqVO.getId())
                .and(wrapper -> wrapper.eq(OaMeetingReserveDO::getStartTime, startTime)
                        .or().eq(OaMeetingReserveDO::getEndTime, endTime)
                        .or().between(OaMeetingReserveDO::getStartTime, startTime, endTime)
                        .or().between(OaMeetingReserveDO::getEndTime, startTime, endTime)
                        .or(wrapper2 -> wrapper2.lt(OaMeetingReserveDO::getStartTime, startTime)
                                .gt(OaMeetingReserveDO::getEndTime, endTime))
                );
        if (oaMeetingReserveMapper.selectCount(lambdaQueryWrapper) > 0) {
            throw exception(OA_MEETING_RESERVE_TIME_OCCUPIED);
        }

        // 更新
        OaMeetingReserveDO updateObj = BeanUtils.toBean(updateReqVO, OaMeetingReserveDO.class);
        updateObj.setApplyEmployeeId(loginEmployee.getId());
        updateObj.setDeptId(loginEmployee.getDeptId());
        updateObj.setApplyEmployeePhone(loginEmployee.getPhone());

        List<Long> participantsIdList = updateReqVO.getParticipants();
        if (CollUtil.isNotEmpty(participantsIdList)) {
            String participants = participantsIdList.stream().map(String::valueOf).collect(Collectors.joining(","));
            updateObj.setParticipants(participants);
            updateObj.setNumber(String.valueOf(participantsIdList.size()));
        } else {
            updateObj.setParticipants(null);
            updateObj.setNumber("0");
        }
        oaMeetingReserveMapper.updateById(updateObj);
    }

    @Override
    public void deleteOaMeetingReserve(Long id) {
        // 校验存在
        validateOaMeetingReserveExists(id);
        // 删除
        oaMeetingReserveMapper.deleteById(id);
    }

    private OaMeetingReserveDO validateOaMeetingReserveExists(Long id) {
        OaMeetingReserveDO oaMeetingReserveDO = oaMeetingReserveMapper.selectById(id);
        if (oaMeetingReserveDO == null) {
            throw exception(OA_MEETING_RESERVE_NOT_EXISTS);
        }
        return oaMeetingReserveDO;
    }

    @Override
    public OaMeetingReserveDO getOaMeetingReserve(Long id) {
        return oaMeetingReserveMapper.selectById(id);
    }

    @Override
    public PageResult<OaMeetingReserveDO> getOaMeetingReservePage(OaMeetingReservePageReqVO pageReqVO) {
        startMeetingList();
        endMeetingList();

        return oaMeetingReserveMapper.selectPage(pageReqVO);
    }

    @Override
    public void cancelOaMeetingReserve(Long id) {
        OaMeetingReserveDO oaMeetingReserveDO = validateOaMeetingReserveExists(id);
        String status = oaMeetingReserveDO.getStatus();
        if (!MeetingReserveStatusEnum.WAITING.getStatus().equals(status)) {
            throw exception(OA_MEETING_RESERVE_STATUS_NOT_WAITING_CAN_NOT_CANCEL);
        }
        oaMeetingReserveMapper.updateById(OaMeetingReserveDO.builder().id(id).status(MeetingReserveStatusEnum.CANCELED.getStatus()).build());
    }

    @Override
    public void startOaMeetingReserve(Long id) {
        OaMeetingReserveDO oaMeetingReserveDO = validateOaMeetingReserveExists(id);
        String status = oaMeetingReserveDO.getStatus();
        if (!MeetingReserveStatusEnum.WAITING.getStatus().equals(status)) {
            throw exception(OA_MEETING_RESERVE_STATUS_NOT_WAITING_CAN_NOT_START);
        }
        oaMeetingReserveMapper.updateById(OaMeetingReserveDO.builder().id(id).status(MeetingReserveStatusEnum.IN_PROGRESS.getStatus()).build());
    }

    @Override
    public void endOaMeetingReserve(Long id) {
        OaMeetingReserveDO oaMeetingReserveDO = validateOaMeetingReserveExists(id);
        String status = oaMeetingReserveDO.getStatus();
        if (!MeetingReserveStatusEnum.IN_PROGRESS.getStatus().equals(status)) {
            throw exception(OA_MEETING_RESERVE_STATUS_NOT_IN_PROGRESS_CAN_NOT_ENDED);
        }
        oaMeetingReserveMapper.updateById(OaMeetingReserveDO.builder().id(id).status(MeetingReserveStatusEnum.ENDED.getStatus()).build());
    }

    @Override
    public void updateOaMeetingReserveStatus(List<Long> ids, String status) {
        if (CollUtil.isEmpty(ids) || StrUtil.isBlank(status)) {
            return;
        }
        LambdaUpdateWrapper<OaMeetingReserveDO> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        lambdaUpdateWrapper.set(OaMeetingReserveDO::getStatus, status)
                .in(OaMeetingReserveDO::getId, ids);
        oaMeetingReserveMapper.update(lambdaUpdateWrapper);
    }


    // 自动启动会议
    private void startMeetingList() {
        LambdaQueryWrapper<OaMeetingReserveDO> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        Date date = new Date();
        // 格式化为 yyyy-MM-dd HH:mm
        String dateStr = DateUtil.format(date, "yyyy-MM-dd HH:mm");
        lambdaQueryWrapper.eq(OaMeetingReserveDO::getStatus, MeetingReserveStatusEnum.WAITING.getStatus())
                .le(OaMeetingReserveDO::getStartTime, dateStr);
        List<OaMeetingReserveDO> reserveDOList = oaMeetingReserveMapper.selectList(lambdaQueryWrapper);
        List<Long> reserveIdList = reserveDOList.stream().map(OaMeetingReserveDO::getId).collect(Collectors.toList());
        updateOaMeetingReserveStatus(reserveIdList, MeetingReserveStatusEnum.IN_PROGRESS.getStatus());
    }

    // 自动结束会议
    private void endMeetingList() {
        LambdaQueryWrapper<OaMeetingReserveDO> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        Date date = new Date();
        // 格式化为 yyyy-MM-dd HH:mm
        String dateStr = DateUtil.format(date, "yyyy-MM-dd HH:mm");
        lambdaQueryWrapper.eq(OaMeetingReserveDO::getStatus, MeetingReserveStatusEnum.IN_PROGRESS.getStatus())
                .lt(OaMeetingReserveDO::getEndTime, dateStr);
        List<OaMeetingReserveDO> reserveDOList = oaMeetingReserveMapper.selectList(lambdaQueryWrapper);
        List<Long> reserveIdList = reserveDOList.stream().map(OaMeetingReserveDO::getId).collect(Collectors.toList());
        updateOaMeetingReserveStatus(reserveIdList, MeetingReserveStatusEnum.ENDED.getStatus());
    }


    @Override
    public List<OaMeetingReserveDO> getOaMeetingReserveList(Long roomId, String month) throws ParseException {
        startMeetingList();
        endMeetingList();

        if (roomId == null || StrUtil.isBlank(month)) {
            return Collections.emptyList();
        }

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM");
        Date parse = sdf.parse(month);
        Calendar c1 = Calendar.getInstance();
        c1.setTime(parse);
        Date monthStart = c1.getTime();
        Calendar c2 = Calendar.getInstance();
        c2.setTime(parse);
        c2.add(Calendar.MONTH, 1);
        Date monthEnd = c2.getTime();
        String monthStartFormat = sdf.format(monthStart);
        String monthEndFormat = sdf.format(monthEnd);

        LambdaQueryWrapper<OaMeetingReserveDO> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(OaMeetingReserveDO::getRoomId, roomId)
                .gt(OaMeetingReserveDO::getStartTime, monthStartFormat)
                .lt(OaMeetingReserveDO::getStartTime, monthEndFormat);
        return oaMeetingReserveMapper.selectList(lambdaQueryWrapper);
    }

}