package cn.exploring.engine.server.db.service;

import cn.exploring.engine.server.db.dao.SpecialSqlMapper;
import cn.exploring.engine.server.db.dao.StudentInfoMapper;
import cn.exploring.engine.server.db.domain.ClassInfo;
import cn.exploring.engine.server.db.domain.StudentInfo;
import cn.exploring.engine.server.db.domain.StudentInfoExample;
import cn.exploring.engine.server.db.domain.vo.StudentInfoExcelImportResultVo;
import cn.exploring.engine.server.db.domain.vo.StudentInfoExcelImportVo;
import cn.exploring.engine.server.db.domain.vo.StudentVo;
import cn.exploring.engine.server.db.util.excel.ExcelReaderListener;
import cn.exploring.engine.server.db.util.excel.StudentInfoExcelReaderListener;
import com.alibaba.excel.EasyExcel;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.google.common.collect.ImmutableMap;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.IOError;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Service
public class StudentInfoService {

    @Resource
    StudentInfoMapper studentInfoMapper;

    @Resource
    ClassService classService;

    @Resource
    SpecialSqlMapper specialSqlMapper;

    @Resource
    StudentPointService studentPointService;

    public static final String STRING_DEFAULT_HEAD = "{{default}}";

    public static final String URL_DEFAULT_HEAD = "https://storage.exploring.cn/class-work/static/img/head-student-default.png";

    /**
     * 添加
     * @param studentInfo
     * @return
     */
    public String addStudentInfo(StudentInfo studentInfo) {
        if (selectStudentInfoByUniqueId(studentInfo.getStudentUniqueId()) != null) {
            return "Fail 学号重复！ 请重新输入学号";
        }
        LocalDateTime now = LocalDateTime.now();
        studentInfo.setAddTime(now);
        studentInfo.setUpdateTime(now);
        studentInfoMapper.insertSelective(studentInfo);
        return "OK 添加成功";
    }

    /**
     * 随机选取一个 学生数据
     * @param classId
     * @return
     */
    public Map selectStudentInfo(Integer classId) {
        return specialSqlMapper.selectRandomStudentInfo(classId);
    }

    /**
     * 更新数据
     * @param studentInfo
     * @return
     */
    public String updateStudentInfo(StudentInfo studentInfo) {
        StudentInfo studentInfo1 = selectStudentInfoByUniqueId(studentInfo.getStudentUniqueId());
        if (studentInfo1 != null && !studentInfo1.getId().equals(studentInfo.getId())) {
            return "Fail 学号重复！ 请重新输入学号";
        }
        studentInfo.setUpdateTime(LocalDateTime.now());
        studentInfoMapper.updateByPrimaryKey(studentInfo);
        return "OK 修改成功";
    }

    /**
     * 批量删除数据
     * @param idList
     * @return
     */
    public Object batchDeleted(List<Integer> idList) {
        List<StudentInfo> studentInfoList = selectStudentInfo(null, null, idList);
        studentInfoList.forEach(x -> {
            x.setDeleted(null);
            updateStudentInfo(x);
        });
        return "batch deleted  OK";
    }

    /**
     * 根据标记学号进行查询
     * @param uniqueId
     * @return
     */
    public StudentInfo selectStudentInfoByUniqueId(String uniqueId) {
        StudentInfoExample example = new StudentInfoExample();
        StudentInfoExample.Criteria criteria = example.createCriteria();
        criteria.andDeletedEqualTo(false).andStudentUniqueIdEqualTo(uniqueId);
        return studentInfoMapper.selectOneByExample(example);

    }

    public List<StudentInfo> selectStudentInfoByUniqueId(Collection<String> uniqueIdList) {
        StudentInfoExample example = new StudentInfoExample();
        StudentInfoExample.Criteria criteria = example.createCriteria();
        criteria.andDeletedEqualTo(false).andStudentUniqueIdIn(new ArrayList<>(uniqueIdList));
        return studentInfoMapper.selectByExample(example);

    }

    /**
     * 检索学生相关的信息
     * @param studentName
     * @param studentId
     * @return
     */
    public List<StudentInfo> selectStudentInfo(String studentName, String studentId, List<Integer> idList) {
        StudentInfoExample example = new StudentInfoExample();
        StudentInfoExample.Criteria criteria = example.createCriteria();
        criteria.andDeletedEqualTo(false);
        if (!StringUtils.isEmpty(studentName)) {
            criteria.andNameLike("%" + studentName + "%");
        }
        if (!StringUtils.isEmpty(studentId)) {
            criteria.andStudentUniqueIdEqualTo(studentId);
        }
        if (idList != null && idList.size() > 0) {
            criteria.andIdIn(idList);
        }
        return studentInfoMapper.selectByExample(example);
    }

    /**
     * 检索学生相关的信息
     * @param studentName
     * @param studentId
     * @return
     */
    public Map selectStudentInfoByWeb(String studentName, String studentId, List<Integer> idList, Integer classId, Integer page, Integer limit, String sort, String order, Integer minCall, Integer maxCall) {
        StudentInfoExample example = new StudentInfoExample();
        StudentInfoExample.Criteria criteria = example.createCriteria();
        criteria.andDeletedEqualTo(false);
        if (!StringUtils.isEmpty(studentName)) {
            criteria.andNameLike("%" + studentName + "%");
        }
        if (!StringUtils.isEmpty(studentId)) {
            criteria.andStudentUniqueIdLike("%" + studentId + "%");
        }
        if (idList != null && idList.size() > 0) {
            criteria.andIdIn(idList);
        }
        if (classId != null) {
            criteria.andClassIdEqualTo(classId);
        }

        Map<Integer, Integer> callTimeMap = studentPointService.countByClassGroupByStudent(classId);
        if (Objects.nonNull(minCall) || Objects.nonNull(maxCall)) {
            criteria.andIdNotIn(callTimeMap.entrySet().stream()
                    .filter(entry -> (Objects.nonNull(minCall) && entry.getValue() < minCall)
                            || (Objects.nonNull(maxCall) && entry.getValue() > maxCall))
                    .map(Map.Entry::getKey).collect(Collectors.toList()));
        }

        PageHelper.startPage(page, limit);
        example.setOrderByClause(sort + " " + order);



        List<StudentInfo> studentInfoList =  studentInfoMapper.selectByExample(example);

        if (CollectionUtils.isEmpty(studentInfoList)) {
            return ImmutableMap.of("total", 0 , "items", Collections.emptyList());
        }

        Map<Integer, String> classMap = classService.getClassNameMap();


        List<StudentVo> dataList = studentInfoList.stream().map(x -> {
            StudentVo voData = new StudentVo();
            BeanUtils.copyProperties(x, voData);
            voData.setClassName(classMap.get(x.getClassId()));
            voData.setCallTimes(callTimeMap.getOrDefault(x.getId(), 0));

            if (StringUtils.isEmpty(x.getHead()) || x.getHead().trim().equalsIgnoreCase(STRING_DEFAULT_HEAD)) {
                voData.setHead(URL_DEFAULT_HEAD);
            }
            return voData;
        }).collect(Collectors.toList());
        Long total = PageInfo.of(studentInfoList).getTotal();
        return new HashMap(){{
            put("total", total);
            put("items", dataList);
        }};
    }

    /**
     * 查询可以被点名的学生名单
     * @param classId   班级ID
     * @param maxCall   排除点名达到指定次数的学生（null为不排除）
     * @return  查询到的list和总数信息
     */
    @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
    public Map<String, Object> listForCall(Integer classId, Integer maxCall) {
        Set<Integer> excluded = null;
        if (Objects.nonNull(maxCall) && maxCall > 0) {
            excluded = studentPointService.countByClassGroupByStudent(classId).entrySet().stream().filter(entry -> entry.getValue() >= maxCall).map(Map.Entry::getKey).collect(Collectors.toSet());
        }

        StudentInfoExample.Criteria criteria = new StudentInfoExample().createCriteria()
                .andDeletedEqualTo(StudentInfo.NOT_DELETED)
                .andClassIdEqualTo(classId);

        if (Objects.nonNull(maxCall) && maxCall > 0) {
            criteria.andIdNotIn(studentPointService.countByClassGroupByStudent(classId).entrySet().stream().filter(entry -> entry.getValue() >= maxCall).map(Map.Entry::getKey).collect(Collectors.toList()));
        }

        List<StudentInfo> queryList = studentInfoMapper.selectByExample(criteria.example())
                .stream().peek(x -> {
                    if (StringUtils.isEmpty(x.getHead()) || x.getHead().trim().equalsIgnoreCase(STRING_DEFAULT_HEAD)) {
                        x.setHead(URL_DEFAULT_HEAD);
                    }
                }).collect(Collectors.toList());
        return ImmutableMap.of("total", queryList.size(), "items", queryList);
    }

    public List<StudentInfoExcelImportVo> importByExcel(MultipartFile file) throws IOException {
        ExcelReaderListener<StudentInfoExcelImportVo> listener = new StudentInfoExcelReaderListener();
        EasyExcel.read(file.getInputStream(), StudentInfoExcelImportResultVo.class, listener).sheet().doRead();
        return listener.getAddList();
    }

    public List<StudentInfoExcelImportResultVo> checkImportByExcel(Collection<StudentInfoExcelImportVo> importVoList, Integer classId) {
        Map<String, StudentInfo> existStudent = selectStudentInfoByUniqueId(importVoList.stream().map(StudentInfoExcelImportVo::getStudentUid).filter(org.apache.commons.lang3.StringUtils::isNotBlank).collect(Collectors.toSet()))
                .stream().collect(Collectors.toMap(StudentInfo::getStudentUniqueId, x -> x, (k1, k2) -> k1));

        Map<Integer, String> classNameMap = classService.listAll().stream().collect(Collectors.toMap(ClassInfo::getId, ClassInfo::getClassName));

        return importVoList.stream().map(vo -> {
            List<String> err = new ArrayList<>();

            if (org.apache.commons.lang3.StringUtils.isBlank(vo.getStudentUid())) {
                err.add("学号不能为空");
            }

            if (org.apache.commons.lang3.StringUtils.isBlank(vo.getName())) {
                err.add("姓名不能为空");
            }


            if (org.apache.commons.lang3.StringUtils.isNotBlank(vo.getStudentUid()) && existStudent.containsKey(vo.getStudentUid())) {
                StudentInfo exist = existStudent.get(vo.getStudentUid());
                err.add(String.format("学号[%s]已存在，对应学生信息：[班级: '%s', 姓名: '%s']", vo.getStudentUid(), classNameMap.getOrDefault(exist.getClassId(), ""), exist.getName()));
            }

            if (CollectionUtils.isEmpty(err)) {
                return null;
            }

            StudentInfoExcelImportResultVo result = new StudentInfoExcelImportResultVo(vo);
            result.setErrMsg(err);

            return result;
        }).filter(Objects::nonNull).collect(Collectors.toList());
    }

    @Transactional(rollbackFor = Exception.class)
    public List<StudentInfoExcelImportResultVo> handleImport(Collection<? extends StudentInfoExcelImportVo> voList, Integer classId) {

        LocalDateTime now = LocalDateTime.now();
        return voList.stream().map(vo -> {
            StudentInfo record = new StudentInfo() {{
                setName(vo.getName());
                setClassId(classId);
                setStudentUniqueId(vo.getStudentUid());
                setAddTime(now);
                setUpdateTime(now);
                setDeleted(StudentInfo.NOT_DELETED);
                setHead(STRING_DEFAULT_HEAD);
            }};

            studentInfoMapper.insert(record);

            StudentInfoExcelImportResultVo result = new StudentInfoExcelImportResultVo(vo);
            result.setId(record.getId());
            return result;
        }).collect(Collectors.toList());
    }

    @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
    public List<StudentInfo> selectByPrimaryKey(Collection<Integer> idList) {
        return CollectionUtils.isEmpty(idList) ? Collections.emptyList()
                : studentInfoMapper.selectByExample(new StudentInfoExample().createCriteria()
                        .andDeletedEqualTo(StudentInfo.NOT_DELETED)
                        .andIdIn(new ArrayList<>(idList))
                    .example());
    }
}
