package com.qmth.distributed.print.business.templete.execute;

import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.qmth.boot.api.exception.ApiException;
import com.qmth.distributed.print.business.bean.dto.ExaminationImportDto;
import com.qmth.distributed.print.business.bean.examRule.FieldsDto;
import com.qmth.distributed.print.business.service.ExamDetailService;
import com.qmth.distributed.print.business.service.ExamTaskService;
import com.qmth.distributed.print.business.templete.importData.AsyncImportTaskTemplete;
import com.qmth.distributed.print.business.templete.service.TaskLogicService;
import com.qmth.teachcloud.common.config.DictionaryConfig;
import com.qmth.teachcloud.common.contant.SpringContextHolder;
import com.qmth.teachcloud.common.contant.SystemConstant;
import com.qmth.teachcloud.common.entity.SysUser;
import com.qmth.teachcloud.common.entity.TBTask;
import com.qmth.teachcloud.common.enums.*;
import com.qmth.teachcloud.common.service.TBTaskService;
import com.qmth.teachcloud.common.util.FileStoreUtil;
import com.qmth.teachcloud.common.util.Result;
import com.qmth.teachcloud.common.util.ResultUtil;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.FillPatternType;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.MessageFormat;
import java.util.*;

/**
 * @Description: 考务数据导入模板
 * @Author: CaoZixuan
 * @Date: 2021-04-07
 */
@Service
public class AsyncExaminationImportTemplateService extends AsyncImportTaskTemplete {
    @Resource
    private ExamTaskService examTaskService;

    @Resource
    private ExamDetailService examDetailService;

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

    public static final String OBJ_TITLE = "考务数据";

    @Override
    public Result importTask(Map<String, Object> map) throws Exception {
        TBTask tbTask = (TBTask) map.get(SystemConstant.TASK);
        InputStream inputStream = super.getUploadFileInputStream(tbTask);
        map.put("inputStream", inputStream);

        StringJoiner stringJoinerSummary = new StringJoiner("\n")
                .add(MessageFormat.format("{0}{1}{2}", DateUtil.format(new Date(), SystemConstant.DEFAULT_DATE_PATTERN), BEGIN_TITLE, OBJ_TITLE));
        tbTask.setStatus(TaskStatusEnum.RUNNING);
        TBTaskService tbTaskService = SpringContextHolder.getBean(TBTaskService.class);
        tbTaskService.updateById(tbTask);
        List<Long> examDetailIdList = null;
        try {
            TaskLogicService taskLogicService = SpringContextHolder.getBean(TaskLogicService.class);

            // 执行导入考务数据
            Map<String, Object> result = taskLogicService.executeImportExaminationLogic(map);

            // 如果excel独立行数据校验有问题则生成excel
            if (result.containsKey(SystemConstant.ERROR_DATA_LIST)) {
                tbTask.setResult(TaskResultEnum.ERROR);
                List<ExaminationImportDto> examinationImportDtoList = JSON.parseArray(JSON.toJSONString(result.get(SystemConstant.ERROR_DATA_LIST)), ExaminationImportDto.class);
                List<FieldsDto> fieldsDtoList = JSON.parseArray(JSON.toJSONString(result.get("fieldsDtoList")), FieldsDto.class);

                if (CollectionUtils.isNotEmpty(examinationImportDtoList) && CollectionUtils.isNotEmpty(fieldsDtoList)) {
                    // 生成excel
                    this.createLocalErrorExcel(examinationImportDtoList, fieldsDtoList, tbTask);
                }

                // 错误信息抛出去
                List<String> errorList = (List<String>) result.get("errorList");
                if (!CollectionUtils.isEmpty(errorList)) {
                    throw ExceptionResultEnum.ERROR.exception(String.join("；\r\n", errorList));
                }
            } else {
                tbTask.setResult(TaskResultEnum.SUCCESS);
            }

            // 检测是否去生成pdf
            if (Objects.isNull(map.get("examDetailIdList"))) {
                throw ExceptionResultEnum.ERROR.exception("导入考务数据失败，数据未正确保存");
            }
            examDetailIdList = (List<Long>) map.get("examDetailIdList");
            // 按照考场检验命题任务是否全部完成，完成生成pdf
            SysUser user = (SysUser) map.get(SystemConstant.USER);

            examTaskService.checkDataByExamination(tbTask.getPrintPlanId(), examDetailIdList, user);

            stringJoinerSummary.add(MessageFormat.format("{0}{1}{2}{3}", DateUtil.format(new Date(), SystemConstant.DEFAULT_DATE_PATTERN), FINISH_TITLE, Long.valueOf(String.valueOf(result.get("dataCount"))), FINISH_ERROR_SIZE));
        } catch (Exception e) {
            log.error(SystemConstant.LOG_ERROR, e);
            stringJoinerSummary.add(MessageFormat.format("{0}{1}{2}{3}", DateUtil.format(new Date(), SystemConstant.DEFAULT_DATE_PATTERN), EXCEPTION_TITLE, EXCEPTION_DATA, e.getMessage()));
            tbTask.setResult(TaskResultEnum.ERROR);

            // 有异常，删除考务数据 （2023-07-21和产品商量后不再删除数据）
            examDetailService.deleteExaminationDataByExamDetailIds(examDetailIdList);

            if (e instanceof ApiException) {
                ResultUtil.error((ApiException) e, ((ApiException) e).getCode(), e.getMessage());
            } else {
                ResultUtil.error(e.getMessage());
            }

        } finally {//生成txt文件
            tbTask.setSummary(stringJoinerSummary.toString());
            tbTask.setStatus(TaskStatusEnum.FINISH);
            tbTaskService.updateById(tbTask);

        }
        return ResultUtil.ok(map);
    }

    /**
     * 生成本地的excel错误信息文件
     *
     * @param examinationImportDtoList 包含错误信息的考务数据
     * @param fieldsDtoList            属性字段集合
     */
    private void createLocalErrorExcel(List<ExaminationImportDto> examinationImportDtoList, List<FieldsDto> fieldsDtoList, TBTask tbTask) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        log.debug("导出Excel开始...");
        XSSFWorkbook wb = new XSSFWorkbook();
        XSSFSheet sheet = wb.createSheet("考务数据模板");

        Font defaultFont = wb.createFont();
        defaultFont.setFontHeightInPoints((short) 12);
        defaultFont.setFontName("宋体");


        int cellCount = fieldsDtoList.size() + 1;
        // 说明
        XSSFCellStyle describeStyle = wb.createCellStyle();
        describeStyle.setAlignment(HorizontalAlignment.LEFT);
        Font describeFont = wb.createFont();
        describeFont.setFontHeightInPoints((short) 12);
        describeFont.setFontName("宋体");
        describeFont.setColor(IndexedColors.RED.getIndex());
        describeStyle.setFont(describeFont);
        String describe = SystemConstant.EXAMINATION_DESCRIBE;
        describe = describe.trim();
        XSSFRow rowDescribe = sheet.createRow(0);
        rowDescribe.setHeightInPoints(195); //行高设置成195px
        for (int i = 0; i < cellCount; i++) {
            XSSFCell cell = rowDescribe.createCell(i);
            if (i == 0) {
                cell.setCellValue(describe);
                cell.setCellStyle(describeStyle);
            }
        }
        CellRangeAddress region = new CellRangeAddress(0, 0, 0, cellCount - 1);
        sheet.addMergedRegion(region);

        // 表头行
        XSSFCellStyle headerStyle = wb.createCellStyle();
        headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        headerStyle.setAlignment(HorizontalAlignment.CENTER);
        headerStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
        headerStyle.setFont(defaultFont);
        XSSFRow rowHead = sheet.createRow(1);
        for (int i = 0; i < fieldsDtoList.size(); i++) {
            XSSFCell cell = rowHead.createCell(i);
            cell.setCellValue(fieldsDtoList.get(i).getName());
            cell.setCellStyle(headerStyle);
        }
        XSSFCell lastCell = rowHead.createCell(cellCount - 1);
        lastCell.setCellValue("错误信息");
        lastCell.setCellStyle(headerStyle);


        // 内容行
        XSSFCellStyle exampleStyle = wb.createCellStyle();
        exampleStyle.setAlignment(HorizontalAlignment.LEFT);
        exampleStyle.setFont(defaultFont);
        List<Field> fields = Arrays.asList(ExaminationImportDto.class.getDeclaredFields());
        for (int i = 0; i < examinationImportDtoList.size(); i++) {
            ExaminationImportDto examinationImportDto = examinationImportDtoList.get(i);
            XSSFRow row = sheet.createRow(i + 2);
            for (int j = 0; j < fieldsDtoList.size(); j++) {
                XSSFCell cell = row.createCell(j);
                FieldsDto head = fieldsDtoList.get(j);
                String code = head.getCode();
                String value = "";

                if (fields.stream().anyMatch(e -> code.equals(e.getName()))) {
                    // 如果表头属性在数据中存在（则该属性属于基础字段）
                    String methodName = "get" + SystemConstant.initCap(code);
                    Method getMethod = examinationImportDto.getClass().getDeclaredMethod(methodName);
                    value = String.valueOf(getMethod.invoke(examinationImportDto));
                } else {
                    // 有可能是扩展字段或者无法匹配的字段 -> 为扩展字段列赋值
                    // 扩展字段
                    List<FieldsDto> extendsField = examinationImportDto.getSecondaryFieldList();
                    for (FieldsDto fieldsDto : extendsField) {
                        if (code.equals(fieldsDto.getCode())) {
                            value = fieldsDto.getValue();
                        }
                    }
                }
                cell.setCellValue(value);
                cell.setCellStyle(exampleStyle);
            }
            // 补充错误信息
            String errorMessage = examinationImportDto.getErrorMessage();
            if (SystemConstant.strNotNull(errorMessage)) {
                XSSFCell cell = row.createCell(cellCount - 1);
                cell.setCellValue(errorMessage);
                cell.setCellStyle(exampleStyle);
            }
        }
        for (int i = 0; i < cellCount; i++) {
            sheet.autoSizeColumn(i);
            int width = sheet.getColumnWidth(i)  * 17 / 10;
            sheet.setColumnWidth(i, Math.min(width, 255 * 256));
        }

        File excelFileTemp = null;
        try {
            FileStoreUtil fileStoreUtil = SpringContextHolder.getBean(FileStoreUtil.class);
            DictionaryConfig dictionaryConfig = SpringContextHolder.getBean(DictionaryConfig.class);
            boolean oss = dictionaryConfig.sysDomain().isOss();
            String ossStr = null;
            StringJoiner stringJoiner = new StringJoiner("");
            if (!oss && Objects.nonNull(dictionaryConfig.fssPublicDomain()) && !StringUtils.isBlank(dictionaryConfig.fssPublicDomain().getConfig()) && !dictionaryConfig.fssPublicDomain().getConfig().startsWith(SystemConstant.START_PARENT)) {
                stringJoiner.add(dictionaryConfig.fssPublicDomain().getConfig()).add(File.separator);
            }
            SystemConstant.getDirName(stringJoiner, UploadFileEnum.FILE, true);
            stringJoiner.add(SystemConstant.getNanoId()).add(SystemConstant.EXCEL_PREFIX);

            String txtDirName = stringJoiner.toString();
            excelFileTemp = SystemConstant.getFileTempVar(SystemConstant.EXCEL_PREFIX);
            FileOutputStream outputStream = new FileOutputStream(excelFileTemp);
            wb.write(outputStream);
            outputStream.flush();
            outputStream.close();
            log.debug("导出Excel结束...");

            String txtFileMd5 = DigestUtils.md5Hex(new FileInputStream(excelFileTemp));
            if (oss || dictionaryConfig.fssPrivateDomain().getConfig().startsWith(SystemConstant.START_PARENT)) {
                ossStr = oss ? SystemConstant.OSS : SystemConstant.LOCAL;
                fileStoreUtil.ossUpload(txtDirName, excelFileTemp, txtFileMd5, fileStoreUtil.getUploadEnumByPath(txtDirName).getFssType());
            } else {
                ossStr = SystemConstant.LOCAL;
                fileStoreUtil.localUpload(txtDirName, new FileInputStream(excelFileTemp), txtFileMd5, LocalCatalogEnum.LOCAL_FILE);
            }
            JSONObject json = new JSONObject();
            json.put(SystemConstant.PATH, stringJoiner.toString());
            json.put(SystemConstant.TYPE, ossStr);
            json.put(SystemConstant.UPLOAD_TYPE, UploadFileEnum.FILE);
            tbTask.setErrorFilePath(json.toJSONString());
        } catch (Exception e) {
            StringJoiner stringJoinerSummary = new StringJoiner("").add(tbTask.getSummary()).add("\n");
            stringJoinerSummary.add(MessageFormat.format("{0}{1}{2}{3}", DateUtil.format(new Date(), SystemConstant.DEFAULT_DATE_PATTERN), EXCEPTION_CREATE_TXT_TITLE, EXCEPTION_DATA, e.getMessage()));

            String summary = stringJoinerSummary.toString();
            tbTask.setSummary(summary);
            tbTask.setResult(TaskResultEnum.ERROR);
            if (e instanceof ApiException) {
                ResultUtil.error((ApiException) e, ((ApiException) e).getCode(), e.getMessage());
            } else {
                ResultUtil.error(e.getMessage());
            }
        } finally {
            if (Objects.nonNull(excelFileTemp)) {
                excelFileTemp.delete();
            }
            TBTaskService tbTaskService = SpringContextHolder.getBean(TBTaskService.class);
            tbTask.setStatus(TaskStatusEnum.FINISH);
            tbTaskService.updateById(tbTask);
        }
    }
}
