package com.ruoyi.oa.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.ruoyi.common.helper.LoginHelper;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.core.domain.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.oa.domain.CalendarDay;
import com.ruoyi.oa.domain.vo.SysHolidayVo;
import com.ruoyi.oa.domain.vo.SysOaHolidayListVo;
import com.ruoyi.workflow.domain.FlowRecord;
import com.ruoyi.workflow.service.IWfTaskService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.oa.domain.bo.SysOaHolidayBo;
import com.ruoyi.oa.domain.vo.SysOaHolidayVo;
import com.ruoyi.oa.domain.SysOaHoliday;
import com.ruoyi.oa.mapper.SysOaHolidayMapper;
import com.ruoyi.oa.service.ISysOaHolidayService;

import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.YearMonth;
import java.time.ZoneId;
import java.util.*;

/**
 * 休假管理Service业务层处理
 *
 * @author liuzongkun999
 * @date 2025-02-20
 */
@RequiredArgsConstructor
@Service
public class SysOaHolidayServiceImpl implements ISysOaHolidayService {

    private final SysOaHolidayMapper baseMapper;

    @Autowired
    private IWfTaskService taskService;

    /**
     * 查询休假管理
     */
    @Override
    public SysOaHolidayVo queryById(Long holidayId){
        return baseMapper.selectVoById(holidayId);
    }

    /**
     * 查询休假管理列表
     */
    @Override
    public TableDataInfo<SysOaHolidayVo> queryPageList(SysOaHolidayBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<SysOaHoliday> lqw = buildQueryWrapper(bo);
        Page<SysOaHolidayVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        for (SysOaHolidayVo record : result.getRecords()) {
            record.setDate(record.getHolidayTime());
        }
        return TableDataInfo.build(result);
    }

    /**
     * 查询休假管理列表
     */
    @Override
    public List<SysOaHolidayVo> queryList(SysOaHolidayBo bo) {
        LambdaQueryWrapper<SysOaHoliday> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<SysOaHoliday> buildQueryWrapper(SysOaHolidayBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<SysOaHoliday> lqw = Wrappers.lambdaQuery();
        lqw.eq(bo.getType() != null, SysOaHoliday::getType, bo.getType());
        if (bo.getHolidayTime() != null) {
            lqw.between(SysOaHoliday::getHolidayTime, getFirstDay(bo.getHolidayTime()),getLastDay(bo.getHolidayTime()));
        }
        lqw.like(StringUtils.isNotBlank(bo.getName()), SysOaHoliday::getName, bo.getName());
        return lqw;
    }

    /**
     * 新增休假管理
     */
    @Override
    public Boolean insertByBo(SysOaHolidayBo bo) {
        SysOaHoliday add = BeanUtil.toBean(bo, SysOaHoliday.class);
        validEntityBeforeSave(add);
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setHolidayId(add.getHolidayId());
        }
        return flag;
    }

    /**
     * 修改休假管理
     */
    @Override
    public Boolean updateByBo(SysOaHolidayBo bo) {
        SysOaHoliday update = BeanUtil.toBean(bo, SysOaHoliday.class);
        validEntityBeforeSave(update);
        return baseMapper.updateById(update) > 0;
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(SysOaHoliday entity){
        //TODO 做一些数据校验,如唯一约束
    }

    /**
     * 批量删除休假管理
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if(isValid){
            //TODO 做一些业务上的校验,判断是否需要校验
        }
        return baseMapper.deleteBatchIds(ids) > 0;
    }

    /**
     * 查询指定日期的休假信息
     */
    @Override
    public SysOaHolidayVo queryHolidayByDate(Date date) {
        LambdaQueryWrapper<SysOaHoliday> lqw = Wrappers.lambdaQuery();
        lqw.like(SysOaHoliday::getHolidayTime, date);
        return baseMapper.selectVoOne(lqw);
    }

    /**
     * 根据节假日类别查询相应的休假
     */
    @Override
    public List<SysOaHolidayListVo> queryHolidayByType(Integer type) {
        LambdaQueryWrapper<SysOaHoliday> lqw = Wrappers.lambdaQuery();
        lqw.eq(SysOaHoliday::getType, type);
        lqw.orderByAsc(SysOaHoliday::getHolidayTime);
        //一、查询出所有该类型的休假
        List<SysOaHolidayVo> rawList = baseMapper.selectVoList(lqw);
        //二、对字段name一样的休假进行合并，并计算休假天数

        //  1、准备容器集合   List用于存储最终结果、Map用于存储合并后的休假天数
        List<SysOaHolidayVo> resultList = new ArrayList<>();
        Map<Long, Integer> mergedMap = new HashMap<>();
        for (SysOaHolidayVo raw : rawList) {
            if (resultList.isEmpty() || !raw.getName().equals(resultList.get(resultList.size() - 1).getName())) {
                // 如果当前休假名称与结果列表中的最后一个休假名称不一致，则将当前休假添加到结果列表中
                resultList.add(raw);
                // 存放休假天数
                mergedMap.put(raw.getHolidayId(),1);
            } else {
                // 如果当前休假名称与结果列表中的最后一个休假名称一致，则将当前休假的休假天数加一
                // 修改map集合里面对应的休假天数

                // 为前后天关系则
                if(raw.getHolidayTime() .getTime() - resultList.get(resultList.size() - 1).getHolidayTime().getTime() == 86400000L * mergedMap.get(resultList.get(resultList.size() - 1).getHolidayId())) {
                    mergedMap.compute(resultList.get(resultList.size() - 1).getHolidayId(), (k, days) -> days + 1);
                } else {
                    resultList.add(raw);
                    mergedMap.put(raw.getHolidayId(),1);
                }
            }
        }
        List<SysOaHolidayListVo> VoList = new ArrayList<>();
        // 2、遍历结果列表，将休假天数添加到结果列表中
        for (SysOaHolidayVo result : resultList) {
            Integer days = mergedMap.get(result.getHolidayId());
            // 计算休假结束时间
            Date endDate = DateUtils.addDays(result.getHolidayTime(), days - 1);
            VoList.add(new SysOaHolidayListVo(result.getHolidayId(), result.getName(), result.getHolidayTime(), endDate, days, result.getType()));
        }
        return VoList;
    }

    /**
     * 查询当前月的所有休假
     */
    @Override
    public List<SysOaHolidayVo> queryHolidayNowMonth() {
        //一、获取当前日期
        LocalDate now = LocalDate.now();
        YearMonth yearMonth = YearMonth.of(now.getYear(), now.getMonthValue());
        LocalDate firstDayOfMonth = yearMonth.atDay(1);
        LocalDate lastDayOfMonth = yearMonth.atEndOfMonth();
        //二、生成Date格式类型的首日与末日
        Date firstDay = Date.from(firstDayOfMonth.atStartOfDay(ZoneId.systemDefault()).toInstant());
        Date lastDay = Date.from(lastDayOfMonth.atStartOfDay(ZoneId.systemDefault()).toInstant());
        //三、查询当月的所有休假信息
        LambdaQueryWrapper<SysOaHoliday> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.between(SysOaHoliday::getHolidayTime, firstDay, lastDay);
        // 升序排序
        queryWrapper.orderByAsc(SysOaHoliday::getHolidayTime);
        return baseMapper.selectVoList(queryWrapper);
    }

    /**
     * 更新一段时间的休假状态
     */
    @Override
    public Boolean updateHolidayStatusByDate(Integer type, Date startDate, Date endDate) {
        LambdaQueryWrapper<SysOaHoliday> lqw = Wrappers.lambdaQuery();
        lqw.between(SysOaHoliday::getHolidayTime, startDate, endDate);
        //生成一个新实体去更新状态
        SysOaHoliday entity = new SysOaHoliday();
        entity.setType(type);
        int update = baseMapper.update(entity, lqw);
        return update > 0;
    }


    /**
     * 按照节假日分类方式查找列表
     * @param bo
     * @return list列表
     */
    @Override
    public List<SysHolidayVo> queryVoList(SysOaHolidayBo bo) {
        List<SysHolidayVo> list = baseMapper.queryVoList(bo);
        for (SysHolidayVo sysHolidayVo : list) {
            List<SysOaHoliday> holidayVoList = sysHolidayVo.getHolidayVoList();
            SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd");
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < holidayVoList.size(); i++) {
                // 格式化日期
                String formattedDate = SDF.format(holidayVoList.get(i).getHolidayTime());
                sb.append(formattedDate);
                // 在每个日期后面加逗号（最后一个日期后面可选）
                if (i < holidayVoList.size() - 1) {
                    sb.append(",");
                }
            }

            sysHolidayVo.setHolidayTimeString( sb.toString());
        }
        return list;
    }

    /**
     * 查询这个月的工作日数量
     * @param payTime
     * @return
     */
    @Override
    public Long queryWorkDayNumByMonth(Date payTime) {
        return   baseMapper.queryWorkDayNumByMonth(payTime);
    }

    @Override
    public List<CalendarDay> queryOwnList(SysOaHolidayBo bo, PageQuery pageQuery) {
        // 1) 查询普通节假日数据
        LambdaQueryWrapper<SysOaHoliday> lqw = buildQueryWrapper(bo);
        Page<SysOaHolidayVo> page = baseMapper.selectVoPage(pageQuery.build(), lqw);

        // 把结果先转换成 Map<String, CalendarDay>，方便后续“根据日期覆盖type”
        // key = "YYYY-MM-DD"
        Map<String, CalendarDay> dayMap = new HashMap<>();
        for (SysOaHolidayVo holiday : page.getRecords()) {
            String dateStr = formatDate(holiday.getHolidayTime()); // 转成 "YYYY-MM-DD"
            Integer type = holiday.getType();  // 0,1,2,3
            CalendarDay calendarDay = new CalendarDay();
            calendarDay.setDate(dateStr);
            calendarDay.setType(type);
            dayMap.put(dateStr, calendarDay);
        }

        // 2) 查询 Flowable - 出差(trip)流程
        //    假设 flowableQueryService.getApprovedRecords(userId, "trip")
        //    返回类似: List<FlowRecord>，其中有 startTime、endTime 等
        List<FlowRecord> tripRecords = taskService.getApprovedRecords(LoginHelper.getUserId(), "trip");
        // 每条记录都代表一段出差区间，逐天设置 type=4
        for (FlowRecord record : tripRecords) {
            fillDateRange(dayMap, record.getStartTime(), record.getEndTime(), 4);
        }

        // 3) 查询 Flowable - 请假(absence)流程
        List<FlowRecord> absenceRecords = taskService.getApprovedRecords(LoginHelper.getUserId(), "absence");
        // 每条记录都代表一段请假区间，逐天设置 type=5
        for (FlowRecord record : absenceRecords) {
            fillDateRange(dayMap, record.getStartTime(), record.getEndTime(), 5);
        }

        // 4) 最终返回列表 (若需要排序，可按日期排序)
        List<CalendarDay> result = new ArrayList<>(dayMap.values());
        result.sort(Comparator.comparing(CalendarDay::getDate));
        return result;
    }

    /**
     * 获得当月第一天
     */
    private Date getFirstDay(Date holidayTime){
        LocalDate localDate = holidayTime.toInstant()
                .atZone(ZoneId.systemDefault())
                .toLocalDate();
        LocalDate firstDay = localDate.withDayOfMonth(1);
        return Date.from(firstDay.atStartOfDay(ZoneId.systemDefault()).toInstant());
    }

    /**
     * 获得最后一天的最后时刻
     */
    private Date getLastDay(Date holidayTime){
        LocalDate localDate = holidayTime.toInstant()
                .atZone(ZoneId.systemDefault())
                .toLocalDate();
        LocalDate lastDay = localDate.withDayOfMonth(localDate.lengthOfMonth());
        return  Date.from(lastDay.atTime(LocalTime.MAX).atZone(ZoneId.systemDefault()).toInstant());
    }

    /**
     * 将 Date 转为 LocalDate（根据系统默认时区）
     */
    private LocalDate convertToLocalDate(Date date) {
        return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
    }

    /**
     * 按 "yyyy-MM-dd" 格式化 Date
     */
    private String formatDate(Date date) {
        if (date == null) {
            return "";
        }
        return new SimpleDateFormat("yyyy-MM-dd").format(date);
    }

    /**
     * 将 [startTime, endTime] 区间内每一天的 type 覆盖为指定的 newType
     */
    private void fillDateRange(Map<String, CalendarDay> dayMap, Date startTime, Date endTime, int newType) {
        // 转成 LocalDate 方便遍历
        LocalDate start = convertToLocalDate(startTime);
        LocalDate end   = convertToLocalDate(endTime);

        // 逐天循环
        for (LocalDate d = start; !d.isAfter(end); d = d.plusDays(1)) {
            String dateStr = d.toString(); // "2025-03-01"
            CalendarDay existingDay = dayMap.get(dateStr);
            if (existingDay != null) {
                // 如果已有记录，就覆盖type
                existingDay.setType(newType);
            } else {
                // 如果没有记录，就新建
                CalendarDay calendarDay = new CalendarDay();
                calendarDay.setDate(dateStr);
                calendarDay.setType(newType);
                dayMap.put(dateStr, calendarDay);
            }
        }
    }


}
