package org.dromara.project.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import org.dromara.common.core.constant.HttpStatus;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.core.utils.ObjectUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.project.domain.BusAttendance;
import org.dromara.project.domain.BusLeave;
import org.dromara.project.domain.BusProjectTeam;
import org.dromara.project.domain.enums.BusAttendanceClockStatusEnum;
import org.dromara.project.domain.enums.BusAttendanceCommuterEnum;
import org.dromara.project.domain.enums.BusOpinionStatusEnum;
import org.dromara.project.domain.enums.BusReviewStatusEnum;
import org.dromara.project.domain.dto.leave.BusLeaveManagerReviewReq;
import org.dromara.project.domain.dto.leave.BusLeaveQueryReq;
import org.dromara.project.domain.vo.leave.BusLeaveVo;
import org.dromara.project.mapper.BusLeaveMapper;
import org.dromara.project.service.IBusAttendanceService;
import org.dromara.project.service.IBusLeaveService;
import org.dromara.project.service.IBusProjectTeamService;
import org.springframework.beans.BeanUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

/**
 * 施工人员请假申请Service业务层处理
 *
 * @author lcj
 * @date 2025-04-08
 */
@Service
public class BusLeaveServiceImpl extends ServiceImpl<BusLeaveMapper, BusLeave>
    implements IBusLeaveService {

    @Resource
    private IBusProjectTeamService projectTeamService;

    @Lazy
    @Resource
    private IBusAttendanceService attendanceService;

    /**
     * 查询施工人员请假申请
     *
     * @param id 主键
     * @return 施工人员请假申请
     */
    @Override
    public BusLeaveVo queryById(Long id) {
        return baseMapper.selectVoById(id);
    }

    /**
     * 分页查询施工人员请假申请列表
     *
     * @param req       查询条件
     * @param pageQuery 分页参数
     * @return 施工人员请假申请分页列表
     */
    @Override
    public TableDataInfo<BusLeaveVo> queryPageList(BusLeaveQueryReq req, PageQuery pageQuery) {
        Page<BusLeave> result = this.page(pageQuery.build(), buildQueryWrapper(req));
        return TableDataInfo.build(this.getVoPage(result));
    }

    /**
     * 查询符合条件的施工人员请假申请列表
     *
     * @param req 查询条件
     * @return 施工人员请假申请列表
     */
    @Override
    public List<BusLeaveVo> queryList(BusLeaveQueryReq req) {
        LambdaQueryWrapper<BusLeave> lqw = buildQueryWrapper(req);
        return baseMapper.selectVoList(lqw);
    }

    /**
     * 管理员审核施工人员请假申请
     *
     * @param req 管理员审核施工人员请假申请
     * @return 是否审核成功
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean managerReview(BusLeaveManagerReviewReq req) {
        Long id = req.getId();
        String managerOpinion = req.getManagerOpinion();
        // 判断该请假记录是否存在
        BusLeave oldLeave = this.getById(id);
        if (oldLeave == null) {
            throw new ServiceException("施工人员请假申请不存在", HttpStatus.NOT_FOUND);
        }
        // 如果已经审核过，则返回
        if (!BusOpinionStatusEnum.UNREAD.getValue().equals(oldLeave.getManagerOpinion())) {
            throw new ServiceException("该请假已审核，请勿重复操作", HttpStatus.BAD_REQUEST);
        }
        // 判断班组长是否审核通过
        String gangerOpinion = oldLeave.getGangerOpinion();
        if (!BusOpinionStatusEnum.PASS.getValue().equals(gangerOpinion)) {
            throw new ServiceException("请等待班组长审核通过后再进行操作", HttpStatus.BAD_REQUEST);
        }
        // todo 判断当前用户是否为项目管理员
        // 填充默认值，更新数据
        BusLeave leave = new BusLeave();
        leave.setId(id);
        leave.setManagerOpinion(managerOpinion);
        leave.setManagerExplain(req.getManagerExplain());
        leave.setManagerTime(new Date());
        leave.setRemark(req.getRemark());
        boolean result = this.updateById(leave);
        if (!result) {
            throw new ServiceException("更新管理员审核操作失败", HttpStatus.ERROR);
        }
        // 更新考勤表记录
        Date startTime = oldLeave.getStartTime();
        Date endTime = oldLeave.getEndTime();
        // 计算相差的时间
        long diffInMillis = endTime.getTime() - startTime.getTime();
        long day = TimeUnit.MILLISECONDS.toDays(diffInMillis) + 1;
        Long userId = oldLeave.getUserId();
        String userName = oldLeave.getUserName();
        Long projectId = oldLeave.getProjectId();
        // 遍历每一天
        List<BusAttendance> attendanceList = new ArrayList<>();
        for (long i = 0; i < day; i++) {
            BusAttendance attendance = new BusAttendance();
            attendance.setUserId(userId);
            attendance.setUserName(userName);
            attendance.setProjectId(projectId);
            Date date = DateUtils.addDays(startTime, (int) i);
            attendance.setLeaveId(id);
            attendance.setClockDate(date);
            attendance.setClockStatus(BusAttendanceClockStatusEnum.LEAVE.getValue());
            attendance.setCommuter(BusAttendanceCommuterEnum.ALLDAY.getValue());
            attendanceList.add(attendance);
        }
        boolean saveBatch = attendanceService.saveBatch(attendanceList);
        if (!saveBatch) {
            throw new ServiceException("更新考勤记录失败", HttpStatus.ERROR);
        }
        return true;
    }

    /**
     * 校验并批量删除施工人员请假申请信息
     *
     * @param ids     待删除的主键集合
     * @param isValid 是否进行有效性校验
     * @return 是否删除成功
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if (isValid) {
            // TODO 做一些业务上的校验,判断是否需要校验
        }
        return baseMapper.deleteByIds(ids) > 0;
    }

    /**
     * 获取施工人员请假申请视图对象
     *
     * @param leave 施工人员请假申请对象
     * @return 施工人员请假申请视图对象
     */
    @Override
    public BusLeaveVo getVo(BusLeave leave) {
        // 对象转封装类
        BusLeaveVo leaveVo = new BusLeaveVo();
        if (leave == null) {
            return leaveVo;
        }
        BeanUtils.copyProperties(leave, leaveVo);
        // 获取班组信息
        Long teamId = leave.getTeamId();
        if (teamId != null) {
            BusProjectTeam team = projectTeamService.lambdaQuery()
                .eq(BusProjectTeam::getId, teamId).select(BusProjectTeam::getTeamName).one();
            leaveVo.setTeamName(team.getTeamName());
        }
        // 添加审核状态
        String status = BusReviewStatusEnum.getEnumByOpinionStatus(leave.getGangerOpinion(), leave.getManagerOpinion());
        leaveVo.setStatus(status);
        return leaveVo;
    }

    /**
     * 获取施工人员请假申请查询条件封装
     *
     * @param req 查询条件
     * @return 查询条件封装
     */
    @Override
    public LambdaQueryWrapper<BusLeave> buildQueryWrapper(BusLeaveQueryReq req) {
        LambdaQueryWrapper<BusLeave> lqw = new LambdaQueryWrapper<>();
        if (req == null) {
            return lqw;
        }
        Long id = req.getId();
        Long userId = req.getUserId();
        String userName = req.getUserName();
        String leaveType = req.getLeaveType();
        Long gangerId = req.getGangerId();
        String gangerName = req.getGangerName();
        String gangerOpinion = req.getGangerOpinion();
        String managerOpinion = req.getManagerOpinion();
        Long projectId = req.getProjectId();
        Long teamId = req.getTeamId();
        // 模糊查询
        lqw.like(StringUtils.isNotBlank(userName), BusLeave::getUserName, userName);
        lqw.like(StringUtils.isNotBlank(gangerName), BusLeave::getGangerName, gangerName);
        // 精确查询
        lqw.eq(ObjectUtils.isNotEmpty(id), BusLeave::getId, id);
        lqw.eq(ObjectUtils.isNotEmpty(userId), BusLeave::getUserId, userId);
        lqw.eq(ObjectUtils.isNotEmpty(gangerId), BusLeave::getGangerId, gangerId);
        lqw.eq(StringUtils.isNotBlank(gangerOpinion), BusLeave::getGangerOpinion, gangerOpinion);
        lqw.eq(StringUtils.isNotBlank(managerOpinion), BusLeave::getManagerOpinion, managerOpinion);
        lqw.eq(ObjectUtils.isNotEmpty(projectId), BusLeave::getProjectId, projectId);
        lqw.eq(ObjectUtils.isNotEmpty(teamId), BusLeave::getTeamId, teamId);
        lqw.eq(StringUtils.isNotBlank(leaveType), BusLeave::getLeaveType, leaveType);
        return lqw;
    }

    /**
     * 获取施工人员请假申请分页对象视图
     *
     * @param leavePage 施工人员请假申请分页对象
     * @return 施工人员请假申请分页对象视图
     */
    @Override
    public Page<BusLeaveVo> getVoPage(Page<BusLeave> leavePage) {
        List<BusLeave> leaveList = leavePage.getRecords();
        Page<BusLeaveVo> leaveVoPage = new Page<>(
            leavePage.getCurrent(),
            leavePage.getSize(),
            leavePage.getTotal());
        if (CollUtil.isEmpty(leaveList)) {
            return leaveVoPage;
        }
        // 关联查询班组信息
        Set<Long> teamIdSet = leaveList.stream().map(BusLeave::getTeamId)
            .collect(Collectors.toSet());
        Map<Long, List<BusProjectTeam>> teamIdTeamMap = projectTeamService.listByIds(teamIdSet).stream()
            .collect(Collectors.groupingBy(BusProjectTeam::getId));
        List<BusLeaveVo> leaveVoList = leaveList.stream().map(leave -> {
            BusLeaveVo leaveVo = new BusLeaveVo();
            BeanUtils.copyProperties(leave, leaveVo);
            // 关联班组信息
            Long teamId = leave.getTeamId();
            String teamName = null;
            if (teamIdTeamMap.containsKey(teamId)) {
                teamName = projectTeamService.getVo(teamIdTeamMap.get(teamId).get(0)).getTeamName();
            }
            leaveVo.setTeamName(teamName);
            // 添加审核状态
            String status = BusReviewStatusEnum.getEnumByOpinionStatus(leave.getGangerOpinion(), leave.getManagerOpinion());
            leaveVo.setStatus(status);
            return leaveVo;
        }).toList();
        leaveVoPage.setRecords(leaveVoList);
        return leaveVoPage;
    }
}
