package com.yida.data.school.leave.service.impl;

import static com.yida.data.common.core.entity.constant.CachePrefixConstant.*;
import static com.yida.data.common.core.entity.constant.imageConstant.LEAVE_IMAGE_URL;

import cc.mrbird.febs.common.redis.service.RedisService;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.HtmlUtil;
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.common.core.entity.constant.AppConstant;
import com.yida.data.common.core.entity.leave.EduLeaveRequest;
import com.yida.data.common.core.entity.notice.app.AppNotice;
import com.yida.data.common.core.entity.notice.app.inside.PushMessage;
import com.yida.data.common.core.entity.notice.qywx.BaseStaffNotice;
import com.yida.data.common.core.entity.notice.qywx.NewsSchoolNotice;
import com.yida.data.common.core.entity.notice.qywx.inside.Article;
import com.yida.data.common.core.entity.notice.qywx.inside.News;
import com.yida.data.common.core.entity.notice.qywx.inside.Text;
import com.yida.data.common.core.entity.school.EduYidaAppAccount;
import com.yida.data.common.core.entity.system.Dept;
import com.yida.data.common.core.entity.system.EduApp;
import com.yida.data.common.core.entity.system.EduYidaApp;
import com.yida.data.common.core.entity.user.*;
import com.yida.data.common.core.exception.FebsException;
import com.yida.data.common.core.utils.AppUtil;
import com.yida.data.common.core.utils.WxUtil;
import com.yida.data.common.service.CommonService;
import com.yida.data.school.dto.leave.FindPageLeaveListDTO;
import com.yida.data.school.dto.leave.ListStudentRequestDetailDTO;
import com.yida.data.school.leave.mapper.EduLeaveRequestMapper;
import com.yida.data.school.leave.service.EduLeaveRequestService;
import com.yida.data.school.vo.leave.CountRequestVO;
import com.yida.data.school.vo.leave.FindPageLeaveListVO;
import com.yida.data.school.vo.leave.LeaveRequestDetailVO;
import com.yida.data.user.dto.ListStudentDTO;
import com.yida.data.user.dto.ListStudentDTO.ListStudentDTOBuilder;
import com.yida.data.user.feign.RemoteStaffService;
import com.yida.data.user.feign.RemoteStudentService;
import com.yida.data.user.feign.RemoteTeacherService;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;


/**
 * 请假表 Service实现
 *
 * @author ccl
 * @date 2021-11-22 16:27:42
 */
@Service
@RequiredArgsConstructor
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
public class EduLeaveRequestServiceImpl extends ServiceImpl
    <EduLeaveRequestMapper, EduLeaveRequest> implements EduLeaveRequestService {

    private final WxUtil wxUtil;
    private final CommonService commonService;
    private final RemoteStaffService remoteStaffService;
    private final RedisService redisService;
    private final RemoteTeacherService teacherService;
    @Value("${febs.parentLeaveRequestUrl}")
    private String parentLeaveRequestUrl;
    @Value("${febs.teacherLeaveRequestUrl}")
    private String teacherLeaveRequestUrl;

    private final RemoteStudentService remoteStudentService;
    private static final ExecutorService PUBLISH_THREAD_POOL =
        new ThreadPoolExecutor(3, 5, 10, TimeUnit.SECONDS, new LinkedBlockingQueue<>());

    @Override
    public void saveOrUpLeave(EduLeaveRequest leaveRequest) {

        Long studentId = leaveRequest.getStudentId();

        List<EduLeaveRequest> lastRequest = baseMapper.findLastRequest(studentId, LocalDateTime.now());

        if (ObjectUtil.isNotEmpty(lastRequest)) {
            EduLeaveRequest last = lastRequest.get(lastRequest.size() - 1);
            //判断时间重叠
            LocalDateTime lastStart = last.getStartTime();
            LocalDateTime lastEnd = last.getEndTime();
            LocalDateTime end = leaveRequest.getEndTime();
            LocalDateTime start = leaveRequest.getStartTime();
            if (start.isAfter(lastStart) && start.isBefore(lastEnd)) {
                throw new FebsException("本次请假时段与上次请假时段重叠，请重试");
            } else if (end.isAfter(lastStart) && end.isBefore(lastEnd)) {
                throw new FebsException("本次请假时段与上次请假时段重叠，请重试");
            } else if (lastStart.isAfter(start) && lastStart.isBefore(end)) {
                throw new FebsException("本次请假时段与上次请假时段重叠，请重试");
            } else if (lastEnd.isAfter(start) && lastEnd.isBefore(end)) {
                throw new FebsException("本次请假时段与上次请假时段重叠，请重试");
            }
        }
        //查询家长称呼
        EduStudent data = new EduStudent();
        if (redisService.hasKey(STUDENT_DATA)) {
            data = (EduStudent) redisService.hget(STUDENT_DATA, leaveRequest.getStudentId().toString());
            if (ObjectUtil.isEmpty(data)) {
                data = remoteStudentService.getStudentNoPermission(leaveRequest.getStudentId()).getData();
            }
        }
        List<EduParent> parents = data.getParents();
        leaveRequest.setParentName(parents.get(0).getParentType());
        leaveRequest.setSchoolId(data.getSchoolId());
        leaveRequest.setCampsId(data.getCampusId());
        leaveRequest.setSectionId(data.getSectionId());
        leaveRequest.setGradeId(data.getGradeId());
        leaveRequest.setClassId(data.getClassId());
        saveOrUpdate(leaveRequest);
        //新增请假通知对应老师
        //职工wxId
        EduStaff staff = remoteStaffService.getStaffNoPermission(leaveRequest.getTeacherId()).getData();
        Dept school = commonService.getDept(staff.getSchoolId());

        //发送企业微信通知
        if (school.getSchoolType().contains("0")) {

            EduApp app = commonService.getAppByCodeAndSchool(AppConstant.CONTACT_SELECT, null,
                staff.getSchoolId());
            String accessToken = wxUtil.getAccessToken(app.getWxCorpId(), app.getWxSecret());

            Text text = new Text();
            String url = teacherLeaveRequestUrl + app.getWxCorpId() + "&loginType=0&state=" + app.getCode() + "&userType=0";
            text.setContent("尊敬的" + staff.getName() + "老师，您有一条新的请假申请，请尽快处理！" + "<a href='" + url + "'>" + "详情..." + "</a>");
            BaseStaffNotice textSchoolNotice = new BaseStaffNotice();
            textSchoolNotice.setText(text);
            textSchoolNotice.setAgentid(app.getWxAgentId());
            textSchoolNotice.setTouser(staff.getWxId());
            //发送微信通知
            wxUtil.pushStaffNotice(accessToken, textSchoolNotice);
        }

        //易达app发送消息
        if (school.getSchoolType().contains("1")) {
            PUBLISH_THREAD_POOL.execute(() -> {
                // 易达app进行消息推送
                if (school.getSchoolType().contains(Dept.TYPE_YIDA_APP)) {
                    // 易达app端发送消息

                    List<String> parentYidaUserIdList = new ArrayList<>();
                    parentYidaUserIdList.add(staff.getYidaAppUserId());
                    if (CollUtil.isNotEmpty(parentYidaUserIdList)) {
                        EduYidaApp yidaApp = commonService.getYidaApp(staff.getSchoolId());
                        EduYidaAppAccount account = commonService.getYidaAppAccount(staff.getSchoolId(), 1);
                        AppNotice appNotice = new AppNotice();
                        appNotice.setUserIds(parentYidaUserIdList);
                        List<PushMessage> msgList = new ArrayList<>();
                        //app推送图文消息
                        PushMessage pushMessage = new PushMessage();
                        String url =
                            teacherLeaveRequestUrl + "appId=" + school.getDeptId() + "&loginType=2" + "&studentId=" + studentId
                                + "&type=1";
                        pushMessage.setContent(
                            "尊敬的" + staff.getName() + "老师，您有一条新的请假申请，请尽快处理！" + "<a href='" + url + "'>" + "详情..." + "</a>");
                        pushMessage.setAccountId(account.getYidaAppAccountId());
                        pushMessage.setMsgType(PushMessage.TEXT);
                        msgList.add(pushMessage);

                        appNotice.setMsgList(msgList);
                        appNotice.setPushMessage(new PushMessage(school.getDeptName(),
                            account.getYidaAppAccountId()));
                        AppUtil.sendMsg(appNotice, yidaApp);
                    }
                }
            });

        }
    }

    @Override
    public Page<EduLeaveRequest> findLeaveList(Long parentId, Long studentId, Page<EduLeaveRequest> page, Integer status) {
        List<Long> studentIds = new ArrayList<>();
        if (ObjectUtil.isEmpty(studentId)) {
            List<EduStudent> students = remoteStudentService.listStudentByParent(parentId).getData();
            for (EduStudent student : students) {
                studentIds.add(student.getId());
            }

        }
        LocalDateTime now = LocalDateTime.now();
        return baseMapper.findpage(page, studentIds, status, studentId, now);
    }

    @Override
    public Page<EduLeaveRequest> findDeptLeaveList(Long deptId, Long teacherId, Integer status, Page<EduLeaveRequest> page,
        String studentName) {
        //查询部门里面的所有学生
        List<EduStudent> students = remoteStudentService.listStudent(ListStudentDTO.builder().classId(deptId).build()).getData();
        List<Long> studentIds = students.stream().map(EduStudent::getId).collect(Collectors.toList());
        if (CollectionUtil.isEmpty(studentIds)) {
            return new Page<>();
        }
        LocalDateTime now = LocalDateTime.now();
        //放入学生信息
        Page<EduLeaveRequest> requestList = baseMapper.findRequestPage(page, studentIds, studentName, status, teacherId, now);
        for (EduLeaveRequest eduLeaveRequest : requestList.getRecords()) {
            for (EduStudent student : students) {
                if (eduLeaveRequest.getStudentId().equals(student.getId())) {
                    eduLeaveRequest.setStudent(student);
                }
            }
        }
        return requestList;
    }

    @Override
    public void updateLeave(Long requestId, Integer status, Long schoolId) {
        EduLeaveRequest request = getById(requestId);
        request.setStatus(status);
        request.setId(requestId);
        request.setCheckTime(LocalDateTime.now());
        updateById(request);

        EduStudent data = new EduStudent();

        if (redisService.hasKey(STUDENT_DATA)) {
            data = (EduStudent) redisService.hget(STUDENT_DATA, request.getStudentId().toString());
            if (ObjectUtil.isEmpty(data)) {
                data = remoteStudentService.getStudentNoPermission(request.getStudentId()).getData();
            }
        }
        List<EduParent> parents = data.getParents();
        List<EduParent> collect = parents.stream().filter(p -> p.getId().equals(request.getParentId()))
            .collect(Collectors.toList());

        //通知企业微信对应家长
        Dept dept = commonService.getDept(schoolId);
        if (dept.getSchoolType().contains("0")) {
            EduApp app = commonService.getAppByCodeAndSchool(AppConstant.CONTACT_SELECT, null,
                schoolId);
            String accessToken = wxUtil.getAccessToken(app.getWxCorpId(), app.getWxSecret());
            List<Long> list = new ArrayList<>();
            list.add(request.getStudentId());
            List<String> parentWxId = remoteStudentService.findParentWxId(list).getData();
            String title = "亲爱的" + data.getStuName() + collect.get(0).getParentType() + ":";
            Article article = new Article();
            article.setTitle(title);
            article.setUrl(
                parentLeaveRequestUrl + app.getWxCorpId() + "&loginType=0&state=" + app.getCode() + "&userType=1&studentId="
                    + request.getStudentId());
            if (status == 1) {
                article.setPicurl(LEAVE_IMAGE_URL);
                article.setDescription(data.getStuName() + "的请假申请已通过。请点击图片查看详情!");
            } else {
                article.setPicurl(LEAVE_IMAGE_URL);
                article.setDescription(data.getStuName() + "的请假申请未被通过。请点击图片查看详情!");
            }
            News news = new News(Collections.singletonList(article));
            NewsSchoolNotice newsSchoolNotice = new NewsSchoolNotice();
            newsSchoolNotice.setNews(news);
            newsSchoolNotice.setAgentid(app.getWxAgentId());
            newsSchoolNotice.setTo_parent_userid(parentWxId);
            wxUtil.pushSchoolNotice(accessToken, newsSchoolNotice);
        }
        //发送图文消息
        if (dept.getSchoolType().contains("1")) {
            EduYidaApp yidaApp = commonService.getYidaApp(schoolId);
            EduYidaAppAccount account = commonService.getYidaAppAccount(schoolId, 0);

            List<PushMessage> msgList = new ArrayList<>();
            AppNotice appNotice = new AppNotice();
            List<String> parentYiDaId = new ArrayList<>();
            parentYiDaId.add(collect.get(0).getYidaAppUserId());
            appNotice.setUserIds(parentYiDaId);
            PushMessage pushMessage = new PushMessage();
            pushMessage.setTitle("亲爱的" + data.getStuName() + collect.get(0).getParentType() + ":");
            if (status == 1) {
                pushMessage.setImgUrl(LEAVE_IMAGE_URL);
                String content = data.getStuName() + "的请假申请已通过。请点击图片查看详情!";
                String cleanHtmlTag = HtmlUtil.cleanHtmlTag(HtmlUtil.unescape(content));
                pushMessage.setContent(cleanHtmlTag.length() > 54 ? cleanHtmlTag.substring(0, 54) + "..." :
                    cleanHtmlTag);
            } else {
                pushMessage.setImgUrl(LEAVE_IMAGE_URL);
                String content = data.getStuName() + "的请假申请未被通过。请点击图片查看详情!";
                String cleanHtmlTag = HtmlUtil.cleanHtmlTag(HtmlUtil.unescape(content));
                pushMessage.setContent(cleanHtmlTag.length() > 54 ? cleanHtmlTag.substring(0, 54) + "..." :
                    cleanHtmlTag);
            }
            pushMessage.setImgUrl(LEAVE_IMAGE_URL);
            pushMessage.setAccountId(account.getYidaAppAccountId());
            pushMessage.setHtmlUrl(
                parentLeaveRequestUrl +
                    "?appId=" + schoolId +
                    "&type=1" +
                    "&studentId=" + request.getStudentId() + "&loginType = 2"
            );
            pushMessage.setMsgType(PushMessage.ARTICLE);

            msgList.add(pushMessage);
            appNotice.setMsgList(msgList);
            appNotice.setPushMessage(new PushMessage(dept.getDeptName(),
                account.getYidaAppAccountId()));
            AppUtil.sendMsg(appNotice, yidaApp);
        }
    }

    @Override
    public Page<FindPageLeaveListVO> findPageLeaveList(Page<FindPageLeaveListVO> page, Integer status, Long sectionId,
        Long gradeId,
        String studentName, Integer reviewStatus, String teacherName,
        Long campsId, Long schoolId) {

        ListStudentDTO studentDTO = new ListStudentDTO();
        studentDTO.setSchoolId(schoolId);
        //现存所有学生
        List<Long> studentIds = new ArrayList<>();

        //现存所有学生放入缓存，如果查询不到学生信息则更新现存所有学生缓存
        if (redisService.hHasKey(schoolId.toString(), LIVE_STUDENT)) {
            List<Object> students = redisService.lGet(LIVE_STUDENT + schoolId.toString(), 0L, -1L);
            for (Object datum : students) {
                EduStudent student = (EduStudent) datum;
                studentIds.add(student.getId());
            }
        } else {
            List<EduStudent> students = remoteStudentService.listStudent(studentDTO).getData();
            redisService.lSet(LIVE_STUDENT + schoolId.toString(), students);
            for (EduStudent datum : students) {
                studentIds.add(datum.getId());
            }
        }

        //分页条件查询
        int all = 0;
        if (sectionId == null && teacherName == null && studentName == null) {
            all = 1;
        }
        Page<FindPageLeaveListVO> pageLeaveList = baseMapper
            .findPageLeaveList(studentName, status, sectionId, gradeId, page, reviewStatus, teacherName, schoolId, all,
                studentIds);
        Page<FindPageLeaveListVO> voPage = updateLiveStudent(pageLeaveList, studentDTO);
        if (ObjectUtil.isEmpty(voPage)) {
            List<Object> students = redisService.lGet(LIVE_STUDENT + schoolId, 0L, -1L);
            //清空之前的学生id
            studentIds = new ArrayList<>();
            for (Object datum : students) {
                EduStudent student = (EduStudent) datum;
                studentIds.add(student.getId());
            }
            pageLeaveList = baseMapper
                .findPageLeaveList(studentName, status, sectionId, gradeId, page, reviewStatus, teacherName, schoolId, all,
                    studentIds);
            return updateLiveStudent(pageLeaveList, studentDTO);
        }

        return pageLeaveList;
    }

    private Page<FindPageLeaveListVO> updateLiveStudent(Page<FindPageLeaveListVO> pageLeaveList, ListStudentDTO studentDTO) {
        for (FindPageLeaveListVO record : pageLeaveList.getRecords()) {
            commonService.getDept(record.getClassId());
            EduStudent student;
            if (redisService.hHasKey(record.getStudentId().toString(), STUDENT_DATA)) {
                student = (EduStudent) redisService.hget(record.getStudentId().toString(), STUDENT_DATA);
            } else {
                student = remoteStudentService.getStudentNoPermission(record.getStudentId()).getData();
                redisService.hset(record.getStudentId().toString(), STUDENT_DATA, student);
            }

            //查询结果中有学生的信息查不到说明应该更新学生存活信息了
            if (ObjectUtil.isEmpty(student)) {
                List<EduStudent> data = remoteStudentService.listStudent(studentDTO).getData();
                redisService.llSet(LIVE_STUDENT + studentDTO.getSchoolId(), data);
                return null;
            }
            record.setStudentName(student.getStuName());
            record.setStuNumber(student.getStuNumber());
            record.setClassName(commonService.getUserDept(student.getClassId()).getDeptName());
            record.setGradeName(commonService.getUserDept(student.getGradeId()).getDeptName());
        }
        return pageLeaveList;
    }


    @Override
    public FindPageLeaveListVO findLeaveRequest(Long id) {
        FindPageLeaveListVO vo = baseMapper.findLeaveRequest(id);
        //从缓存取学生
        EduStudent student = (EduStudent) redisService.hget(STUDENT_DATA, vo.getStudentId().toString());
        if (ObjectUtil.isEmpty(student)) {
            student = remoteStudentService.getStudentNoPermission(vo.getStudentId()).getData();
            redisService.hset(STUDENT_DATA, student.getId().toString(), student);
        }
        vo.setGradeName(student.getGradeName());
        vo.setClassName(student.getClassName());
        return vo;
    }

    @Override
    public void updateLeaveRequest() {
        LocalDateTime now = LocalDateTime.now();
        //查询已失效数据
        List<EduLeaveRequest> requestList = list(Wrappers.lambdaQuery(new EduLeaveRequest())
            .eq(EduLeaveRequest::getStatus, 0)
            .le(EduLeaveRequest::getStartTime, now));
        //修改并保存为已失效
        for (EduLeaveRequest eduLeaveRequest : requestList) {
            eduLeaveRequest.setStatus(3);
        }
        updateBatchById(requestList);
    }

    @Override
    public List<LeaveRequestDetailVO> listStudentLeave(ListStudentRequestDetailDTO dto) {
        List<LeaveRequestDetailVO> res = null;
        ListStudentDTOBuilder condition = ListStudentDTO.builder();
        if (dto.getStudentId() != null) {
            condition.studentIds(Arrays.asList(dto.getStudentId()));
        }
        if (dto.getClassId() != null) {
            condition.classId(dto.getClassId());
        }
        List<EduStudent> studentList = remoteStudentService.listStudent(condition.build()).getData();
        if (CollUtil.isNotEmpty(studentList)) {
            res = baseMapper.listStudentRequest(dto, studentList.stream().map(x -> x.getId()).collect(Collectors.toList()));
            if (CollUtil.isNotEmpty(res)) {
                Map<Long, List<EduStudent>> studentGroup = studentList.stream().collect(Collectors.groupingBy(EduStudent::getId));
                for (LeaveRequestDetailVO detail : res) {
                    EduStudent student = studentGroup.get(detail.getStudentId()).get(0);
                    detail.setStudentName(student.getStuName());
                    detail.setStudentNumber(student.getStuNumber());
                    detail.setType(student.getType());
                }
            }
        }
        return res;
    }

    @Override
    public CountRequestVO countStudentRequest(ListStudentRequestDetailDTO dto) {
        CountRequestVO vo = new CountRequestVO();
        vo.setRequestNum(0);
        vo.setStudentNum(0);
        List<LeaveRequestDetailVO> res = null;
        ListStudentDTOBuilder condition = ListStudentDTO.builder();
        if (dto.getStudentId() != null) {
            condition.studentIds(Arrays.asList(dto.getStudentId()));
        }
        if (dto.getClassId() != null) {
            condition.classId(dto.getClassId());
        }
        List<EduStudent> studentList = remoteStudentService.listStudent(condition.build()).getData();
        if (CollUtil.isNotEmpty(studentList)) {
            res = baseMapper.listStudentRequest(dto, studentList.stream().map(x -> x.getId()).collect(Collectors.toList()));
            if (CollUtil.isNotEmpty(res)) {
                vo.setRequestNum(res.size());
                vo.setStudentNum((int) (res.stream().map(x -> x.getStudentId()).distinct().count()));
            }
        }
        return vo;
    }

    @Override
    public Boolean existRequest(ListStudentRequestDetailDTO dto) {
        Integer existRequest = baseMapper.existRequest(dto);
        return existRequest != null;
    }
}
