package com.yida.data.device.studentCard.service.impl;


import cc.mrbird.febs.common.redis.service.RedisService;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.StrUtil;
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.common.core.entity.card.EduStudentCard;
import com.yida.data.common.core.entity.card.EduStudentCardAlarm;
import com.yida.data.common.core.entity.constant.AppConstant;
import com.yida.data.common.core.entity.constant.CachePrefixConstant;
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.TextSchoolNotice;
import com.yida.data.common.core.entity.notice.qywx.inside.Text;
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.EduParent;
import com.yida.data.common.core.entity.user.EduStudent;
import com.yida.data.common.core.enums.CardAlarmType;
import com.yida.data.common.core.utils.AppUtil;
import com.yida.data.common.core.utils.EnumUtils;
import com.yida.data.common.core.utils.LocationUtil;
import com.yida.data.common.core.utils.NumberUtil;
import com.yida.data.common.core.utils.WxUtil;
import com.yida.data.common.service.CommonService;
import com.yida.data.device.dto.studentCard.AlarmPageDTO;
import com.yida.data.device.studentCard.mapper.EduStudentCardAlarmMapper;
import com.yida.data.device.studentCard.mapper.EduStudentCardFenceMapper;
import com.yida.data.device.studentCard.mapper.EduStudentCardMapper;
import com.yida.data.device.studentCard.service.EduStudentCardAlarmService;
import com.yida.data.device.vo.studentCard.AlarmPageVO;
import com.yida.data.device.vo.studentCard.StudentAlarmVO;
import com.yida.data.user.dto.ListStudentDTO;
import com.yida.data.user.feign.RemoteStudentService;
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 lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * 电子学生证-告警信息 Service实现
 *
 * @author zhanghaijun
 * @date 2021-08-04 15:08:05
 */
@Slf4j
@Service
@RequiredArgsConstructor
@Transactional
public class EduStudentCardAlarmServiceImpl extends ServiceImpl
    <EduStudentCardAlarmMapper, EduStudentCardAlarm> implements EduStudentCardAlarmService {

    private final static ExecutorService PUSH_MSG_EXECUTOR = new ThreadPoolExecutor(5, 10, 30L, TimeUnit.SECONDS,
        new LinkedBlockingQueue<>());
    private final static String FENCE_ALARM_MSG = "亲爱的家长，您的小孩%s于%s%s【%s】围栏！";
    private final static String BAT_ALARM_MSG = "您好！%s同学的电子学生证电量仅剩%s%，为保证正常使用，请及时充电！";
    private final static String STEP_MSG = "您好！%s同学今天总共走了%s步，消耗了%sKJ热量！劳逸结合，身体倍儿棒，继续加油哦！";
    private final static String RISK_AREA_MSG = "您好！%s同学进入了危险区域！%s！当前位置:%s！";
    private final static String DEVICE_STATIC_MSG = " 您好!监测到%s同学的%s没有佩戴或者脱落可能会影响部分功能的使用，请知晓。";
    private final static String MSG_FLAG = "【数据来自电子学生证】";
    private final EduStudentCardMapper eduStudentCardMapper;
    private final EduStudentCardFenceMapper eduStudentCardFenceMapper;

    private final RemoteStudentService remoteStudentService;
    private final WxUtil wxUtil;
    private final CommonService commonService;
    private final RedisService redisService;

    @Override
    public IPage<AlarmPageVO> listAlarmPage(AlarmPageDTO dto) {

        ListStudentDTO studentCondition = ListStudentDTO.builder()
            .schoolId(dto.getSchoolId())
            .campusId(dto.getCampusId())
            .sectionId(dto.getSectionId())
            .gradeId(dto.getGradeId())
            .classId(dto.getClassId()).isQueryParent(false).build();
        List<EduStudent> studentList = remoteStudentService.listStudent(studentCondition).getData();
        // 包含关键词时过滤出符合条件的学生
        List<Long> conformStudent = null;
        if (StrUtil.isNotBlank(dto.getKeyword()) && CollUtil.isNotEmpty(studentList)) {
            conformStudent = studentList.parallelStream().filter(x -> x.getStuName().contains(dto.getKeyword()))
                .map(EduStudent::getId).collect(
                    Collectors.toList());
        }
        IPage<AlarmPageVO> page = baseMapper.listAlarmPage(dto.toPage(), dto, studentList, conformStudent);
        if (CollUtil.isNotEmpty(page.getRecords())) {
            Map<Long, List<EduStudent>> studentGroup = studentList.stream()
                .collect(Collectors.groupingBy(EduStudent::getId));
            for (AlarmPageVO record : page.getRecords()) {
                List<EduStudent> students = studentGroup.get(record.getStudentId());
                EduStudent student = students.get(0);
                record.setStudentName(student.getStuName());
                record.setSchoolName(student.getSchoolName());
                record.setGradeClass(student.getGradeName() + student.getClassName());
            }
        }
        return page;
    }

    @Override
    public IPage<StudentAlarmVO> listStudentAlarmPage(Long studentId, Integer pageNum, Integer pageSize) {
        return baseMapper.listStudentAlarmPage(Page.of(pageNum, pageSize), studentId);
    }

    @Override
    public void saveAlarm(EduStudentCardAlarm alarm) {
        EduStudentCard studentCard = eduStudentCardMapper
            .selectOne(Wrappers.<EduStudentCard>lambdaQuery().eq(EduStudentCard::getImei, alarm.getImei()));
        if (studentCard != null) {
            Dept school = commonService.getDept(studentCard.getSchoolId());
            EduStudent student = (EduStudent) redisService
                .hget(CachePrefixConstant.STUDENT_DATA, studentCard.getStudentId().toString());
            if (student == null) {
                student = remoteStudentService.getStudentNoPermission(studentCard.getStudentId()).getData();
                if (student == null) {
                    log.error("学生未找到,studentId:[{}]", studentCard.getStudentId());
                    return;
                }
                redisService.hset(CachePrefixConstant.STUDENT_DATA, studentCard.getStudentId().toString(), student);
            }
            // 部分学生信息
            alarm.setSchoolId(studentCard.getSchoolId());
            alarm.setMobile(studentCard.getStudentPhone());
            alarm.setStudentId(studentCard.getStudentId());
            // 构造通知信息
            alarm.setRemark(buildAlarmMsg(alarm, student, studentCard));
            if (StrUtil.isBlank(alarm.getAddress()) && alarm.getLng() != null & alarm.getLat() != null) {
                alarm.setAddress(LocationUtil.locationToName(alarm.getLng(), alarm.getLat()));
            }
            save(alarm);
            EduStudent finalStudent = student;
            PUSH_MSG_EXECUTOR.submit(() -> {
                if (school.getSchoolType().contains(Dept.TYPE_QYWX)) {
                    // 推送微信通知
                    pushSchoolNotice(finalStudent, alarm, school);
                }
                if (school.getSchoolType().contains(Dept.TYPE_YIDA_APP)) {
                    // 推送app通知
                    pushYidaAppNotice(finalStudent, alarm);
                }
            });
        }
    }

    private void pushSchoolNotice(EduStudent student, EduStudentCardAlarm alarm, Dept school) {
        EduApp app = commonService.getAppByCodeAndSchool(AppConstant.INDEX_PARENT, null, school.getDeptId());
        TextSchoolNotice notice = new TextSchoolNotice();
        notice.setAgentid(app.getWxAgentId());
        notice.setTo_student_userid(Collections.singletonList(student.getWxId()));
        try {
            notice.setText(new Text(alarm.getRemark() + MSG_FLAG));
        } catch (Exception e) {
            return;
        }
        wxUtil.pushSchoolNotice(wxUtil.getAccessToken(app.getWxCorpId(), app.getWxSecret()), notice);
    }

    private void pushYidaAppNotice(EduStudent student, EduStudentCardAlarm alarm) {
        EduYidaApp yidaApp = commonService.getYidaApp(alarm.getSchoolId());
        List<String> parentId = student.getParents().stream().map(EduParent::getYidaAppUserId)
            .filter(StrUtil::isNotBlank).collect(Collectors.toList());
        AppNotice notice = new AppNotice();
        PushMessage message = new PushMessage();
        try {
            message.setContent(alarm.getRemark() + MSG_FLAG);
        } catch (Exception e) {
            return;
        }
        message.setMsgType(PushMessage.TEXT);
        notice.setMsgList(Collections.singletonList(message));
        notice.setUserIds(parentId);
        AppUtil.sendMsg(notice, yidaApp);
    }

    private String buildAlarmMsg(EduStudentCardAlarm alarm, EduStudent student, EduStudentCard card) {
        String msg = null;
        try {
            CardAlarmType alarmType = EnumUtils
                .valueOf(CardAlarmType.class, alarm.getAlarmType(), CardAlarmType.class.getMethod("getValue"));
            switch (alarmType) {
                case IN_FENCE:
                    msg = String.format(FENCE_ALARM_MSG, student.getStuName(),
                        LocalDateTimeUtil.format(alarm.getAlarmTime(), "yyyy年MM月dd日HH:mm:ss"), "进入", alarm.getRemark());
                    break;
                case OUT_FENCE:
                    msg = String.format(FENCE_ALARM_MSG, student.getStuName(),
                        LocalDateTimeUtil.format(alarm.getAlarmTime(), "yyyy年MM月dd日HH:mm:ss"), "离开", alarm.getRemark());
                    break;
                case LOW_BAT:
                    msg = String.format(BAT_ALARM_MSG, student.getStuName(), alarm.getRemarkValue());
                    break;
                case STEP:
                    msg = String.format(STEP_MSG, student.getStuName(), alarm.getRemarkValue(),
                        NumberUtil.format("#.##", Integer.valueOf(alarm.getRemarkValue()) / 76.1));
                    break;
                case RISK_AREA:
                    msg = String.format(RISK_AREA_MSG, student.getStuName(), alarm.getRemarkValue(), alarm.getAddress());
                    break;
                case DEVICE_STATIC:
                    msg = String.format(DEVICE_STATIC_MSG, student.getStuName(),
                        "1".equals(card.getMcType().split("_")[1]) ? "电子学生证" : "校徽");
                default:
                    break;
            }
        } catch (Exception e) {
            log.error("转换推送消息失败", e);
        }
        return msg;
    }
}
