package jnpf.cultivate.controller.web.question;

import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.github.pagehelper.PageInfo;
import jnpf.base.ActionResult;
import jnpf.base.vo.DownloadVO;
import jnpf.base.vo.PageListVO;
import jnpf.cultivate.service.FtbCultivateQuestionBankService;
import jnpf.cultivate.service.FtbCultivateQuestionService;
import jnpf.cultivate.utils.QuestionExcelExportUtil;
import jnpf.model.cultivate.po.question.FtbCultivateQuestionBank;
import jnpf.model.cultivate.req.paper.PreQuestionImportReq;
import jnpf.model.cultivate.req.questionbank.AddQuestionReq;
import jnpf.model.cultivate.req.questionbank.EditQuestionReq;
import jnpf.model.cultivate.req.questionbank.QueryQuestionReq;
import jnpf.model.cultivate.req.questionbank.QuestionOptionReq;
import jnpf.model.cultivate.resp.*;
import jnpf.model.enums.CourseEnums;
import jnpf.model.personnels.req.employment.BatchByPrimaryIdReq;
import jnpf.util.FtbUtil;
import jnpf.util.RedisUtil;
import jnpf.util.excel.EasyExcelUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;

/**
 * web题目模块
 *
 * @author xxxxx
 */
@Slf4j
@RestController
@RequestMapping("/question")
public class FtbCultivateQuestionController {

    /**
     * 题目服务对象
     */
    @Autowired
    private FtbCultivateQuestionService questionService;
    @Autowired
    private FtbCultivateQuestionBankService questionBankService;

    @Autowired
    private RedisUtil redisUtil;

    /**
     * 分页列出题库中题目列表【根据题库ID】
     *
     * @param req
     * @return
     */
    @GetMapping("/pageLists")
    public ActionResult<PageListVO<QuestionVo>> pageLists(QueryQuestionReq req) {

        PageInfo<QuestionVo> pageVo = questionService.getPageList(req);
        return ActionResult.page(pageVo.getList(), FtbUtil.getPagination(pageVo));
    }


    /**
     * 分页列出重复题目
     *
     * @param req
     * @return
     */
    @GetMapping("/queryRepeatQuestion")
    public ActionResult<PageListVO<QuestionVo>> queryRepeatQuestion(@Valid QueryQuestionReq req) {
        PageInfo<QuestionVo> pageVo = questionService.queryRepeatQuestion(req);
        return ActionResult.page(pageVo.getList(), FtbUtil.getPagination(pageVo));
    }

    /**
     * 添加题目
     *
     * @param id          题库ID
     * @param addQuestion
     * @return
     */
    @PostMapping("/add/{id}")
//    @SaCheckPermission(QUESTION_PERMISSION_PRE + "btn_question_add")
    public ActionResult<Boolean> addQuestion(@PathVariable("id") String id, @RequestBody @Valid AddQuestionReq addQuestion) {
        QuestionExcelExportUtil.checkAddQuestionParam(addQuestion);
        questionService.insertData(id, addQuestion);
        return ActionResult.success(true);
    }

    /**
     * 添加题目【兼容没有创建题库导致正确的添加地址匹配错误，而没有给用户正确提示】兼容【/add/{id}】
     *
     * @param addQuestion
     * @return
     */
    @PostMapping("/add")
    public ActionResult<Boolean> addQuestionNoQuestionBank(@RequestBody @Valid AddQuestionReq addQuestion) {
        throw new RuntimeException("请先创建题库");
    }

    /**
     * 编辑题目
     *
     * @param editQuestion
     * @return
     */
    @PostMapping("/edit")
//    @SaCheckPermission(QUESTION_PERMISSION_PRE + "btn_question_edit")
    public ActionResult<Boolean> editQuestion(@RequestBody EditQuestionReq editQuestion) {
        QuestionExcelExportUtil.checkEditQuestionParam(editQuestion);
        questionService.updateData(editQuestion);
        return ActionResult.success(true);
    }


    /**
     * 查询题目详情，编辑时使用
     *
     * @param id 题目ID
     * @return
     */
    @GetMapping("/get/{id}")
//    @SaCheckPermission(QUESTION_PERMISSION_PRE + "btn_question_get")
    public ActionResult<QuestionVo> get(@PathVariable("id") String id) {
        return ActionResult.success(questionService.getInfo(id));
    }

    /**
     * 删除题目
     *
     * @param id 题目ID
     * @return
     */
    @DeleteMapping("/del/{id}")
//    @SaCheckPermission(QUESTION_PERMISSION_PRE + "btn_question_delete")
    public ActionResult<Boolean> del(@PathVariable("id") String id) {
        questionService.deleteData(id);
        return ActionResult.success(true);
    }

    /**
     * 批量删除题目
     *
     * @param req 题目ID
     * @return
     */
    @DeleteMapping("/batchDel")
    public ActionResult<Boolean> batchDel(@RequestBody @Validated BatchByPrimaryIdReq req) {
        if (req == null || CollectionUtil.isEmpty(req.getIds())) {
            throw new RuntimeException("请选择批量删除的题目");
        }
        questionService.batchDel(req.getIds());
        return ActionResult.success(true);
    }

    /**
     * 检测题目是否可以删除
     *
     * @param id 题目ID
     * @return
     */
    @GetMapping("/check-can-del/{id}")
    public ActionResult<QuestionCanDeleteMsg> checkCanDel(@PathVariable("id") String id) {
        return ActionResult.success(questionService.checkQuestionCanDelete(id));
    }

    /**
     * 批量检测题目是否可以删除
     *
     * @param req 题目ID
     * @return
     */
    @PostMapping("/batchCheckCanDel")
    public ActionResult<QuestionCanDeleteMsg> batchCheckCanDel(@RequestBody @Validated BatchByPrimaryIdReq req) {
        if (req == null || CollectionUtil.isEmpty(req.getIds())) {
            throw new RuntimeException("请选择批量删除的题目");
        }
        return ActionResult.success(questionService.batchCheckCanDel(req.getIds()));
    }


    /**
     * 模版下载
     *
     * @return
     */
    @GetMapping("/download/template")
    public ActionResult<DownloadVO> templateDownload() {
        DownloadVO vo = DownloadVO.builder().build();
        try {
            vo.setName("题目导入模板.xlsx");
            vo.setUrl("https://jnpf-resource-1304460613.cos.ap-chengdu.myqcloud.com/xgl/%E9%A2%98%E7%9B%AE%E6%A8%A1%E6%9D%BF.xlsx");
        } catch (Exception e) {
            log.error("下载模板错误:" + e.getMessage());
        }
        return ActionResult.success(vo);
    }


    /**
     * 统计字符串中相邻下划线视为一个时的下划线总数
     *
     * @param str
     * @return
     */
    public int countUnderscoresConsideringAdjacentAsOne(String str) {
        // 使用正则表达式替换连续的下划线为单个下划线
        String normalizedStr = str.replaceAll("_+", "_");
        // 遍历处理后的字符串，计算下划线数量
        int count = 0;
        for (int i = 0; i < normalizedStr.length(); i++) {
            if (normalizedStr.charAt(i) == '_') {
                count++;
            }
        }
        return count;
    }


    /**
     * 预检测题目导入接口
     *
     * @param preQuestionImportReq
     * @param questionBankId 题库ID
     * @return
     */
    @PostMapping("/preImportData/{questionBankId}")
    public ActionResult<PreQuestionImportVO> preImportData(@RequestBody @Validated PreQuestionImportReq preQuestionImportReq, @PathVariable(name = "questionBankId") String questionBankId) {
        String rediskey = QuestionExcelExportUtil.qeneralQuestionImportKey();
        redisUtil.remove(rediskey);
        ImportParams importParams = new ImportParams();
        importParams.setStartSheetIndex(1); // 读取第二个Sheet，因为索引是从0开始
        List<ExcelImportQuestionResultReq> allQuestionList = new ArrayList<>();
        try {
            InputStream inputStream = EasyExcelUtils.checkExcelFile(preQuestionImportReq.getFileUrl());
            List<Map<String, Object>> list = ExcelImportUtil.importExcel(inputStream, Map.class, importParams);
            log.info("题库导入数据：{}", JSON.toJSONString(list));
            inputStream.close();

            int line = 2;
            for (Map<String, Object> o : list) {
                ExcelImportQuestionResultReq addQuestionReq = new ExcelImportQuestionResultReq();
                CourseEnums.QuestionType type = CourseEnums.QuestionType.getByDesc(String.valueOf(o.get("题型")));
                CourseEnums.QuestionDifficulty difficulty = CourseEnums.QuestionDifficulty.getByDesc(String.valueOf(o.get("难度")));
                addQuestionReq.setContent(ObjectUtils.isNotEmpty(o.get("题目")) ? String.valueOf(o.get("题目")) : "");
                addQuestionReq.setAnalysis(ObjectUtils.isNotEmpty(o.get("解析")) ? String.valueOf(o.get("解析")) : "");
                if (null == difficulty || null == type || StringUtils.isEmpty(addQuestionReq.getContent())) {
                    addQuestionReq.setMsg("题目内容为空");
                    addQuestionReq.setSuccess(1);
                    continue;
                }
                addQuestionReq.setDifficulty(difficulty.getCode());
                addQuestionReq.setType(type.getCode());
                List<QuestionOptionReq> optionFormList = new ArrayList<>();

                Set<String> keySet = o.keySet();
                long sortCode = 0;
                for (String key : keySet) {
                    QuestionOptionReq optionForm = new QuestionOptionReq();
                    if (key.contains("选项")) {
                        String answer = String.valueOf(o.get("正确答案"));
                        String option = key.substring(2);
                        if (Objects.isNull(o.get(key))) {
                            continue;
                        }
                        optionForm.setContent(String.valueOf(o.get(key)));
                        if (!StringUtils.isEmpty(answer)) {
                            optionForm.setIsRightOption(answer.contains(option) ? 1 : 0);
                        }
                        optionForm.setSortCode(sortCode++);
                        optionFormList.add(optionForm);
                    }
                    if (CourseEnums.QuestionType.FILL.getCode().equals(addQuestionReq.getType())) {
                        optionForm.setRightAnswer(optionForm.getContent());
                    }
                }
                addQuestionReq.setOptionList(optionFormList);
                allQuestionList.add(addQuestionReq);
                line++;
            }

        } catch (Exception e) {
            e.printStackTrace();
            log.error("题目与导入失败");
            return ActionResult.fail("文件解析异常，导入失败!");
        }
        //
        if (CollectionUtil.isEmpty(allQuestionList)) {
            throw new RuntimeException("导入题列表不能为空");
        }
        QuestionExcelExportUtil.dealFillQuestion(allQuestionList);
        PreQuestionImportVO vo = new PreQuestionImportVO();
        vo.setUniqueId(rediskey);

        List<ExcelImportQuestionResultReq> normal = new ArrayList<>();
        List<ExcelImportQuestionResultReq> error = new ArrayList<>();
        for (ExcelImportQuestionResultReq item : allQuestionList) {
            if (item.getSuccess() == 1) {//fail
                error.add(item);
            } else {//success
                normal.add(item);
            }
        }
        vo.setNormal(normal);
        vo.setError(error);
        vo.setNormalNumber(normal.size());
        vo.setErrorNumber(error.size());
        redisUtil.insert(rediskey, vo, 60 * 60);
        return ActionResult.success("成功", vo);

    }


    /**
     * 真正导入题目到题库
     *
     * @param questionBankId 题库ID
     * @param key            预检测导入时返回的唯一标识(必传)，用于导出异常数据
     * @return
     */
    @PostMapping("/realImportData/{questionBankId}/{key}")
    public ActionResult<Object> realImportData(@PathVariable(name = "questionBankId") String questionBankId, @PathVariable(name = "key") String key) {
        if (!redisUtil.exists(key)) {
            throw new RuntimeException("导入数据已过期，请重新导入!");
        }
        PreQuestionImportVO data = JSON.parseObject(redisUtil.getString(key).toString(), PreQuestionImportVO.class);
        if(data==null || CollectionUtil.isEmpty(data.getNormal())){
            throw new RuntimeException("无正常题目可以导入");
        }
        ImportQuestionResultVo importQuestionResultVo = questionService.realImportData(questionBankId, data.getNormal());
        if(CollectionUtil.isEmpty(importQuestionResultVo.getFailList())){
            String rediskey = QuestionExcelExportUtil.qeneralQuestionImportKey();
            redisUtil.remove(rediskey);
        }
        return ActionResult.success("导入成功", importQuestionResultVo);
    }

    /**
     * 异常数据导出
     *
     * @param key 预检测导入时返回的唯一标识(必传)，用于导出异常数据
     */
    @GetMapping("/exceptionDataExport/{key}")
    public void exceptionDataExport(@PathVariable(name = "key") String key, HttpServletResponse response) throws IOException {
        if (!redisUtil.exists(key)) {
            throw new RuntimeException("导入数据已过期，请重新导入!");
        }
        PreQuestionImportVO data = JSON.parseObject(redisUtil.getString(key).toString(), PreQuestionImportVO.class);
        String fileName = "异常题目" + DateUtil.format(new Date(), "yyyyMMdd");
        if(data.getErrorNumber()<=0){
            throw new RuntimeException("无异常数据");
        }
        List<ExcelUserQuestionVo> list = QuestionExcelExportUtil.convertErrorQuestionList(data.getError());
        QuestionExcelExportUtil.questionExportExcel(response, fileName, list, ExcelUserQuestionVo.class);
    }


    /**
     * 根据题库导出题目
     *
     * @param questionBankId
     */

    @GetMapping("/questionExport/{questionBankId}")
    public void questionExport(@PathVariable(name = "questionBankId") String questionBankId, HttpServletResponse response) throws Exception {

        FtbCultivateQuestionBank questionBank = questionBankService.getById(questionBankId);
        if (null == questionBank) {
            throw new RuntimeException("题库不存在");
        }
        List<UserQuestionVo> userQuestionVoList = questionService.queryAllQuestionByQuestionBankId(questionBankId);
        if (CollUtil.isEmpty(userQuestionVoList)) {
            throw new Exception("暂无可导出题目");
        }
        String fileName = questionBank.getBankContent() + "_题目" + DateUtil.format(new Date(), "yyyyMMdd");
        List<ExcelUserQuestionVo> list = QuestionExcelExportUtil.convertQuestionList(userQuestionVoList);
        QuestionExcelExportUtil.questionExportExcel1(response, fileName, list, ExcelUserQuestionVo.class);
    }

    /**
     * 题目导入
     *
     * @param file
     * @param questionBankId 题库ID
     * @return
     */
    @PostMapping("/importData/{questionBankId}")
    public ActionResult<Object> importData(MultipartFile file, @PathVariable(name = "questionBankId") String questionBankId) {
        ImportParams importParams = new ImportParams();
        importParams.setStartSheetIndex(1); // 读取第二个Sheet，因为索引是从0开始
        List<AddQuestionReq> allQuestionList = new ArrayList<>();
        try {
            InputStream inputStream = file.getInputStream();
            List<Map<String, Object>> list = ExcelImportUtil.importExcel(inputStream, Map.class, importParams);
            log.info("题库导入数据：{}", JSON.toJSONString(list));
            inputStream.close();

            int line = 2;
            for (Map<String, Object> o : list) {
                AddQuestionReq addQuestionReq = new AddQuestionReq();
                CourseEnums.QuestionType type = CourseEnums.QuestionType.getByDesc(String.valueOf(o.get("题型")));
                CourseEnums.QuestionDifficulty difficulty = CourseEnums.QuestionDifficulty.getByDesc(String.valueOf(o.get("难度")));
                addQuestionReq.setContent(ObjectUtils.isNotEmpty(o.get("题目")) ? String.valueOf(o.get("题目")) : "");
                addQuestionReq.setAnalysis(ObjectUtils.isNotEmpty(o.get("解析")) ? String.valueOf(o.get("解析")) : "");
                if (null == difficulty || null == type || StringUtils.isEmpty(addQuestionReq.getContent())) {
                    return ActionResult.fail("导入失败！请检查第" + line + "行数据");
                }
                addQuestionReq.setDifficulty(difficulty.getCode());
                addQuestionReq.setType(type.getCode());
                List<QuestionOptionReq> optionFormList = new ArrayList<>();

                Set<String> keySet = o.keySet();
                long sortCode = 0;
                for (String key : keySet) {
                    QuestionOptionReq optionForm = new QuestionOptionReq();
                    if (key.contains("选项")) {
                        String answer = String.valueOf(o.get("正确答案"));
                        String option = key.substring(2);
                        if (Objects.isNull(o.get(key))) {
                            continue;
                        }
                        optionForm.setContent(String.valueOf(o.get(key)));
                        if (!StringUtils.isEmpty(answer)) {
                            optionForm.setIsRightOption(answer.contains(option) ? 1 : 0);
                        }
                        optionForm.setSortCode(sortCode++);
                        optionFormList.add(optionForm);
                    }
                    if (CourseEnums.QuestionType.FILL.getCode().equals(addQuestionReq.getType())) {
                        optionForm.setRightAnswer(optionForm.getContent());
                    }
                }
                addQuestionReq.setOptionList(optionFormList);
                allQuestionList.add(addQuestionReq);
                line++;
            }

        } catch (Exception e) {
            log.error("导入题目异常：{}", e);
            return ActionResult.fail("参数异常，导入失败！");
        }
        //
        if (CollectionUtil.isNotEmpty(allQuestionList)) {
            dealFillQuestion(allQuestionList);
            ImportQuestionResultVo importQuestionResultVo = questionService.importData(questionBankId, allQuestionList);
            log.info(importQuestionResultVo.getFailList().toString());
            return ActionResult.success("导入成功", importQuestionResultVo);
        }
        return ActionResult.fail("导入失败");
    }

    /**
     * 检测并填充加入的题目数据
     * @param allQuestionList 请求的题目数据
     */
    private void dealFillQuestion(List<AddQuestionReq> allQuestionList) {
        for (AddQuestionReq addQuestionReq : allQuestionList) {
            String content = addQuestionReq.getContent();
            if (StringUtils.isNotEmpty(content) && CourseEnums.QuestionType.FILL.getCode().equals(addQuestionReq.getType())) {
                int count = countUnderscoresConsideringAdjacentAsOne(content);
                if (addQuestionReq.getOptionList().size() > count) {
                    addQuestionReq.setOptionList(addQuestionReq.getOptionList().subList(0, count));
                } else if (addQuestionReq.getOptionList().size() < count) {
                    long size = addQuestionReq.getOptionList().size();
                    for (int i = 0; i < count - size; i++) {
                        QuestionOptionReq optionReq = new QuestionOptionReq();
                        optionReq.setContent("");
                        optionReq.setSortCode(size + i);
                        addQuestionReq.getOptionList().add(optionReq);
                    }
                }
            } else if (CourseEnums.QuestionType.MULTI.getCode().equals(addQuestionReq.getType())) {
                if (CollectionUtil.isEmpty(addQuestionReq.getOptionList())) {
                    throw new RuntimeException("题目:" + addQuestionReq.getContent() + ",多选题选项不能为空");
                }
                if (addQuestionReq.getOptionList().size() < 2) {
                    throw new RuntimeException("题目:" + addQuestionReq.getContent() + ",多选题选项不能少于2个");
                }
                int num = 0;
                for (QuestionOptionReq optionReq : addQuestionReq.getOptionList()) {
                    if (optionReq.getIsRightOption() != null && optionReq.getIsRightOption() == 1) {
                        num++;
                    }
                }
                if (num < 2) {
                    throw new RuntimeException("题目:" + addQuestionReq.getContent() + ",多选题正确答案不能小于2个");
                }

            } else if (CourseEnums.QuestionType.SINGLE.getCode().equals(addQuestionReq.getType())) {
                if (CollectionUtil.isEmpty(addQuestionReq.getOptionList())) {
                    throw new RuntimeException("题目:" + addQuestionReq.getContent() + ",单选题选项不能为空");
                }
                if (addQuestionReq.getOptionList().size() < 2) {
                    throw new RuntimeException("题目:" + addQuestionReq.getContent() + ",单选题选项不能少于2个");
                }
                int num = 0;
                for (QuestionOptionReq optionReq : addQuestionReq.getOptionList()) {
                    if (optionReq.getIsRightOption() != null && optionReq.getIsRightOption() == 1) {
                        num++;
                    }
                }
                if (num != 1) {
                    throw new RuntimeException("题目:" + addQuestionReq.getContent() + ",单选题正确答案应该只有1个");
                }

            }
        }
    }


}
