package com.yinlihupo.enlish.service.service.exam;

import com.yinlihupo.enlish.service.constant.ExamWordsConstant;
import com.yinlihupo.enlish.service.domain.dataobject.*;
import com.yinlihupo.enlish.service.domain.mapper.*;
import com.yinlihupo.enlish.service.service.ExamWordsService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;

@Service
@Slf4j
public class ExamWordsServiceImpl implements ExamWordsService {

    @Resource
    private VocabularyBankDOMapper vocabularyBankDOMapper;
    @Resource
    private ExamWordsDOMapper examWordsDOMapper;
    @Resource
    private StudentExamWordsDOMapper studentExamWordsDOMapper;
    @Resource
    private ExamWordsJudgeResultDOMapper examWordsJudgeResultDOMapper;
    @Resource
    private StudentDOMapper studentDOMapper;
    @Resource
    private UnitDOMapper unitDOMapper;

    @Value("${templates.count}")
    private Integer wordCount;
    @Value("${tmp.png}")
    String tmpPng;

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public ExamWordsDO generateExamWords(Integer studentId, Integer type) {

        ExamWordsDO examWordsDO;

        if (type == ExamWordsConstant.EXAM_TYPE_BASELINE) {
            log.info("生成摸底测试");
            examWordsDO = generateBaselineExamWords(studentId);
        } else if (type == ExamWordsConstant.EXAM_TYPE_MIDTERM) {
            log.info("生成期中测试");
            examWordsDO = generateMidtermExamWords(studentId);
        } else  {
            log.info("生成期末测试");
            examWordsDO = generateFinalExamWords(studentId);
        }

        List<Integer> wordIds = new ArrayList<>(examWordsDO.getWordIds());
        if (wordIds.size() < wordCount) {
            log.info("单词数量不足，补充单词");
            StudentDO studentDO = studentDOMapper.selectStudentById(studentId);
            List<VocabularyBankDO> vocabularyBankDOS = vocabularyBankDOMapper.selectByGradeIdAndNotMatchIds(studentDO.getGradeId(), wordIds, wordCount - wordIds.size());
            List<Integer> list = new ArrayList<>(vocabularyBankDOS.stream().map(VocabularyBankDO::getId).toList());
            wordIds.addAll(list);
            examWordsDO.setWordIds(wordIds);
        }

        return examWordsDO;
    }

    private ExamWordsDO generateBaselineExamWords(Integer studentId) {
        StudentDO studentDO = studentDOMapper.selectStudentById(studentId);

        Integer gradeId = studentDO.getActualGradeId();

        int zoneA = ExamWordsConstant.getZoneA(gradeId);
        int zoneASize = ExamWordsConstant.ZONE_A_SIZE;
        List<VocabularyBankDO> vocabularyBankDOS = new ArrayList<>(vocabularyBankDOMapper.selectVocabularyBankListByGradeIdRandom(zoneA, zoneASize));

        int zoneB = ExamWordsConstant.getZoneB(gradeId);
        int zoneBSize = ExamWordsConstant.ZONE_B_SIZE;
        vocabularyBankDOS.addAll(vocabularyBankDOMapper.selectVocabularyBankListByGradeIdRandom(zoneB, zoneBSize));

        int zoneC = ExamWordsConstant.getZoneC(gradeId);
        int zoneCSize = ExamWordsConstant.ZONE_C_SIZE;
        vocabularyBankDOS.addAll(vocabularyBankDOMapper.selectVocabularyBankListByGradeIdRandom(zoneC, zoneCSize));

        int zoneD = ExamWordsConstant.getZoneD(gradeId);
        int zoneDSize = ExamWordsConstant.ZONE_D_SIZE;
        vocabularyBankDOS.addAll(vocabularyBankDOMapper.selectVocabularyBankListByGradeIdRandom(zoneD, zoneDSize));

        int zoneE = ExamWordsConstant.getZoneE(gradeId);
        int zoneESize = ExamWordsConstant.ZONE_E_SIZE;
        vocabularyBankDOS.addAll(vocabularyBankDOMapper.selectVocabularyBankListByGradeIdRandom(zoneE, zoneESize));

        int zoneF = ExamWordsConstant.getZoneF(gradeId);
        int zoneFSize = ExamWordsConstant.ZONE_F_SIZE;
        vocabularyBankDOS.addAll(vocabularyBankDOMapper.selectVocabularyBankListByGradeIdRandom(zoneF, zoneFSize));

        ExamWordsDO examWordsDO = ExamWordsDO.builder()
                .gradeId(gradeId)
                .level(1)
                .type(ExamWordsConstant.EXAM_TYPE_BASELINE)
                .title("摸低测试" + studentDO.getName())
                .createdAt(LocalDateTime.now())
                .wordIds(vocabularyBankDOS.stream().map(VocabularyBankDO::getId).toList())
                .build();

        return getExamWordsDO(studentId, examWordsDO);
    }

    private ExamWordsDO generateMidtermExamWords(Integer studentId) {
        StudentDO studentDO = studentDOMapper.selectStudentById(studentId);
        Integer gradeId = studentDO.getGradeId();
        List<UnitDO> unitDOS = unitDOMapper.selectByUnitName(ExamWordsConstant.getGradeName(gradeId) + "上");
        ExamWordsDO examWordsDO = getExamWordsDO(studentId, studentDO, gradeId, unitDOS);
        examWordsDO.setTitle("期中测试" + studentDO.getName());
        return getExamWordsDO(studentId, examWordsDO);

    }

    private ExamWordsDO generateFinalExamWords(Integer studentId) {
        StudentDO studentDO = studentDOMapper.selectStudentById(studentId);
        Integer gradeId = studentDO.getGradeId();
        List<UnitDO> unitDOS = unitDOMapper.selectByUnitName(ExamWordsConstant.getGradeName(gradeId));
        ExamWordsDO examWordsDO = getExamWordsDO(studentId, studentDO, gradeId, unitDOS);
        examWordsDO.setTitle("期末测试" + studentDO.getName());
        return getExamWordsDO(studentId, examWordsDO);
    }

    @NonNull
    private ExamWordsDO getExamWordsDO(Integer studentId, StudentDO studentDO, Integer gradeId, List<UnitDO> unitDOS) {
        if (unitDOS.isEmpty()) {
            throw new RuntimeException("没有找到对应的单元");
        }
        List<VocabularyBankDO> vocabularyBankDOS = vocabularyBankDOMapper.selectByUnitIds(unitDOS.stream().map(UnitDO::getId).toList());

        ExamWordsDO examWordsDO = ExamWordsDO.builder()
                .gradeId(gradeId)
                .level(1)
                .type(ExamWordsConstant.EXAM_TYPE_BASELINE)
                .title(studentDO.getName())
                .createdAt(LocalDateTime.now())
                .wordIds(vocabularyBankDOS.stream().map(VocabularyBankDO::getId).toList())
                .build();

        return getExamWordsDO(studentId, examWordsDO);
    }

    @NonNull
    private ExamWordsDO getExamWordsDO(Integer studentId, ExamWordsDO examWordsDO) {
        int insert = examWordsDOMapper.insert(examWordsDO);
        if (insert <= 0) {
            throw new RuntimeException("插入考试失败");
        }

        int insertStudentsExam = studentExamWordsDOMapper.insertStudentsExam(studentId, examWordsDO.getId());
        if (insertStudentsExam <= 0) {
            throw new RuntimeException("插入学生关联考试失败");
        }

        return examWordsDO;
    }

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public int saveExamWordsPngToDbAndLocal(MultipartFile file) {

        File dir = new File(tmpPng);
        if (!dir.exists()) {
            dir.mkdirs();
        }

        try {
            String originalFilename = file.getOriginalFilename();
            String suffix = "";
            if (originalFilename != null && originalFilename.contains(".")) {
                suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
            }
            String newFileName = UUID.randomUUID() + suffix;
            String path = tmpPng + newFileName;

            File dest = new File(path);
            file.transferTo(dest);
            int insert = examWordsJudgeResultDOMapper.insert(path);
            log.info("上传文件成功");
            return insert;
        } catch (IOException e) {
            throw new RuntimeException("上传失败", e);
        }

    }

    @Override
    public void updateExamWordsWordIdsOrder(ExamWordsDO examWordsDO) {
        examWordsDOMapper.updateWordIdsOrder(examWordsDO);
    }

    @Override
    public ExamWordsDO getExamWordsDOById(Integer id) {
        return examWordsDOMapper.selectById(id);
    }


}
