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

import cn.exploring.engine.server.db.dao.SpecialSqlMapper;
import cn.exploring.engine.server.db.dao.StudentPointInfoMapper;
import cn.exploring.engine.server.db.domain.StudentInfo;
import cn.exploring.engine.server.db.domain.StudentPointInfo;
import cn.exploring.engine.server.db.domain.StudentPointInfoExample;
import cn.exploring.engine.server.db.domain.vo.StudentPointExcelExportVo;
import cn.exploring.engine.server.db.domain.vo.StudentPointWithStudentInfoVo;
import cn.exploring.engine.server.db.util.DateTimeUtil;
import cn.exploring.engine.server.db.util.SqlUtil;
import cn.exploring.engine.server.db.util.StrUtil;
import cn.exploring.engine.server.db.util.Util;
import cn.zhxu.bs.BeanSearcher;
import cn.zhxu.bs.SearchResult;
import cn.zhxu.bs.operator.*;
import cn.zhxu.bs.util.MapBuilder;
import cn.zhxu.bs.util.MapUtils;
import com.alibaba.excel.EasyExcel;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.google.common.collect.ImmutableMap;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
public class StudentPointService {

    public static final Log log = LogFactory.getLog(StudentPointService.class);

    @Resource
    StudentPointInfoMapper studentPointInfoMapper;

    @Resource
    SpecialSqlMapper specialSqlMapper;

    @Resource
    StudentInfoService studentInfoService;

    @Autowired
    BeanSearcher beanSearcher;

    public Map<String, Object> selectPointByWeb( String studentName,  String studentUniqueId, Integer classId, String startTime,  String endTime, Integer page, Integer limit,  String sort,  String order) {
        LocalDateTime start = Optional.ofNullable(startTime).filter(StringUtils::isNotBlank).map(s -> LocalDateTime.parse(s, DateTimeUtil.DATE_TIME_FORMATTER3)).orElse(null);
        LocalDateTime end = Optional.ofNullable(endTime).filter(StringUtils::isNotBlank).map(s -> LocalDateTime.parse(s, DateTimeUtil.DATE_TIME_FORMATTER3)).orElse(null);

        MapBuilder wrapper = MapUtils.builder()
                .field(StudentPointWithStudentInfoVo::getDeleted, StudentPointInfo.NOT_DELETED).op(Equal.class)
                .field(StudentPointWithStudentInfoVo::getName, studentName).op(Contain.class)
                .field(StudentPointWithStudentInfoVo::getStudentUniqueId, studentUniqueId).op(Contain.class)
                .field(StudentPointWithStudentInfoVo::getClassId, classId).op(Equal.class)
                .field(StudentPointWithStudentInfoVo::getRecordTime, start).op(GreaterEqual.class)
                .field(StudentPointWithStudentInfoVo::getRecordTime, end).op(LessEqual.class);

        SearchResult<StudentPointWithStudentInfoVo> result = beanSearcher.search(StudentPointWithStudentInfoVo.class, SqlUtil.page(wrapper, page, limit, sort, order).build());
        return ImmutableMap.of("total", result.getTotalCount().longValue(), "items", result.getDataList());
    }

    /**
     * id 检索
     * @param idList
     * @return
     */
    public List<StudentPointInfo> selectPointInfo(List<Integer> idList) {
        StudentPointInfoExample example = new StudentPointInfoExample();
        StudentPointInfoExample.Criteria criteria = example.createCriteria();
        criteria.andDeletedEqualTo(false);
        if (idList != null && idList.size() > 0) {
            criteria.andIdIn(idList);
        }
        return studentPointInfoMapper.selectByExample(example);

    }

    public String addPoint(StudentPointInfo studentPoint) {
        LocalDateTime now = LocalDateTime.now();
        studentPoint.setRecordTime(now);
        studentPoint.setUpdateTime(now);
        studentPoint.setAddTime(now);
        studentPoint.setDeleted(false);
        studentPointInfoMapper.insertSelective(studentPoint);
        return "add OK";
    }

    public Object updatePoint(StudentPointInfo studentPoint) {
        LocalDateTime now = LocalDateTime.now();
        studentPoint.setUpdateTime(now);
        studentPointInfoMapper.updateByPrimaryKey(studentPoint);
        return "update OK";
    }

    public Object batchDeleted(List<Integer> idList) {
        List<StudentPointInfo> studentPointInfoList = selectPointInfo(idList);
        studentPointInfoList.forEach(x -> {
            x.setDeleted(null);
            updatePoint(x);
        });
        return "batch deleted OK";
    }

    @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
    public Map<Integer, List<StudentPointInfo>> selectByClassGroupByStudent(Integer classId) {
        StudentPointInfoExample.Criteria criteria = new StudentPointInfoExample().createCriteria()
                .andDeletedEqualTo(StudentPointInfo.NOT_DELETED);

        if (Objects.nonNull(classId)) {
            criteria.andClassIdEqualTo(classId);
        }

        return studentPointInfoMapper.selectByExample(criteria.example())
                .stream().collect(Collectors.groupingBy(StudentPointInfo::getStudentId, Collectors.toList()));
    }

    public Map<Integer, Integer> countByClassGroupByStudent(Integer classId) {
        return selectByClassGroupByStudent(classId).entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().size()));
    }

    @Transactional(rollbackFor = Exception.class)
    public void removeAll() {
        studentPointInfoMapper.updateByExampleSelective(new StudentPointInfo(){{
            setUpdateTime(LocalDateTime.now());
            setDeleted(StudentPointInfo.IS_DELETED);
        }} , new StudentPointInfoExample().createCriteria()
                        .andDeletedEqualTo(StudentPointInfo.NOT_DELETED)
                .example());
    }

    @Transactional(rollbackFor = Exception.class)
    public void removeByStudentId(Integer studentId) {
        StudentPointInfo record = new StudentPointInfo();
        record.setUpdateTime(LocalDateTime.now());
        record.setDeleted(true);

        int i = studentPointInfoMapper.updateByExampleSelective(record,
                new StudentPointInfoExample().createCriteria()
                        .andDeletedEqualTo(StudentPointInfo.NOT_DELETED)
                        .andStudentIdEqualTo(studentId)
                        .example());

        log.info(StrUtil.format("【成绩服务】已批量删除成绩  学生ID:'{}'  删除数量: '{}'", studentId, i));
    }

    @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
    public List<StudentPointInfo> listByClassId(Integer classId) {
        return studentPointInfoMapper.selectByExample(new StudentPointInfoExample().createCriteria()
                        .andDeletedEqualTo(StudentPointInfo.NOT_DELETED)
                        .andClassIdEqualTo(classId)
                .example());
    }

    public List<StudentPointExcelExportVo> transToExcelExportVo(Collection<StudentPointInfo> list) {
        if (CollectionUtils.isEmpty(list)) {
            return Collections.emptyList();
        }

        Map<Integer, StudentInfo> studentInfoMap = studentInfoService.selectByPrimaryKey(list.stream().map(StudentPointInfo::getStudentId).collect(Collectors.toSet()))
                .stream().collect(Collectors.toMap(StudentInfo::getId, x -> x, (k1, k2) -> k1));

        return list.stream().map(pointInfo -> new StudentPointExcelExportVo() {{
            StudentInfo studentInfo = studentInfoMap.getOrDefault(pointInfo.getStudentId(), new StudentInfo());
            setStudentName(studentInfo.getName());
            setStudentUid(studentInfo.getStudentUniqueId());
            setScore(pointInfo.getPoint());
            setAddTime(pointInfo.getAddTime());
        }}).collect(Collectors.toList());
    }

    public File transToExcelFile(List<StudentPointExcelExportVo> voList) {
        File file = null;
        try {
            file = File.createTempFile("TMP-EXCEL-", ".xlsx");

            EasyExcel.write(file, StudentPointExcelExportVo.class).sheet().doWrite(voList);
        } catch (IOException e) {
            e.printStackTrace();
        }

        return file;
    }


}
