package com.qmth.themis.business.templete.impl;

import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.qmth.themis.business.annotation.ExcelNotExport;
import com.qmth.themis.business.annotation.ExcelNote;
import com.qmth.themis.business.cache.bean.ExamCacheBean;
import com.qmth.themis.business.constant.SystemConstant;
import com.qmth.themis.business.dto.MarkResultSimpleExportDto;
import com.qmth.themis.business.dto.response.MarkResultDto;
import com.qmth.themis.business.entity.TOeExamRecord;
import com.qmth.themis.business.entity.TSyncExamStudentScore;
import com.qmth.themis.business.service.TEExamService;
import com.qmth.themis.business.service.TEExamStudentService;
import com.qmth.themis.business.service.TOeExamRecordService;
import com.qmth.themis.business.service.TSyncExamStudentScoreService;
import com.qmth.themis.business.templete.TaskExportCommon;
import com.qmth.themis.business.templete.TaskExportTemplete;
import com.qmth.themis.business.templete.service.TempleteLogicService;
import com.qmth.themis.common.contanst.Constants;
import com.qmth.themis.common.util.GsonUtil;
import com.qmth.themis.common.util.Result;
import com.qmth.themis.common.util.ResultUtil;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.io.*;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @Description: 成绩简版导出任务
 * @Param:
 * @return:
 * @Author: wangliang
 * @Date: 2020/10/20
 */
@Service
public class TaskMarkResultSimpleExportTemplete implements TaskExportTemplete {

    private final static Logger log = LoggerFactory.getLogger(TaskMarkResultSimpleExportTemplete.class);

    @Resource
    TEExamStudentService teExamStudentService;

    @Resource
    TempleteLogicService templeteLogicService;

    @Resource
    TEExamService teExamService;

    @Resource
    TOeExamRecordService tOeExamRecordService;

    @Resource
    TSyncExamStudentScoreService tSyncExamStudentScoreService;

    /**
     * 考场导出模版
     *
     * @param map
     * @return
     */
    @Override
    public Result exportTask(Map<String, Object> map) throws IOException {
        log.info("开始导出成绩简版数据");
        long start = System.currentTimeMillis();
        TaskExportCommon taskExportCommon = new TaskExportCommon(map);
        taskExportCommon.init();
        taskExportCommon.setTxtList(new ArrayList());
        SXSSFWorkbook wb = null;
        StringJoiner stringJoiner = null;
        String excelFilePath = null;
        InputStream inputStream = null;
        ByteArrayOutputStream out = null;
        int min = 0, line = 0;
        try {
            taskExportCommon.getTxtList()
                    .add(DateUtil.format(new Date(), Constants.DEFAULT_DATE_PATTERN) + "->开始准备处理导出的成绩简版数据");
            Long examId = (Long) map.get(SystemConstant.EXAM_ID);
            Long activityId = (Long) map.get(SystemConstant.ACTIVITY_ID);
            String identity = (String) map.get("identity");
            String name = (String) map.get(SystemConstant.NAME);
            String courseCode = (String) map.get(SystemConstant.COURSE_CODE);

            List<MarkResultSimpleExportDto> markResultSimpleExportDtoList = null;
            int pageSize = SystemConstant.MAX_QUERY_SIZE, pageNumber = SystemConstant.MIN_QUERY_SIZE;
            IPage<MarkResultSimpleExportDto> markResultQueryExportIpage = teExamStudentService.markResultQueryExportIpage(new Page<>(pageNumber, pageSize), examId, activityId, identity, name, courseCode);
            while (Objects.nonNull(markResultQueryExportIpage) && markResultQueryExportIpage.getRecords().size() > 0) {
                markResultSimpleExportDtoList = Objects.isNull(markResultSimpleExportDtoList) ? new ArrayList<>(markResultQueryExportIpage.getRecords().size() * 10) : markResultSimpleExportDtoList;
                pageNumber++;
                markResultSimpleExportDtoList.addAll(markResultQueryExportIpage.getRecords());
                markResultQueryExportIpage = teExamStudentService.markResultQueryExportIpage(new Page<>(pageNumber, pageSize), examId, activityId, identity, name, courseCode);
            }

            if (!CollectionUtils.isEmpty(markResultSimpleExportDtoList)) {
                Set<String> examRecordIdsSet = new LinkedHashSet<>(markResultSimpleExportDtoList.size());
                for (MarkResultSimpleExportDto markResultSimpleExportDto : markResultSimpleExportDtoList) {
                    MarkResultDto markResultDto = GsonUtil
                            .fromJson(GsonUtil.toJson(markResultSimpleExportDto), MarkResultDto.class);
                    if (Objects.nonNull(markResultDto.getExamId())) {
                        ExamCacheBean examCacheBean = teExamService.getExamCacheBean(markResultDto.getExamId());
                        TOeExamRecord tOeExamRecord = tOeExamRecordService
                                .findMarkResult(markResultDto, examCacheBean.getRecordSelectStrategy());
                        if (Objects.nonNull(tOeExamRecord)) {
                            examRecordIdsSet.add(String.valueOf(tOeExamRecord.getId()));
                            markResultSimpleExportDto.setObjectiveScore(Objects.isNull(tOeExamRecord.getObjectiveScore()) ? "0" : String.valueOf(tOeExamRecord.getObjectiveScore()));
                            markResultSimpleExportDto.setRecordId(String.valueOf(tOeExamRecord.getId()));
                            markResultSimpleExportDto.setPaperId(tOeExamRecord.getPaperId());
                            markResultSimpleExportDto.setSubjectiveScore(Objects.isNull(markResultSimpleExportDto.getSubjectiveScore()) ? "0" : String.valueOf(markResultSimpleExportDto.getSubjectiveScore()));
                            markResultSimpleExportDto.setSumScore(String.valueOf(Double.parseDouble(markResultSimpleExportDto.getObjectiveScore()) + Double.parseDouble(markResultSimpleExportDto.getSubjectiveScore())));
                            if (Objects.nonNull(tOeExamRecord.getFirstStartTime())) {
                                markResultSimpleExportDto.setFirstStartTime(DateUtil.format(new Date(tOeExamRecord.getFirstStartTime()), SystemConstant.DEFAULT_DATE_PATTERN));
                            }
                            if (Objects.nonNull(tOeExamRecord.getFinishTime())) {
                                markResultSimpleExportDto.setFinishTime(DateUtil.format(new Date(tOeExamRecord.getFinishTime()), SystemConstant.DEFAULT_DATE_PATTERN));
                            }
                        }
                    }
                }
                if (!CollectionUtils.isEmpty(examRecordIdsSet)) {
                    Map<String, MarkResultSimpleExportDto> markResultSimpleExportDtoMap = markResultSimpleExportDtoList.stream().collect(
                            Collectors.toMap(MarkResultSimpleExportDto::getRecordId, Function.identity(), (dto1, dto2) -> dto1));

                    //获取云阅卷同步成绩
                    List<TSyncExamStudentScore> tSyncExamStudentScoreList = tSyncExamStudentScoreService.findByExamRecordIds(examRecordIdsSet);
                    if (!CollectionUtils.isEmpty(tSyncExamStudentScoreList)) {
                        for (TSyncExamStudentScore t : tSyncExamStudentScoreList) {
                            MarkResultSimpleExportDto markResultSimpleExportDto = markResultSimpleExportDtoMap.get(t.getExamNumber());
                            if (Objects.nonNull(markResultSimpleExportDto)) {
                                markResultSimpleExportDto.setSubjectiveScore(Objects.isNull(t.getSubjectiveScore()) ? "0" : String.valueOf(t.getSubjectiveScore()));
                                markResultSimpleExportDto.setSumScore(String.valueOf(Double.parseDouble(markResultSimpleExportDto.getObjectiveScore()) + Double.parseDouble(markResultSimpleExportDto.getSubjectiveScore())));
                            }
                        }
                    }
                }
            }
            boolean oss = (boolean) taskExportCommon.getOssEnv().get(SystemConstant.OSS);
            stringJoiner = new StringJoiner("");
            if (!oss) {
                stringJoiner.add(SystemConstant.FILES_DIR).add(File.separator);
            }
            stringJoiner = SystemConstant.getDirName(stringJoiner, false);
            //创建excel
            wb = new SXSSFWorkbook();
            Sheet sheet = wb.createSheet("成绩统计简版");
            CellStyle style = wb.createCellStyle();
            style.setAlignment(HorizontalAlignment.CENTER); // 水平居中格式
            style.setVerticalAlignment(VerticalAlignment.CENTER); //垂直居中
            Row row = sheet.createRow(0);
            Field[] fields = MarkResultSimpleExportDto.class.getDeclaredFields();
            //绘制表头
            for (int i = 0; i < fields.length; i++) {
                Field field = fields[i];
                field.setAccessible(true);
                Annotation annotation = field.getAnnotation(ExcelNotExport.class);
                if (Objects.isNull(annotation)) {
                    Cell cell = row.createCell(i);
                    cell.setCellValue(field.getAnnotation(ExcelNote.class).value());
                    cell.setCellStyle(style);
//                    sheet.setColumnWidth(i, 15 * 256);
                }
            }
            int rowIndex = 0, cellIndex = 0, max = SystemConstant.MAX_EXPORT_SIZE, size = markResultSimpleExportDtoList
                    .size();
            if (max >= size) {
                max = size;
            }
            while (max <= size) {
                List subList = markResultSimpleExportDtoList.subList(min, max);
                //绘制数据
                for (int y = 0; y < subList.size(); y++) {
                    line++;
                    MarkResultSimpleExportDto markResultSimpleExportDto = (MarkResultSimpleExportDto) subList.get(y);
                    rowIndex++;
                    cellIndex = 0;
                    Row sxssfRow = sheet.createRow(rowIndex);
                    for (Field field : fields) {
                        field.setAccessible(true);
                        Annotation annotation = field.getAnnotation(ExcelNotExport.class);
                        if (Objects.isNull(annotation)) {
                            taskExportCommon
                                    .createExcelCell(sxssfRow, cellIndex, field.get(markResultSimpleExportDto), style);
                            cellIndex++;
                        }
                    }
                }
                map.put("max", max);
                map.put("min", min);
                map.put("size", size);
                map = templeteLogicService.execExportMarkResultSimpleLogic(map);
                if (max == size) {
                    break;
                }
                min = max;
                max += SystemConstant.MAX_EXPORT_SIZE;
                if (max >= size) {
                    max = size;
                }
            }
            //临时缓冲区
            out = new ByteArrayOutputStream();
            wb.write(out);
            byte[] bookByteAry = out.toByteArray();
            inputStream = new ByteArrayInputStream(bookByteAry);
            excelFilePath = taskExportCommon.ossUpload(inputStream, stringJoiner.toString());
            long end = System.currentTimeMillis();
            log.info("导出成绩简版数据结束,============耗时============:{}秒", (end - start) / 1000);
        } catch (Exception e) {
            log.error("导出报错", e);
            taskExportCommon.exception(line, e, taskExportCommon.getTxtList());
        } finally {
            //这里写入txt文件
            taskExportCommon.writeExportResultTxt(stringJoiner.toString(), taskExportCommon.getTxtList().toString(),
                    excelFilePath);
            if (Objects.nonNull(wb)) {
                wb.dispose();
            }
            if (Objects.nonNull(inputStream)) {
                inputStream.close();
            }
            if (Objects.nonNull(out)) {
                out.flush();
                out.close();
            }
        }
        return ResultUtil.ok(true);
    }
}