package com.yinlihupo.enlish.service.service.plan;

import com.yinlihupo.enlish.service.constant.ExamWordsConstant;
import com.yinlihupo.enlish.service.constant.LessonPlanConstant;
import com.yinlihupo.enlish.service.domain.dataobject.*;
import com.yinlihupo.enlish.service.domain.mapper.*;
import com.yinlihupo.enlish.service.model.bo.Sentence;
import com.yinlihupo.enlish.service.model.bo.Word;
import com.yinlihupo.enlish.service.service.LessonPlansService;
import com.yinlihupo.enlish.service.utils.DifyClient;
import com.yinlihupo.enlish.service.utils.StringToPlanMapUtil;
import com.yinlihupo.framework.common.util.JsonUtils;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class LessonPlansServiceImpl implements LessonPlansService {

    @Resource
    private LessonPlansDOMapper lessonPlansDOMapper;
    @Resource
    private StudentLessonPlansDOMapper studentLessonPlansDOMapper;
    @Resource
    private VocabularyBankDOMapper vocabularyBankDOMapper;
    @Resource
    private UnitDOMapper unitDOMapper;
    @Resource
    private GradeUnitDOMapper gradeUnitDOMapper;
    @Resource
    private GradeDOMapper gradeDOMapper;
    @Resource
    private DifyClient difyClient;
    @Resource
    private ExamWordsDOMapper examWordsDOMapper;
    @Resource
    private StudentExamWordsDOMapper studentExamWordsDOMapper;
    @Resource
    private StudentDOMapper studentDOMapper;
    @Resource
    private PlanExamDOMapper planExamDOMapper;
    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    @Resource
    private ClassDOMapper classDOMapper;

    @Override
    public void generateLessonPlans(Integer studentId, Integer unitId, Integer wordSize) {
        String key = LessonPlanConstant.buildGeneratePlanContent(studentId);
        redisTemplate.opsForValue().set(key, studentId);
        redisTemplate.expire(key, 7, TimeUnit.MINUTES);

        log.info("开始生成计划");
        List<VocabularyBankDO> vocabularyBankDOS = vocabularyBankDOMapper.selectVocabularyBankDOAllByUnitId(unitId);
        UnitDO unitDO = unitDOMapper.selectByPrimaryKey(unitId);
        GradeUnitDO gradeUnitDO = gradeUnitDOMapper.selectByUnitId(unitId);
        GradeDO gradeDO = gradeDOMapper.selectById(gradeUnitDO.getGradeId());

        List<VocabularyBankDO> totalWords = new ArrayList<>();

        // 补差词汇所用词汇的
        List<VocabularyBankDO> vocabularyBankListStudentNotMaster = getVocabListRandom(vocabularyBankDOMapper
                .selectVocabularyBankListStudentNotMaster(gradeUnitDO.getGradeId(), studentId), 50);
        int gapSize = vocabularyBankListStudentNotMaster.size();
        int countGap = gapSize / 5;

        int syncSize = vocabularyBankDOS.size();
        wordSize = wordSize <= 0 ? syncSize / 5 : wordSize;
        int checkTotal = 50;
        List<List<VocabularyBankDO>> weeksSync = new ArrayList<>();
        List<List<VocabularyBankDO>> weeksGap = new ArrayList<>();
        int j = 0;
        for (int i = 0; i < 5; i++) {
            List<VocabularyBankDO> syncVocabList;
            if ((i + 1) * wordSize < syncSize) {
                syncVocabList = vocabularyBankDOS.subList(i * wordSize, (i + 1) * wordSize);
            } else {
                syncVocabList = new ArrayList<>(weeksSync.get(Math.min(j++,  weeksSync.size() - 1)));
            }
            List<VocabularyBankDO> gapVocabList = vocabularyBankListStudentNotMaster.subList(i * countGap, Math.min(i * countGap + countGap, gapSize));
            weeksSync.add(syncVocabList);
            weeksGap.add(gapVocabList);

            totalWords.addAll(syncVocabList);
            totalWords.addAll(gapVocabList);

            List<VocabularyBankDO> reviewVocabList = new ArrayList<>();
            List<VocabularyBankDO> checkList = new ArrayList<>();
            // 艾宾浩斯遗忘曲线
            switch (i) {
                case 1 -> reviewVocabList.addAll(syncVocabList);
                case 2 -> {
                    reviewVocabList.addAll(weeksSync.get(0));
                    reviewVocabList.addAll(weeksSync.get(1));

                    checkList.addAll(weeksSync.get(1));
                    checkList.addAll(weeksSync.get(1));
                }
                case 3 -> {
                    reviewVocabList.addAll(weeksSync.get(1));
                    reviewVocabList.addAll(weeksGap.get(2));

                    checkList.addAll(weeksSync.get(1));
                    checkList.addAll(weeksSync.get(2));
                }
                case 4 -> {
                    reviewVocabList.addAll(weeksSync.get(2));
                    reviewVocabList.addAll(weeksGap.get(3));

                    checkList.addAll(weeksSync.get(2));
                    checkList.addAll(weeksSync.get(3));
                }
            }
            List<VocabularyBankDO> checkWords = vocabularyBankDOMapper
                    .selectVocabularyBankListSelfCheck(gradeUnitDO.getGradeId(), studentId, checkList.stream().map(VocabularyBankDO::getId).toList(), Math.max(checkTotal - checkList.size(), 0));
            checkList.addAll(checkWords);

            Map<String, Object> lessonPlanMap = null;
            try {
                lessonPlanMap = generateWeekdayPlans(syncVocabList, gapVocabList, reviewVocabList, checkList, i + 1, gradeDO, unitDO, studentId);
                LessonPlansDO lessonPlansDO = LessonPlansDO.builder()
                        .title(lessonPlanMap.get("title").toString())
                        .gradeId(gradeDO.getId().toString())
                        .unitId(unitDO.getId())
                        .createdAt(LocalDateTime.now())
                        .contentDetails(JsonUtils.toJsonString(lessonPlanMap))
                        .build();
                lessonPlansDOMapper.insert(lessonPlansDO);

                StudentLessonPlansDO studentLessonPlansDO = StudentLessonPlansDO.builder()
                        .studentId(studentId)
                        .planId(lessonPlansDO.getId())
                        .build();
                studentLessonPlansDOMapper.insert(studentLessonPlansDO);

                Integer examId = (Integer) lessonPlanMap.get("examId");
                PlanExamDO planExamDO = PlanExamDO.builder()
                        .planId(lessonPlansDO.getId())
                        .examId(examId)
                        .build();
                planExamDOMapper.insert(planExamDO);

            } catch (Exception e) {
                log.info("生成第{}天计划失败，失败原因 {}", i + 1, e.getMessage());
            }
            log.info("生成第{}天计划成功", i + 1);
        }


        try {
            log.info("开始生成周末计划");
            int syncWeekendSize = totalWords.size() / 2;
            for (int i = 0; i < 2; i++) {
                List<VocabularyBankDO> checkList = totalWords.subList(i * syncWeekendSize, Math.min((i + 1) * syncWeekendSize, syncWeekendSize));
                Map<String, Object> map = generateWeekendPlans(checkList, i + 6, gradeDO, unitDO, studentId);

                LessonPlansDO lessonPlansDO = LessonPlansDO.builder()
                        .title(map.get("examStr").toString() + "复习")
                        .gradeId(gradeDO.getId().toString())
                        .unitId(unitDO.getId())
                        .createdAt(LocalDateTime.now())
                        .contentDetails(JsonUtils.toJsonString(map))
                        .build();
                lessonPlansDOMapper.insert(lessonPlansDO);

                Integer examId = (Integer) map.get("examId");
                PlanExamDO planExamDO = PlanExamDO.builder()
                        .planId(lessonPlansDO.getId())
                        .examId(examId)
                        .build();
                planExamDOMapper.insert(planExamDO);

                StudentLessonPlansDO studentLessonPlansDO = StudentLessonPlansDO.builder()
                        .studentId(studentId)
                        .planId(lessonPlansDO.getId())
                        .build();
                studentLessonPlansDOMapper.insert(studentLessonPlansDO);
                log.info("生成第{}天计划成功", i + 6);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public List<LessonPlansDO> findLessonPlans(List<Integer> ids) {
        return lessonPlansDOMapper.findLessonPlansByStudentId(ids);
    }

    @Override
    public LessonPlansDO findLessonPlanById(Integer id) {
        return lessonPlansDOMapper.selectByLessonId(id);
    }

    @Override
    public List<LessonPlansDO> findLessonPlansByStudentId(Integer studentId) {
        return lessonPlansDOMapper.selectByStudentId(studentId);
    }


    private Map<String, Object> generateWeekendPlans(List<VocabularyBankDO> words,
                                                        int day,
                                                        GradeDO gradeDO, UnitDO unitDO, Integer studentId) throws IOException {

        Map<String, Object> data = new HashMap<>();
        words.forEach(word -> word.setDefinition(word.getDefinition().length() > 5 ? word.getDefinition().substring(0, 5) : word.getDefinition()));
        List<Integer> wordIds = words.stream().map(VocabularyBankDO::getId).toList();
        StudentDO studentDO = studentDOMapper.selectStudentById(studentId);

        String ExamTitle = gradeDO.getTitle() + unitDO.getTitle() + "教案小测第" + ExamWordsConstant.day2Chinese(day) + "天" + studentDO.getName();
        ExamWordsDO examWordsDO = ExamWordsDO.builder()
                .gradeId(gradeDO.getId())
                .level(1)
                .wordIds(wordIds)
                .type(ExamWordsConstant.EXAM_TYPE_TEST)
                .title(ExamTitle)
                .createdAt(LocalDateTime.now())
                .build();
        examWordsDOMapper.insert(examWordsDO);
        studentExamWordsDOMapper.insertStudentsExam(studentId, examWordsDO.getId());

        ClassDO classDO = classDOMapper.selectClassDOById(studentDOMapper.selectStudentById(studentId).getClassId());
        data.put("examId", examWordsDO.getId());
        data.put("studentId", studentId);
        data.put("studentStr", gradeDO.getTitle() + " " + classDO.getTitle() + " " + studentDO.getName());
        data.put("examStr", ExamTitle);

        List<VocabularyBankDO> words1 = words.subList(0, words.size() / 2);
        List<VocabularyBankDO> words2 = words.subList(words.size() / 2, words.size());
        data.put("words1", words1);
        data.put("words2", words2);
//        LoopRowTableRenderPolicy policy = new LoopRowTableRenderPolicy();
//        Configure config = Configure.builder()
//                .bind("checkList", policy)
//                .build();
//
//        XWPFTemplate template = XWPFTemplate.compile(planWeekend, config);
//        template.render(data);
//        template.writeAndClose(new FileOutputStream("C:\\project\\java\\enlish_edu\\enlish\\enlish-service\\src\\main\\resources\\tmp\\word" + "复习" + day + ".docx"));

        return data;
    }

    private Map<String, Object> generateWeekdayPlans(List<VocabularyBankDO> syncVocabList,
                                        List<VocabularyBankDO> gapVocabList,
                                        List<VocabularyBankDO> reviewVocabList,
                                        List<VocabularyBankDO> checkList,
                                        int day,
                                        GradeDO gradeDO, UnitDO unitDO, Integer studentId) throws Exception {

        String title = gradeDO.getTitle() + " " + unitDO.getTitle() + " " + "第" + day + "天";
        Map<String, Object> data = new HashMap<>();
        data.put("title", title);
        data.put("syncVocabList", syncVocabList);
        data.put("gapVocabList", gapVocabList);
        data.put("reviewVocabList", reviewVocabList);
        data.put("checkList", checkList);
        data.put("checkListAns", checkList);
        // 中译英
        List<Word> list = syncVocabList.stream().map(vocabularyBankDO -> Word.builder().title(vocabularyBankDO.getWord()).definition(vocabularyBankDO.getDefinition()).build()).toList();
        list.forEach(word -> word.setTitle("                "));

        List<Word> drillRound1 = new ArrayList<>(list);
        Collections.shuffle(drillRound1);
        data.put("drillRound1", drillRound1);
        List<Word> drillRound2 = new ArrayList<>(list);
        Collections.shuffle(drillRound2);
        data.put("drillRound2", drillRound2);
        List<Word> drillRound3 = new ArrayList<>(list);
        Collections.shuffle(drillRound3);
        data.put("drillRound3", drillRound3);

        // 英译中
        List<VocabularyBankDO> mixedDrill = new ArrayList<>();
        mixedDrill.addAll(syncVocabList);
        mixedDrill.addAll(gapVocabList);
        mixedDrill.addAll(reviewVocabList);
        List<Word> mixedList = new ArrayList<>(mixedDrill.stream().map(vocabularyBankDO -> Word.builder().title(vocabularyBankDO.getWord()).definition(vocabularyBankDO.getDefinition()).build()).toList());
        mixedList.forEach(word -> word.setDefinition("                "));
        Collections.shuffle(mixedList);
        data.put("mixedDrill", mixedList);

        // 文章 A
        log.info("生成文章 A 中文开始");
        Map<String, String> mapA = getArticleStringMap(gradeDO, unitDO, studentId, syncVocabList);
        log.info("生成文章 A 成功 {}", mapA);
        data.put("articleATitle", mapA.get(LessonPlanConstant.TITLE));
        data.put("articleApassage", mapA.get(LessonPlanConstant.PASSAGE));
        data.put("articleAquiz", mapA.get(LessonPlanConstant.QUIZ));
        data.put("articleAans", mapA.get(LessonPlanConstant.ANSWER_KEY_EXPLANATION));
        data.put("articleAtran", mapA.get(LessonPlanConstant.FULL_TRANSLATION));

        // 文章 B
        log.info("生成文章 B 中文开始");
        Map<String, String> mapB;
        List<VocabularyBankDO> wordsArticleB = new ArrayList<>();
        wordsArticleB.addAll(syncVocabList);
        wordsArticleB.addAll(gapVocabList);
        wordsArticleB.addAll(reviewVocabList);
        mapB = getArticleStringMap(gradeDO, unitDO, studentId, wordsArticleB);
        log.info("生成文章 B 成功 {}", mapB);
        data.put("articleBTitle", mapB.get(LessonPlanConstant.TITLE));
        data.put("articleBpassage", mapB.get(LessonPlanConstant.PASSAGE));
        data.put("articleBquiz", mapB.get(LessonPlanConstant.QUIZ));
        data.put("articleBans", mapB.get(LessonPlanConstant.ANSWER_KEY_EXPLANATION));
        data.put("articleBtran", mapB.get(LessonPlanConstant.FULL_TRANSLATION));

        // 连词成句
        List<Sentence> sentences = difyClient.sendSentenceAnalyze(syncVocabList.subList(0, Math.max(10, syncVocabList.size())), gradeDO.getTitle());
        data.put("sentences", sentences);
        data.put("sentencesAns", sentences);
        log.info( "生成连词成句成功");


        // 教案小测
        List<VocabularyBankDO> words = new ArrayList<>(syncVocabList);
        words.addAll(gapVocabList);
        words.addAll(reviewVocabList);
        if (words.size() < 100) {
            words.addAll(vocabularyBankDOMapper.selectVocabularyBankListByGradeIdRandom(gradeDO.getId(), 100 - words.size()));
        } else {
            words = words.subList(0, 100);
        }
        words.forEach(word -> word.setDefinition(word.getDefinition().length() > 5 ? word.getDefinition().substring(0, 5) : word.getDefinition()));
        List<Integer> wordIds = words.stream().map(VocabularyBankDO::getId).toList();
        StudentDO studentDO = studentDOMapper.selectStudentById(studentId);

        String ExamTitle = gradeDO.getTitle() + unitDO.getTitle() + "教案小测 第" + ExamWordsConstant.day2Chinese(day) + "天" + studentDO.getName();
        ExamWordsDO examWordsDO = ExamWordsDO.builder()
                .gradeId(gradeDO.getId())
                .level(1)
                .wordIds(wordIds)
                .type(ExamWordsConstant.EXAM_TYPE_TEST)
                .title(ExamTitle)
                .createdAt(LocalDateTime.now())
                .build();
        examWordsDOMapper.insert(examWordsDO);
        studentExamWordsDOMapper.insertStudentsExam(studentId, examWordsDO.getId());

        data.put("examId", examWordsDO.getId());
        data.put("studentId", studentId);
        data.put("studentStr", studentDO.getName());
        data.put("examStr", ExamTitle);

        List<VocabularyBankDO> words1 = words.subList(0, wordIds.size() / 2);
        List<VocabularyBankDO> words2 = words.subList(wordIds.size() / 2, wordIds.size());
        data.put("words1", words1);
        data.put("words2", words2);

        log.info("生成教案小测成功");

//        LoopRowTableRenderPolicy policy = new LoopRowTableRenderPolicy();
//        Configure config = Configure.builder()
//                .bind("syncVocabList", policy)
//                .bind("gapVocabList", policy)
//                .bind("reviewVocabList", policy)
//                .bind("drillRound1", policy)
//                .bind("drillRound2", policy)
//                .bind("drillRound3", policy)
//                .bind("mixedDrill", policy)
//                .bind("checkList", policy)
//                .bind("checkListAns", policy)
//                .build();
//
//        XWPFTemplate template = XWPFTemplate.compile(planWeekday, config);
//        template.render(data);
//        template.writeAndClose(new FileOutputStream("C:\\project\\java\\enlish_edu\\enlish\\enlish-service\\src\\main\\resources\\tmp\\word" + title + ".docx"));

        return data;
    }

    @NonNull
    private Map<String, String> getArticleStringMap(GradeDO gradeDO, UnitDO unitDO, Integer studentId, List<VocabularyBankDO> words) throws Exception {
        Map<String, String> map;
        StringBuilder sb = new StringBuilder();
        words.forEach(word -> sb.append(word.getWord()).append(","));
        sb.deleteCharAt(sb.length() - 1);
        String string = sb.toString();
        int i = 0;
        do {
            log.info("第{}次生成文章中文开始", ++i);
            String answer = difyClient.sendChat(string, String.valueOf(studentId) + UUID.randomUUID(), null).getAnswer();
            map = StringToPlanMapUtil.parseTextToMap(answer);
        } while (map.get(LessonPlanConstant.TITLE) == null
                || map.get(LessonPlanConstant.PASSAGE) == null
                || map.get(LessonPlanConstant.QUIZ) == null
                || map.get(LessonPlanConstant.ANSWER_KEY_EXPLANATION) == null
                || map.get(LessonPlanConstant.FULL_TRANSLATION) == null
        );
        return map;
    }


    public List<VocabularyBankDO> getVocabListRandom(List<VocabularyBankDO> list, int count) {

        List<VocabularyBankDO> randomResultList;

        int listSize = list.size();
        if (listSize <= count) {
            randomResultList = new ArrayList<>(list);
        } else {
            List<VocabularyBankDO> tempList = new ArrayList<>(list);
            Collections.shuffle(tempList); // 随机打乱列表顺序
            randomResultList = new ArrayList<>(tempList.subList(0, count));

        }

        return randomResultList;
    }

}
