package com.smppw.modaq.application.components.report.parser;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.smppw.modaq.application.components.ReportParseUtils;
import com.smppw.modaq.common.enums.ReportParseStatus;
import com.smppw.modaq.common.exception.ReportParseException;
import com.smppw.modaq.domain.dto.report.*;
import com.smppw.modaq.domain.entity.EmailFieldMappingDO;
import com.smppw.modaq.domain.mapper.EmailFieldMappingMapper;
import com.smppw.modaq.infrastructure.util.ConvertUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.Map;

/**
 * @author wangzaijun
 * @date 2024/9/30 18:13
 * @description 非python接口的报告解析抽象（主要是支持pdf、word和excel等格式）
 */
public abstract class AbstractReportParser<T extends ReportData> implements ReportParser<T> {
    protected final Logger logger = LoggerFactory.getLogger(this.getClass());

    private final EmailFieldMappingMapper fieldMappingMapper;
    /**
     * 字段匹配规则
     */
    protected Map<String, String> fieldMapper;

    public AbstractReportParser(EmailFieldMappingMapper fieldMappingMapper) {
        this.fieldMappingMapper = fieldMappingMapper;
        this.fieldMapper = MapUtil.newHashMap(128);
    }

    /**
     * 初始化数据的方法
     */
    protected void init() {
        List<EmailFieldMappingDO> mapping = this.fieldMappingMapper.getEmailFieldMapping(ListUtil.of(3, 4));
        if (CollUtil.isEmpty(mapping)) {
            throw new ReportParseException(ReportParseStatus.PARSE_RULE_NO_FUND);
        }
        for (EmailFieldMappingDO temp : mapping) {
            String code = temp.getCode();
            List<String> names = StrUtil.split(temp.getName(), ",");
            for (String name : names) {
                this.fieldMapper.putIfAbsent(name, code);
            }
        }
    }

    /**
     * 数据清洗，默认啥也不做
     *
     * @param reportData 结果数据
     */
    protected abstract void cleaningReportData(T reportData);

    /**
     * 解析报告的其他信息并设置到对象中
     *
     * @param reportInfo 报告基本信息
     * @param fundInfo   报告中基金基本信息
     * @return /
     */
    protected abstract T parseExtInfoAndSetData(ReportBaseInfoDTO reportInfo,
                                                ReportFundInfoDTO fundInfo) throws ReportParseException;

    /**
     * 绑定基金基本信息
     *
     * @param params /
     * @return /
     */
    protected abstract ReportFundInfoDTO buildFundInfo(ReportParserParams params);

    /**
     * 构建报告解析结果的所有数据
     *
     * @param params   请求参数
     * @param filename 文件名
     * @return /
     */
    protected T buildReportData(ReportParserParams params, String filename) {
        try {
            // 报告基本信息
            ReportBaseInfoDTO reportInfo = this.buildReportInfo(params);
            // 解析报告中主体基金的基本信息
            ReportFundInfoDTO reportFundInfo = this.buildFundInfo(params);
            if (reportFundInfo != null && StrUtil.isBlank(reportFundInfo.getFundCode())) {
                reportFundInfo.setFundCode(ReportParseUtils.matchFundCode(params.getFilename()));
            }
            // 解析其他表格信息并且设置结果字段
            T reportData = this.parseExtInfoAndSetData(reportInfo, reportFundInfo);
            // 数据清洗后返回
            this.cleaningReportData(reportData);
            return reportData;
        } catch (ReportParseException e) {
            throw e;
        } catch (Exception e) {
            this.logger.warn("报告{} 解析时有异常：{}", filename, ExceptionUtil.stacktraceToString(e));
            throw new ReportParseException(ReportParseStatus.NOT_A_FIXED_FORMAT, filename);
        }
    }

    /**
     * 构建只有两列表格的dto数据对象
     *
     * @param <DTO>   泛型对象
     * @param fileId  文件id
     * @param clazz   泛型对象
     * @param infoMap 表格转换的函数
     * @return /
     */
    protected <DTO extends BaseReportDTO<?>> DTO buildDto(Integer fileId,
                                                          Class<DTO> clazz,
                                                          Map<String, Object> infoMap) {
        try {
            DTO dto = clazz.getDeclaredConstructor().newInstance();
            dto.setFileId(fileId);
            this.buildInfo(infoMap, dto);
            return dto;
        } catch (Exception ignored) {
        }
        return null;
    }

    /**
     * 对象字段设置
     *
     * @param extInfoMap 名称与值的对应关系
     * @param info       待设置的对象
     */
    protected void buildInfo(Map<String, Object> extInfoMap, Object info) {
        if (MapUtil.isEmpty(extInfoMap)) {
            return;
        }
        for (Map.Entry<String, Object> entry : extInfoMap.entrySet()) {
            String k = ReportParseUtils.cleaningValue(entry.getKey());
            String fieldValue = ReportParseUtils.cleaningValue(entry.getValue(), false);
            String fieldName = this.fieldMapper.get(k);
            if (StrUtil.isBlank(fieldName)) {
                continue;
            }
            try {
                ReflectUtil.setFieldValue(info, fieldName, fieldValue);
            } catch (Exception e) {
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("{} 字段值设置错误：{}", fieldName, e.getMessage());
                }
            }
        }
    }

    /**
     * 构建报告基本信息
     *
     * @param params /
     * @return /
     */
    protected ReportBaseInfoDTO buildReportInfo(ReportParserParams params) {
        Integer fileId = params.getFileId();
        String reportName = params.getFilename();
        ReportBaseInfoDTO reportInfo = new ReportBaseInfoDTO(fileId);
        reportInfo.setReportName(reportName);
        reportInfo.setReportType(params.getReportType().name());
        String reportDate = ReportParseUtils.matchReportDate(params.getReportType(), reportName);
        reportInfo.setReportDate(ConvertUtil.toDate(reportDate));
        return reportInfo;
    }
}
