package com.yida.data.attendance.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yida.data.attendance.mapper.EduAttendanceDeviceMapper;
import com.yida.data.attendance.mapper.EduAttendanceRuleMapper;
import com.yida.data.attendance.service.*;
import com.yida.data.attendance.vo.ConflictRuleVO;
import com.yida.data.attendance.vo.RuleListVO;
import com.yida.data.common.core.entity.CurrentUser;
import com.yida.data.common.core.entity.attendance.*;
import com.yida.data.common.core.entity.system.Dept;
import com.yida.data.common.core.entity.system.SystemUser;
import com.yida.data.common.core.utils.Asserts;
import com.yida.data.system.feign.RemoteDeptService;
import com.yida.data.system.feign.RemoteUserService;
import com.yida.data.user.feign.RemoteUserDeptService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@Service
@Slf4j
@RequiredArgsConstructor
@Transactional
public class EduAttendanceRuleServiceImpl extends ServiceImpl<EduAttendanceRuleMapper, EduAttendanceRule> implements
    EduAttendanceRuleService {

    private final EduAttendanceDeviceRuleService eduAttendanceDeviceRuleService;
    private final EduAttendanceRuleDateService eduAttendanceRuleDateService;
    private final EduAttendanceRuleTimeService eduAttendanceRuleTimeService;
    private final EduAttendanceRuleDeptService eduAttendanceRuleDeptService;
    private final EduAttendanceRuleDeviceService eduAttendanceRuleDeviceService;

    private final EduAttendanceDeviceMapper eduAttendanceDeviceMapper;
    private final RemoteDeptService remoteDeptService;
    private final RemoteUserDeptService remoteUserDeptService;
    private final RemoteUserService remoteUserService;

    @Override
    public IPage<RuleListVO> listRulePage(Page page, CurrentUser currentUser, Long schoolId, Long campusId,
        String ruleName,
        Integer type,
        Integer status, String startDate, String endDate) {
        if (currentUser.getDeptType() == 0) {
            schoolId = currentUser.getDeptId();
        } else if (currentUser.getDeptType() != 5) {
            Dept school = remoteDeptService.findSchoolByDept(currentUser.getDeptId()).getData();
            if (school != null) {
                schoolId = school.getDeptId();
            }
        }
        IPage<RuleListVO> res = baseMapper.listRulePage(page, schoolId, campusId, ruleName, type, status,
            startDate,
            endDate);
        res.getRecords().forEach(ruleListVO -> {
            SystemUser systemUser = remoteUserService.getInfoById(ruleListVO.getCreateId()).getData();
            if (Objects.nonNull(systemUser)) {
                ruleListVO.setCreateName(systemUser.getNickname());
            }
        });
        return res;
    }

    @Override
    @Transactional
    public void saveRule(EduAttendanceRule rule) {
        if (rule.getId() != null) {
            // 更新前删除原有关系(设备绑定，运行时间，绑定部门)
            eduAttendanceRuleDateService.remove(
                Wrappers.lambdaQuery(new EduAttendanceRuleDate()).eq(EduAttendanceRuleDate::getRuleId,
                    rule.getId()));
            eduAttendanceRuleTimeService.remove(
                Wrappers.lambdaQuery(new EduAttendanceRuleTime()).eq(EduAttendanceRuleTime::getRuleId,
                    rule.getId()));
            eduAttendanceRuleDeptService.remove(
                Wrappers.lambdaQuery(new EduAttendanceRuleDept()).eq(EduAttendanceRuleDept::getRuleId,
                    rule.getId()));
            eduAttendanceRuleDeviceService.remove(
                Wrappers.lambdaQuery(new EduAttendanceRuleDevice()).eq(EduAttendanceRuleDevice::getRuleId,
                    rule.getId()));

        } else {
            LocalDateTime now = LocalDateTime.now();
            for (EduAttendanceRuleDate ruleDate : rule.getDateList()) {
                if (now.isAfter(ruleDate.getStartDate().atTime(0, 0, 0))) {
                    rule.setStatus(0);
                    break;
                }
            }
        }
        saveOrUpdate(rule);
        // 保存运行时间
        for (EduAttendanceRuleDate ruleDate : rule.getDateList()) {
            ruleDate.setId(null);
            ruleDate.setRuleId(rule.getId());
        }
        eduAttendanceRuleDateService.saveBatch(rule.getDateList());
        // 保存考勤时间
        for (EduAttendanceRuleTime ruleTime : rule.getTimeList()) {
            ruleTime.setId(null);
            ruleTime.setRuleId(rule.getId());
        }
        eduAttendanceRuleTimeService.saveBatch(rule.getTimeList());
        // 保存部门绑定关系
        for (EduAttendanceRuleDept ruleDept : rule.getDeptList()) {
            ruleDept.setRuleId(rule.getId());
            ruleDept.setId(null);
        }
        eduAttendanceRuleDeptService.saveBatch(rule.getDeptList());

        // 保存规则与设备关系
        for (EduAttendanceRuleDevice ruleDevice : rule.getDeviceList()) {
            EduAttendanceDevice eduAttendanceDevice = eduAttendanceDeviceMapper.selectById(ruleDevice.getDeviceId());
            ruleDevice.setDeviceId(eduAttendanceDevice.getId());
            ruleDevice.setDeviceSn(eduAttendanceDevice.getSn());
            ruleDevice.setRuleId(rule.getId());
            ruleDevice.setCreateDate(LocalDateTime.now());
        }
        eduAttendanceRuleDeviceService.saveBatch(rule.getDeviceList());
    }

    @Override
    public List<ConflictRuleVO> checkConflict(EduAttendanceRule rule) {
        // 校验规则时间是否冲突
        List<Long> dept = rule.getDeptList().stream().map(x -> x.getDeptId()).collect(Collectors.toList());
        // 查询有关联的部门
        List<Long> deptIds = remoteUserDeptService.getUpAndDownId(dept.toArray(new Long[0])).getData();
        List<EduAttendanceRuleDate> date = rule.getDateList();
        List<EduAttendanceRuleTime> time = rule.getTimeList();
        log.info("deptIds:[{}]", deptIds);
        return baseMapper.checkNormalRule(deptIds, date, time, rule.getType(), rule.getId());
    }

    @Override
    @Transactional
    public void deleteRule(List<Long> ids) {
        eduAttendanceDeviceRuleService.remove(
            Wrappers.lambdaQuery(new EduAttendanceDeviceRule()).in(EduAttendanceDeviceRule::getRuleId, ids));
        eduAttendanceRuleDateService.remove(
            Wrappers.lambdaQuery(new EduAttendanceRuleDate()).in(EduAttendanceRuleDate::getRuleId, ids));
        eduAttendanceRuleTimeService.remove(
            Wrappers.lambdaQuery(new EduAttendanceRuleTime()).in(EduAttendanceRuleTime::getRuleId, ids));
        eduAttendanceRuleDeptService.remove(
            Wrappers.lambdaQuery(new EduAttendanceRuleDept()).in(EduAttendanceRuleDept::getRuleId, ids));
        removeByIds(ids);
    }

    @Override
    public void changeRuleRun(Long id) {
        EduAttendanceRule rule = getById(id);
        Integer status = rule.getStatus();
        if (rule.getStatus() == 0) {
            // 运行中得规则可以停止
            status = 1;
        } else if (rule.getStatus() == 1) {
            List<EduAttendanceRuleDate> dateList = eduAttendanceRuleDateService.list(
                Wrappers.lambdaQuery(new EduAttendanceRuleDate())
                    .eq(EduAttendanceRuleDate::getRuleId, id));
            LocalDateTime now = LocalDateTime.now();
            for (EduAttendanceRuleDate ruleDate : dateList) {
                LocalDateTime startTime = ruleDate.getStartDate().atTime(0, 0, 0);
                LocalDateTime endTime = ruleDate.getEndDate().atTime(23, 59, 59);
                if (now.isAfter(startTime) && now.isBefore(endTime)) {
                    // 已经停止得规则未过期可以再次运行
                    status = 0;
                    break;
                }
            }
        }
        Asserts.isTrue(!rule.getStatus().equals(status), "变更状态失败");
        rule.setStatus(status);
        updateById(rule);
    }

    @Override
    public RuleListVO getValidRule(List<Long> deptIds, LocalDateTime date, Integer type, String deviceSn) {
        // 向上获取部门id
//        List<Long> deptIds = remoteUserDeptService.getUpAndDownId(new Long[]{deptId}).getData();
        List<RuleListVO> ruleList = baseMapper.listValidRule(deptIds, date.toLocalDate(),
            date.getDayOfWeek().getValue(), null, deviceSn, null);
        RuleListVO validRule = CollUtil.isNotEmpty(ruleList) ? ruleList.get(0) : null;
        return validRule;
    }

    @Override
    public void updateRuleStatus() {
        // 到期的规则
        List<Long> expireIds = baseMapper.listExpireRule(LocalDate.now());
        if (CollUtil.isNotEmpty(expireIds)) {
            baseMapper.update(null, Wrappers.lambdaUpdate(new EduAttendanceRule())
                .in(EduAttendanceRule::getId, expireIds)
                .set(EduAttendanceRule::getStatus, 2));
        }
        // 启动规则
        List<Long> notStartIds = baseMapper.listNotStartRule(LocalDate.now());
        if (CollUtil.isNotEmpty(notStartIds)) {
            baseMapper.update(null, Wrappers.lambdaUpdate(new EduAttendanceRule())
                .in(EduAttendanceRule::getId, notStartIds)
                .set(EduAttendanceRule::getStatus, 0));
        }
        // 一段时间到期  另一端时间未到
        List<Long> willStartIds = baseMapper.listWillStartRule(LocalDate.now());
        if (CollUtil.isNotEmpty(willStartIds)) {
            baseMapper.update(null, Wrappers.lambdaUpdate(new EduAttendanceRule())
                .in(EduAttendanceRule::getId, willStartIds)
                .set(EduAttendanceRule::getStatus, 3));
        }
    }

    @Override
    public List<Long> listValidRuleDept(Long schoolId, Integer type) {
        LocalDateTime now = LocalDateTime.now();
        List<Long> deptIds = new ArrayList<>();
        List<RuleListVO> ruleList = baseMapper.listValidRule(null, now.toLocalDate(), now.getDayOfWeek().getValue(), type, null,
            schoolId);
        if (CollUtil.isNotEmpty(ruleList)) {
            for (RuleListVO rule : ruleList) {
                deptIds.addAll(rule.getDeptList().stream().map(x -> x.getDeptId()).collect(Collectors.toList()));
            }
            deptIds = deptIds.stream().distinct().collect(Collectors.toList());
        }
        return deptIds;
    }
}
