package com.yeejoin.amos.knowledgebase.face.util.excel;

import com.baomidou.mybatisplus.core.toolkit.Sequence;
import com.yeejoin.amos.knowledgebase.face.model.KnowledgeDynamicsOptionModel;
import com.yeejoin.amos.knowledgebase.face.model.KnowledgeTagInstanceModel;
import com.yeejoin.amos.knowledgebase.face.model.KnowledgeTagModel;
import com.yeejoin.amos.knowledgebase.face.model.KnowledgeTagValueModel;
import com.yeejoin.amos.knowledgebase.face.orm.entity.KnowledgeDocContent;
import com.yeejoin.amos.knowledgebase.face.orm.entity.KnowledgeDynamicsValue;
import com.yeejoin.amos.knowledgebase.face.orm.entity.KnowledgeTagInstance;
import com.yeejoin.amos.knowledgebase.face.orm.entity.KnowledgeTagValue;
import com.yeejoin.amos.knowledgebase.face.service.DocAuditService;
import com.yeejoin.amos.knowledgebase.face.service.DocContentService;
import com.yeejoin.amos.knowledgebase.face.service.TagInstanceService;
import com.yeejoin.amos.knowledgebase.face.service.TagValueService;
import com.yeejoin.amos.knowledgebase.face.util.DocSortUtil;
import lombok.Data;
import lombok.Getter;
import lombok.NonNull;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.typroject.tyboot.core.foundation.context.RequestContext;
import org.typroject.tyboot.core.foundation.exception.BaseException;
import org.typroject.tyboot.core.foundation.utils.Bean;
import org.typroject.tyboot.core.foundation.utils.DateUtil;
import org.typroject.tyboot.core.foundation.utils.ValidationUtil;
import org.typroject.tyboot.core.restful.exception.instance.BadRequest;

import java.text.DecimalFormat;
import java.util.*;
import java.util.regex.Pattern;

/**
 * excel格式的战例解析--定制化
 *
 * @author tiantao
 */
public class ExcelParser {
    private static final Pattern NUMBER_STRING_PATTEN = Pattern.compile("^[-\\+]?[\\d]*$");
    private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
    private static final String DATE_FORMAT4_HTML = "yyyy-MM-dd HH:mm";

    private static String getHolderRegex(Integer index) {
        return "#\\{knowledge-" + index + "\\}";
    }

    private static String getHolderAllRegex() {
        return "#\\{knowledge-\\d+\\}";
    }

    private static String removeAllHolder(String str) {
        return str.replaceAll(getHolderAllRegex(), "");
    }

    private static String replaceHolder(String str, Integer index, String text) {
        if (text == null) {
            text = "";
        }
        return str.replaceAll(getHolderRegex(index), text);
    }

    /**
     * 配置信息中涉及的标签列表以id为key
     */
    private final Map<Long, KnowledgeTagModel> tagMap;
    /**
     * 导入配置
     */
    private final Map<String, ExcelImportConfig>  excelConfigMap;

    /**
     * 文档动态字段数据类型Map
     */
    private final Map<String, KnowledgeDynamicsOptionModel> fieldTypeMap;
    /**
     * 标签的值类型
     */
    private final Map<Long, String> tagValueTypeMap;
    /**
     * 涉及的枚举信息，以分组为外层key，中文值为内层key，英文值为value
     */
    private final Map<String, Map<String, String>> enumCnEnMap;
    /**
     * 标签的单位
     */
    private final Map<Long, String> tagUnitMap;


    /**
     * 解析的数据及返回结果数据
     */
    private List<ImportResult> ImportResults = new ArrayList<>();
    private TablesDataList tablesDataList = new TablesDataList();

    private Sequence sequence;

    public List<ImportResult> getImportResults() {
        return ImportResults;
    }

    private Date now = new Date();
    private final String orgCode;

    public ExcelParser(@NonNull Map<Long, KnowledgeTagModel> tagMap, @NonNull Map<String, ExcelImportConfig> excelConfigMap,
                       Map<String, KnowledgeDynamicsOptionModel> fieldTypeMap, @NonNull Map<Long, String> tagValueTypeMap,
                       @NonNull Map<String, Map<String, String>> enumCnEnMap, @NonNull Map<Long, String> tagUnitMap,
                       @NonNull Sequence sequence, String orgCode) {
        this.tagMap = Collections.unmodifiableMap(tagMap);
        this.excelConfigMap = excelConfigMap;
        this.fieldTypeMap = Collections.unmodifiableMap(fieldTypeMap);
        this.tagValueTypeMap = Collections.unmodifiableMap(tagValueTypeMap);
        this.enumCnEnMap = Collections.unmodifiableMap(enumCnEnMap);
        this.tagUnitMap = Collections.unmodifiableMap(tagUnitMap);
        this.sequence = sequence;
        this.orgCode = orgCode;
    }

    /**
     * 解析表格行
     *
     * @param row 表格行对象
     */
    public void parseRow(Row row, Map<String, Long> typeValueMap) {
        try {
            String typeValue = convertCellValueToString(row.getCell(1));
            if (ValidationUtil.isEmpty(typeValue) || ValidationUtil.isEmpty(typeValueMap.get(typeValue))) {
                throw new BadRequest("缺少基本信息");
            }
            ExcelImportConfig excelConfig = this.excelConfigMap.get(String.valueOf(typeValueMap.get(typeValue)));
            String docTitle = convertCellValueToString(row.getCell(excelConfig.getTitleColumn()));
//            String docDirectory = convertCellValueToString(row.getCell(excelConfig.getDirectoryColumn()));
            String docDirectory = excelConfig.getDirectoryColumn();
            if (ValidationUtil.isEmpty(docTitle) || ValidationUtil.isEmpty(docDirectory)) {
                throw new BadRequest("缺少基本信息");
            }
            Long directoryId = Long.parseLong(docDirectory);
            // 创建文档
            KnowledgeDocContent docContent = getNewDoc(excelConfig);
            // 设置文档分类
            docContent.setDocTitle(docTitle);
            docContent.setDirectoryId(directoryId);
            // 创建动态字段
            List<KnowledgeDynamicsValue> dynamicsValues = getDynamicsValueList(excelConfig, row, docContent);
            // 创建标签实例
            List<KnowledgeTagInstance> tagInstances = new ArrayList<>();
            // 以及标签值实例
            List<KnowledgeTagValue> tagValues = new ArrayList<>();
            Map<Long, List<Integer>> tagColumns = excelConfig.getTagColumns();
            Map<Integer, String> formatMap = excelConfig.getFormatMap();
            for (Long tagSeq : tagColumns.keySet()) {
                if (!tagMap.containsKey(tagSeq) || !tagValueTypeMap.containsKey(tagSeq)) {
                    continue;
                }
                String valueType = tagValueTypeMap.get(tagSeq);
                KnowledgeTagInstance tagInstance = getTagInstance(tagMap.get(tagSeq), docContent.getSequenceNbr());
                switch (valueType) {
                    case "single": {
                        Integer valueColIndex = tagColumns.get(tagSeq).get(0);
                        String cellValue = convertCellValueToString(row.getCell(valueColIndex));
                        if (ValidationUtil.isEmpty(cellValue) || !NUMBER_STRING_PATTEN.matcher(cellValue).matches()) {
                            continue;
                        }
                        tagValues.add(getTagValue(tagInstance, TagValueService.VALUE_TAG_FIELD_SINGLEVALUE, cellValue));
                        replaceHtmlHolder(docContent, valueColIndex, cellValue, cellValue);
                        break;
                    }
                    case "date": {
                        Integer valueColIndex = tagColumns.get(tagSeq).get(0);
                        String cellValue = convertCellValueToString(row.getCell(valueColIndex));
                        if (ValidationUtil.isEmpty(cellValue) || !formatMap.containsKey(valueColIndex)) {
                            continue;
                        }
                        try {
                            Date date = DateUtil.formatStringToDate(cellValue, formatMap.get(valueColIndex));
                            cellValue = DateUtil.formatDate(date, DATE_FORMAT);
                            String htmlDateStr = DateUtil.formatDate(date, DATE_FORMAT4_HTML);
                            tagValues.add(getTagValue(tagInstance, TagValueService.VALUE_TAG_FIELD_DATE_H, cellValue));
                            replaceHtmlHolder(docContent, valueColIndex, htmlDateStr, cellValue);
                        } catch (Exception e) {
                            continue;
                        }
                        break;
                    }
                    case "text": {
                        Integer valueColIndex = tagColumns.get(tagSeq).get(0);
                        String cellValue = convertCellValueToString(row.getCell(valueColIndex));
                        if (ValidationUtil.isEmpty(cellValue)) {
                            continue;
                        }
                        tagValues.add(getTagValue(tagInstance, TagValueService.VALUE_TAG_FIELD_TEXT, cellValue));
                        replaceHtmlHolder(docContent, valueColIndex, cellValue, cellValue);
                        break;
                    }
                    case "range":
                        if (tagColumns.get(tagSeq).size() > 1) {
                            Integer valueColIndex1 = tagColumns.get(tagSeq).get(0);
                            String cellValue1 = convertCellValueToString(row.getCell(valueColIndex1));
                            Integer valueColIndex2 = tagColumns.get(tagSeq).get(1);
                            String cellValue2 = convertCellValueToString(row.getCell(valueColIndex2));
                            if (ValidationUtil.isEmpty(cellValue1) && ValidationUtil.isEmpty(cellValue2)
                                    || !NUMBER_STRING_PATTEN.matcher(cellValue1).matches() || !NUMBER_STRING_PATTEN.matcher(cellValue2).matches()) {
                                continue;
                            }
                            if (!ValidationUtil.isEmpty(cellValue1)) {
                                tagValues.add(getTagValue(tagInstance, TagValueService.VALUE_TAG_FIELD_RANGE_MIN, cellValue1));
                                replaceHtmlHolder(docContent, valueColIndex1, cellValue1, cellValue1);
                            }
                            if (!ValidationUtil.isEmpty(cellValue2)) {
                                tagValues.add(getTagValue(tagInstance, TagValueService.VALUE_TAG_FIELD_RANGE_MAX, cellValue2));
                                replaceHtmlHolder(docContent, valueColIndex2, cellValue2, cellValue2);
                            }
                        } else {
                            continue;
                        }
                        break;
                    default:
                        break;
                }
                tagInstances.add(tagInstance);
            }
            if(ValidationUtil.isEmpty(excelConfig.getTitleColumn())) {
                throw new BadRequest("缺少标题所在的索引信息");
            }

            replaceHtmlHolder(docContent, excelConfig.getTitleColumn(), docTitle, docTitle);

            docContent.setHtmlContent(removeAllHolder(docContent.getHtmlContent()));
            docContent.setTextContent(removeAllHolder(docContent.getTextContent()));

            String textContent = String.valueOf(docContent.getTextContent());
            docContent.setSummary(textContent.substring(0,Math.min(300, textContent.length())));
            List<KnowledgeTagInstanceModel> docTags = stuffTagValues(tagInstances, tagValues);
            docContent.setSortStr(DocSortUtil.getSortStr(docTags));
            this.tablesDataList.getDocContentList().add(docContent);
            this.tablesDataList.getDynamicsValueList().addAll(dynamicsValues);
            this.tablesDataList.getTagInstanceList().addAll(tagInstances);
            this.tablesDataList.getTagValueList().addAll(tagValues);
            this.ImportResults.add(new ImportResult(row.getRowNum()));
        } catch (BaseException e) {
            this.ImportResults.add(new ImportResult(row.getRowNum(), e.getMessage()));
        } catch (Exception e) {
            this.ImportResults.add(new ImportResult(row.getRowNum(), e.getStackTrace()[0].getMethodName()));
        }
    }

    private List<KnowledgeTagInstanceModel> stuffTagValues(List<KnowledgeTagInstance> tagInstances, List<KnowledgeTagValue> tagValues) {
        if (ValidationUtil.isEmpty(tagInstances)) {
            return Collections.emptyList();
        }
        ArrayList<KnowledgeTagInstanceModel> resList = Bean.toModels(tagInstances, KnowledgeTagInstanceModel.class);
        Map<Object, KnowledgeTagInstanceModel> instanceModelMap = Bean.listToMap(resList, "sequenceNbr", KnowledgeTagInstanceModel.class);
        if (!ValidationUtil.isEmpty(tagValues)) {
            tagValues.forEach(tagValue->{
                KnowledgeTagInstanceModel instanceModel = instanceModelMap.get(tagValue.getInstanceSeq());
                List<KnowledgeTagValueModel> tagValueModels = instanceModel.getTagValues();
                if (null == tagValueModels) {
                    tagValueModels = new ArrayList<>();
                    instanceModel.setTagValues(tagValueModels);
                }
                tagValueModels.add(Bean.toModel(tagValue, new KnowledgeTagValueModel()));
            });
        }
        return resList;
    }

    private void replaceHtmlHolder(KnowledgeDocContent docContent, Integer colIndex, String contentValue, String textValue) {
        docContent.setHtmlContent(replaceHolder(docContent.getHtmlContent(), colIndex, contentValue));
        docContent.setTextContent(replaceHolder(docContent.getTextContent(), colIndex, textValue));
    }

    /**
     * 获取解析到的数据列表，并刷新以备下一波解析
     *
     * @return 数据列表合集
     */
    public TablesDataList getAndFlushDatas() {
        TablesDataList res = this.tablesDataList;
        this.tablesDataList = new TablesDataList();
        return res;
    }

    /**
     * 创建一个新的文档
     *
     * @return
     */
    private KnowledgeDocContent getNewDoc(ExcelImportConfig excelConfig) {
        KnowledgeDocContent docContent = new KnowledgeDocContent();
        docContent.setSequenceNbr(sequence.nextId());
        docContent.setRecDate(now);
        docContent.setRecUserId(RequestContext.getExeUserId());
        docContent.setUserId(RequestContext.getExeUserId());
        docContent.setCreateTime(now);
        docContent.setAgencyCode(RequestContext.getAgencyCode());
        docContent.setOrgCode(this.orgCode);
        docContent.setHtmlContent(excelConfig.getHtmlModule());
        docContent.setTextContent(excelConfig.getSummaryModule());
        docContent.setDocStatus(DocContentService.DOC_STATUS_UNPUBLISHED);
        docContent.setAuditStatus(DocAuditService.DOC_AUDIT_STATUS_SAVED);
        docContent.setHaveAttachment(false);
        return docContent;
    }

    private List<KnowledgeDynamicsValue> getDynamicsValueList(ExcelImportConfig excelConfig, Row row, KnowledgeDocContent docContent) {
        List<KnowledgeDynamicsValue> dynamicsValues = new ArrayList<>();
        Map<String, Integer> basicColumns = excelConfig.getBasicColumns();
        Map<Integer, String> enumMap = excelConfig.getEnumMap();
        for (String fieldName : basicColumns.keySet()) {
            Integer colIndex = basicColumns.get(fieldName);
            Cell cell = row.getCell(colIndex);
            KnowledgeDynamicsOptionModel optionModel = fieldTypeMap.get(fieldName);
            if (null == optionModel) {
                throw new BadRequest("无法识别的字段名：" + fieldName);
            }
            String type = fieldTypeMap.get(fieldName).getDataType();
            String cellValue = convertCellValueToString(cell);
            if (type.toUpperCase().equals("ENUM")) {
                if (!enumMap.containsKey(colIndex)) {
                    throw new BadRequest("缺少枚举字段的配置信息：" + fieldName);
                }
                String enumName = enumMap.get(colIndex);
                cellValue = enumCnEnMap.get(enumName).get(cellValue);
            }
            dynamicsValues.add(getNewDynamicsField(optionModel, cellValue, docContent.getSequenceNbr()));
            // 替换模板占位字符
            replaceHtmlHolder(docContent, colIndex, cellValue, cellValue);
        }
        return dynamicsValues;
    }

    private KnowledgeDynamicsValue getNewDynamicsField(KnowledgeDynamicsOptionModel optionModel, String cellValue, Long docSequenceNbr) {
        KnowledgeDynamicsValue dynamicsValue = new KnowledgeDynamicsValue();
        dynamicsValue.setSequenceNbr(sequence.nextId());
        dynamicsValue.setRecDate(now);
        dynamicsValue.setRecUserId(RequestContext.getExeUserId());
        dynamicsValue.setFieldName(optionModel.getFieldName());
        dynamicsValue.setFieldValue(ValidationUtil.isEmpty(cellValue) ? "" : cellValue);
        dynamicsValue.setDataType(optionModel.getDataType());
        dynamicsValue.setAgencyCode(RequestContext.getAgencyCode());
        dynamicsValue.setFieldLabel(optionModel.getFieldLabel());
        dynamicsValue.setGroupSeq(optionModel.getGroupSeq());
        dynamicsValue.setQueryStrategy(optionModel.getQueryStrategy());
        dynamicsValue.setInstanceId(docSequenceNbr);
        dynamicsValue.setOptionSeq(optionModel.getSequenceNbr());
        return dynamicsValue;
    }

    KnowledgeTagInstance getTagInstance(KnowledgeTagModel tagModel, Long docSequenceNbr) {
        KnowledgeTagInstance tagInstance = new KnowledgeTagInstance();
        tagInstance.setSequenceNbr(sequence.nextId());
        tagInstance.setRecDate(now);
        tagInstance.setRecUserId(RequestContext.getExeUserId());
        tagInstance.setAgencyCode(RequestContext.getAgencyCode());
        tagInstance.setTagSeq(tagModel.getSequenceNbr());
        tagInstance.setTargetSeq(docSequenceNbr);
        tagInstance.setTagType(tagModel.getTagType());
        tagInstance.setTagName(tagModel.getTagName());
        tagInstance.setMarkingType(TagInstanceService.MARKING_TYPE_DOC);
        return tagInstance;
    }

    /**
     * 创建一个新的标签值实例
     *
     * @param tagInstance 实例对象
     * @param value       值
     * @return Model
     */
    private KnowledgeTagValue getTagValue(KnowledgeTagInstance tagInstance, String fieldName, String value) {
        KnowledgeTagValue tagValue = new KnowledgeTagValue();
        tagValue.setSequenceNbr(sequence.nextId());
        tagValue.setRecDate(now);
        tagValue.setRecUserId(RequestContext.getExeUserId());
        tagValue.setAgencyCode(RequestContext.getAgencyCode());
        tagValue.setInstanceSeq(tagInstance.getSequenceNbr());
        tagValue.setFieldName(fieldName);
        tagValue.setTagValue(value);
        tagValue.setUnit(tagUnitMap.get(tagInstance.getTagSeq()));
        return tagValue;
    }

    /**
     * 获取单元格的文本内容
     *
     * @param cell
     * @return
     */
    public static String convertCellValueToString(Cell cell) {
        if (cell == null) {
            return null;
        }
        String returnValue = null;
        switch (cell.getCellType()) {
            //数字
            case NUMERIC:
                Double doubleValue = cell.getNumericCellValue();
                // 格式化科学计数法，取一位整数
                DecimalFormat df = new DecimalFormat("0");
                returnValue = df.format(doubleValue);
                break;
            //字符串
            case STRING:
                returnValue = cell.getStringCellValue().trim();
                break;
            //布尔
            case BOOLEAN:
                Boolean booleanValue = cell.getBooleanCellValue();
                returnValue = booleanValue.toString();
                break;
            // 空值
            case BLANK:
                break;
            // 公式
            case FORMULA:
                returnValue = cell.getCellFormula();
                break;
            // 故障
            case ERROR:
                break;
            default:
        }
        return returnValue;
    }

    /**
     * 数据涉及对象列表缓存
     */
    @Getter
    public class TablesDataList {
        private List<KnowledgeDocContent> docContentList = new ArrayList<>();
        private List<KnowledgeTagInstance> tagInstanceList = new ArrayList<>();
        private List<KnowledgeTagValue> tagValueList = new ArrayList<>();
        private List<KnowledgeDynamicsValue> dynamicsValueList = new ArrayList<>();
    }

    @Data
    class ImportResult {
        // 行号
        private Integer rowIndex;
        // 是否导入成功
        private boolean success;
        // 错误信息
        private String errorMessege;

        ImportResult(Integer rowIndex, String errorMessege) {
            this.rowIndex = rowIndex;
            this.success = false;
            this.errorMessege = errorMessege;
        }

        ImportResult(Integer rowIndex) {
            this.rowIndex = rowIndex;
            this.success = true;
        }
    }
}
