package cn.com.qmth.stmms.ms.admin.api;

import cn.com.qmth.stmms.ms.admin.assembler.ScoreAssembler;
import cn.com.qmth.stmms.ms.admin.dto.ScoreCheckDTO;
import cn.com.qmth.stmms.ms.admin.utils.WaterMarkUtils;
import cn.com.qmth.stmms.ms.commons.config.AliYunOssConfig;
import cn.com.qmth.stmms.ms.commons.config.ImageConfig;
import cn.com.qmth.stmms.ms.commons.config.ImageServerConfig;
import cn.com.qmth.stmms.ms.commons.utils.MD5Util;
import cn.com.qmth.stmms.ms.commons.utils.OssUtil;
import cn.com.qmth.stmms.ms.commons.web.PageableDTO;
import cn.com.qmth.stmms.ms.core.cache.ParamCache;
import cn.com.qmth.stmms.ms.core.domain.MarkStage;
import cn.com.qmth.stmms.ms.core.domain.Paper;
import cn.com.qmth.stmms.ms.core.domain.Student;
import cn.com.qmth.stmms.ms.core.domain.Work;
import cn.com.qmth.stmms.ms.core.domain.task.MarkTask;
import cn.com.qmth.stmms.ms.core.repository.MarkTaskRepo;
import cn.com.qmth.stmms.ms.core.repository.PaperRepo;
import cn.com.qmth.stmms.ms.core.repository.StudentRepo;
import cn.com.qmth.stmms.ms.core.repository.WorkRepo;
import cn.com.qmth.stmms.ms.core.vo.Subject;
import cn.com.qmth.stmms.ms.marking.assembler.PaperAssembler;
import cn.com.qmth.stmms.ms.marking.dto.PaperDTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.persistence.criteria.Predicate;
import java.awt.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.*;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 成绩处理api
 * Created by zhengmin on 2017/1/6.
 */
@RestController
@RequestMapping("api/score")
public class ScoreApi {

    @Autowired
    private PaperRepo paperRepo;

    @Autowired
    private MarkTaskRepo markTaskRepo;

    @Autowired
    private StudentRepo studentRepo;

    @Autowired
    private WorkRepo workRepo;

    @Autowired
    private ScoreAssembler scoreAssembler;

    @Autowired
    private PaperAssembler paperAssembler;

    @Autowired
    ImageConfig imageConfig;

    @Resource
    OssUtil ossUtil;

    @Resource
    AliYunOssConfig aliYunOssConfig;

    @Resource
    ImageServerConfig imageServerConfig;

    /**
     * 查询
     *
     * @param workId     评卷工作id
     * @param examNumber 准考证号
     * @return
     */
    @RequestMapping(value = "search/byExamNumber", method = RequestMethod.GET)
    public ScoreCheckDTO getByExamNumber(@RequestParam Long workId,
                                         @RequestParam String examNumber) {
        Student student = studentRepo.findByWorkIdAndExamNumber(workId, examNumber);
        List<Paper> papers = paperRepo.findByWorkIdAndExamNumber(workId, examNumber);
        return scoreAssembler.toDTO(student, papers);
    }

    /**
     * 查询
     *
     * @param workId       评卷工作id
     * @param secretNumber 密号
     * @return
     */
    @RequestMapping(value = "search/bySecretNumber", method = RequestMethod.GET)
    public ScoreCheckDTO getBySecretNumber(@RequestParam Long workId,
                                           @RequestParam String secretNumber) {
        List<Paper> papers = paperRepo.findByWorkIdAndSecretNumber(workId, secretNumber);
        String examNumber = papers.get(0).getExamNumber();
        Student student = studentRepo.findByWorkIdAndExamNumber(workId, examNumber);

        return scoreAssembler.toDTO(student, papers);
    }

    /**
     * 查询
     *
     * @param workId       评卷工作id
     * @param secretNumber 任务密号
     * @return
     */
    @RequestMapping(value = "search/byTaskSecretNumber", method = RequestMethod.GET)
    public ScoreCheckDTO getByTaskSecretNumber(@RequestParam Long workId,
                                               @RequestParam String secretNumber) {
        List<Paper> papers = new ArrayList<Paper>();
        List<MarkTask> markTasks = markTaskRepo.findByWorkIdAndSecretNumber(workId, secretNumber);
        for (MarkTask markTask : markTasks) {
            papers.add(markTask.getPaper());
        }
        if (papers.isEmpty()) {
            return null;
        }
        String examNumber = papers.get(0).getExamNumber();
        Student student = studentRepo.findByWorkIdAndExamNumber(workId, examNumber);
        return scoreAssembler.toDTO(student, papers);
    }

    @RequestMapping(value = "search/byTotalScore", method = RequestMethod.GET)
    public ResponseEntity getByTotalScore(@RequestParam final Long workId,
                                          @RequestParam final Long totalScore) {
//        List<Object[]> paperList = paperRepo.findByTotalScore(workId);
//        List<String> examNumbers = paperList.stream().filter(p -> {
//            Double t = (Double) p[1];
//            return t.longValue() > totalScore;
//        }).map(p -> {
//            return (String) p[0];
//        }).collect(Collectors.toList());
//
//        List<Paper> papers = paperRepo.findByWorkIdAndExamNumberIn(workId, examNumbers);
//        return papers.stream().map(paper -> {
//            String examNumber = paper.getExamNumber();
//            Student student = studentRepo.findByWorkIdAndExamNumber(workId, examNumber);
//            return scoreAssembler.toDTO(student, papers);
//        }).collect(Collectors.toList());


        List<ScoreCheckDTO> scoreDTOs = new ArrayList<>();
        List<Paper> papers = paperRepo.findByWorkId(workId);
        Map<String, List<Paper>> group = papers.stream().collect(Collectors.groupingBy(Paper::getExamNumber));
        group.forEach((k, v) -> {
            //long ct = v.stream().mapToDouble(Paper::getScore).filter(s -> s >= hs).count();
            long studentTotalScore = 0;
            for (Paper p : v) {
                studentTotalScore += p.getScore();
            }
            if (v.size() == 3 && studentTotalScore >= totalScore) {

                Student student = studentRepo.findByWorkIdAndExamNumber(workId, k);
                scoreDTOs.add(scoreAssembler.toDTO(student, v));
            }
        });
        return ResponseEntity.ok(scoreDTOs);
    }

    /**
     * 查询任务两科成绩高于hs，另外一颗低于ls
     *
     * @param workId 评卷工作id
     * @param hs     高分
     * @param ls     低分
     * @return
     */
    @RequestMapping(value = "search/byRange", method = RequestMethod.GET)
    public ResponseEntity getByScoreRange(@RequestParam Long workId,
                                          @RequestParam Double hs,
                                          @RequestParam Double ls) {
        List<ScoreCheckDTO> scoreDTOs = new ArrayList<>();
        List<Paper> papers = paperRepo.findByScoreRange(hs, ls);
        Map<String, List<Paper>> group = papers.stream().collect(Collectors.groupingBy(Paper::getExamNumber));
        group.forEach((k, v) -> {
            long ct = v.stream().mapToDouble(Paper::getScore).filter(s -> s >= hs).count();
            if (v.size() == 3 && ct == 2) {
                Student student = studentRepo.findByWorkIdAndExamNumber(workId, k);
                scoreDTOs.add(scoreAssembler.toDTO(student, v));
            }
        });
        return ResponseEntity.ok(scoreDTOs);
    }

    /**
     * 更新分数
     *
     * @param paperId 试卷id
     * @param body    分数
     * @return
     */
    @RequestMapping(value = "update/{paperId}", method = RequestMethod.PUT)
    public ResponseEntity updateScore(@PathVariable Long paperId, @RequestBody ModelMap body) {
        Paper paper = paperRepo.findOne(paperId);
        if (paper == null || body.get("score") == null) {
            return new ResponseEntity(HttpStatus.OK);
        }
        Double score = Double.valueOf(body.get("score").toString());
        if (score >= 0 && score <= 100) {
            paper.setScore(score);
            paperRepo.save(paper);
        }
        return new ResponseEntity(HttpStatus.OK);
    }

    @RequestMapping(value = "merge/{subject}", method = RequestMethod.GET)
    public ResponseEntity merge(@PathVariable Subject subject) {
        Work activedWork = workRepo.findByActiveTrue();
        List<Paper> papers = paperRepo.findByWorkIdAndSubject(activedWork.getId(), subject);
        Iterator<Paper> paperIterator = papers.iterator();
        long count = 0;
        while (paperIterator.hasNext()) {
            Paper paper = paperIterator.next();
            List<MarkTask> markTasks = markTaskRepo.findByPaperIdAndStage(paper.getId(), MarkStage.SCORE);
            long leftCount = markTasks.stream().filter(i -> i.getResult() == null).count();
            if (leftCount == 0) {
                OptionalDouble finalScore = markTasks.stream().map(m -> m.getResult())
                        .mapToInt(Integer::valueOf).average();
                double fs = Math.round(finalScore.orElse(0));
                paper.setScore(fs);
                paperRepo.save(paper);
                count++;
            }
            if (count % 500 == 0) {
                System.out.println(subject.toString() + ":" + count);
            }
        }
        return ResponseEntity.ok(null);
    }

    @Value("${sys.config.imageDir}")
    private String imagePath;

    @Value("${sys.config.watermark}")
    private String watermark;

    private static int watermarkTotal;
    private static int watermarkFinished;
    private static boolean watermarking = false;

    @PostMapping("/watermark")
    public void watermark(@RequestParam("workId") Long workId) {
        if (!watermarking) {
            watermarking = true;
            watermarkTotal = 0;
            watermarkFinished = 0;

            final Font font = new Font("宋体", Font.PLAIN, 60);                     //水印字体
            final Color color = new Color(254, 3, 10, 255);                               //水印图片色彩以及透明度
            final List<Paper> papers = paperRepo.findByWorkId(workId);
            watermarkTotal = papers.size();

            new Thread(() -> {

                for (int i = 0; i < watermarkTotal; i++) {
                    watermarkFinished = i + 1;
                    Paper paper = papers.get(i);
                    Double score = paper.getInspectScore() == null ? paper.getScore() : paper.getInspectScore();
                    if (Objects.isNull(score)) {
                        continue;
                    }
                    String examNumber = paper.getExamNumber();
                    String areaCode = paper.getAreaCode();
                    Subject subject = paper.getSubject();

                    String imageFile = null;
                    String watermarkFile = null;
                    String imageMd5 = null;
                    StringJoiner watermarkSj = null;
                    if (ParamCache.paramMap.get(workId).getImageEncrypt() == 1) {
                        Student student = studentRepo.findByWorkIdAndExamNumber(workId, examNumber);
                        imageMd5 = MD5Util.getImageRuleMd5(workId, subject.ordinal(), areaCode, examNumber, student.getId());

                        imageFile = imagePath + "/" + workId + "/" + subject.toString() + "/" + areaCode + "/" + imageMd5;
                        watermarkFile = watermark + "/" + workId + "/" + subject.toString() + "/" + areaCode + "/" + imageMd5;
                    } else {
                        if (imageConfig.isCustomSubject() && imageServerConfig.isAliyunOss()) {
                            //阿里云oss图片加水印
                            String imageDirName = aliYunOssConfig.getImageDir();
                            String watermarkDirName = aliYunOssConfig.getWatermark();
                            imageDirName = imageDirName.substring(imageDirName.indexOf(aliYunOssConfig.getBucket() + File.separator), imageDirName.lastIndexOf("/")).replace(aliYunOssConfig.getBucket() + File.separator, "");
                            watermarkDirName = watermarkDirName.substring(watermarkDirName.indexOf(aliYunOssConfig.getBucket() + File.separator), watermarkDirName.lastIndexOf("/")).replace(aliYunOssConfig.getBucket() + File.separator, "");
                            StringJoiner imageSj = new StringJoiner("").add(imageDirName).add(File.separator).add(String.valueOf(paper.getWorkId())).add(File.separator).add(paper.getSubject().toString()).add(File.separator).add(paper.getAreaCode()).add(File.separator).add(paper.getExamNumber()).add(".jpg");
                            watermarkSj = new StringJoiner("").add(watermarkDirName).add(File.separator).add(String.valueOf(paper.getWorkId())).add(File.separator).add(paper.getSubject().toString()).add(File.separator).add(paper.getAreaCode()).add(File.separator).add(paper.getExamNumber()).add(".jpg");
                            imageFile = imagePath + "/" + workId + "/" + subject.toString() + "/" + areaCode + "/" + examNumber + ".jpg";
                            watermarkFile = watermark + "/" + workId + "/" + subject.toString() + "/" + areaCode + "/" + examNumber + ".jpg";
                            try {
                                ossUtil.ossDownload(imageSj.toString(), imageFile);
                                ossUtil.ossDownload(watermarkSj.toString(), watermarkFile);
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        } else {
                            imageFile = imagePath + "/" + workId + "/" + subject.toString() + "/" + areaCode + "/" + examNumber + ".jpg";
                            watermarkFile = watermark + "/" + workId + "/" + subject.toString() + "/" + areaCode + "/" + examNumber + ".jpg";
                        }
                    }
                    File targetFile = new File(watermarkFile);
                    targetFile.getParentFile().mkdirs();//创建父级目录
                    String txt = "【" + paper.getExamNumber() + "】【" + paper.getStudentName() + "】【" + String.valueOf(score.intValue()) + "分】";
                    String text = new String(txt.getBytes(), Charset.forName("UTF-8"));
//                    WaterMarkUtils.addWaterMark(imageFile, watermarkFile, text, color, font);
                    try {
                        if (ParamCache.paramMap.get(workId).getImageEncrypt() == 1) {
                            WaterMarkUtils.addTextWatermarkNew(imageMd5, text, "jpg", imageFile, watermarkFile);
                        } else {
                            File watermarkF = WaterMarkUtils.addTextWatermark(text, "jpg", imageFile, watermarkFile);
                            if (imageConfig.isCustomSubject() && imageServerConfig.isAliyunOss()) {
                                ossUtil.ossUpload(watermarkSj.toString(), new FileInputStream(watermarkF));
                                watermarkF.delete();
                            }
                        }
                        System.out.println(watermarkFile + "生成成功");
                    } catch (IOException e) {
                        System.out.println(watermarkFile + "生成失败");
                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                }

                watermarking = false;
            }).start();
        } else {
            throw new RuntimeException("请勿重复生成");
        }


    }

    @GetMapping("/watermark")
    public Map<String, Object> watermarkProgress(@RequestParam("workId") Long workId) {
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("watermarkTotal", watermarkTotal);
        resultMap.put("watermarkFinished", watermarkFinished);
        resultMap.put("watermarking", watermarking);
        return resultMap;
    }

    @RequestMapping(value = "search/byCondition", method = RequestMethod.GET)
    public PageableDTO byCondition(
            @RequestParam Long workId,
            @RequestParam String areaCode,
            @RequestParam Subject subject,
            @RequestParam(required = false) Long startNumber,
            @RequestParam(required = false) Long endNumber,
            @RequestParam(required = false) Boolean isManual,
            @RequestParam(required = false) Boolean missing,
            Pageable pageable) {
        Specification<Paper> specification = (root, query, builder) -> {
            List<Predicate> predicates = new ArrayList<>();
            predicates.add(builder.equal(root.get("workId"), workId));
            predicates.add(builder.equal(root.get("areaCode"), areaCode));
            predicates.add(builder.equal(root.get("subject"), subject));
            if (startNumber != null && endNumber != null) {
                predicates.add(builder.between(root.get("examNumber"), startNumber, endNumber));
            } else if (startNumber != null) {
                predicates.add(builder.equal(root.get("examNumber"), startNumber));
            }
            if (Objects.nonNull(missing)) {
                predicates.add(builder.equal(root.get("isMissing"), missing));
            }
            predicates.add(builder.equal(root.get("isManual"), Optional.ofNullable(isManual).orElse(true)));
            predicates.add(builder.equal(root.get("test"), 0));
            return builder.and(predicates.toArray(new Predicate[predicates.size()]));
        };
        List<PaperDTO> paperDTOs = new ArrayList<>();
        Page<Paper> paperPage = paperRepo.findAll(specification, pageable);
        paperPage.getContent().forEach(p -> {
            paperDTOs.add(paperAssembler.toDTOForSheet(p));
        });
        return new PageableDTO(paperDTOs, paperPage.getTotalElements(), paperPage.getTotalPages(), pageable.getPageNumber());
    }

    /**
     * 标记缺考
     *
     * @param id
     * @return
     */
    @PostMapping("/missing/{id}")
    public ResponseEntity doMissing(@PathVariable("id") Long id) {
        //更新为缺考
        Paper paper = paperRepo.findOne(id);
        paper.setIsMissing(!paper.getIsMissing());
        paperRepo.save(paper);
        Map map = new HashMap();
        map.put("missing", paper.getIsMissing());
        return ResponseEntity.ok(map);
    }
}
