package com.simuwang.daq.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import com.simuwang.base.common.conts.DateConst;
import com.simuwang.base.common.conts.EmailParseStatusConst;
import com.simuwang.base.common.conts.NavParseStatusConst;
import com.simuwang.base.common.conts.EmailTypeConst;
import com.simuwang.base.common.util.EmailUtil;
import com.simuwang.base.common.util.FileUtil;
import com.simuwang.base.config.EmailRuleConfig;
import com.simuwang.base.mapper.*;
import com.simuwang.base.pojo.dos.*;
import com.simuwang.base.pojo.dto.EmailContentInfoDTO;
import com.simuwang.base.pojo.dto.EmailFundNavDTO;
import com.simuwang.base.pojo.dto.MailboxInfoDTO;
import jakarta.mail.Folder;
import jakarta.mail.Message;
import jakarta.mail.Store;
import jakarta.mail.internet.MimeMultipart;
import jakarta.mail.search.ComparisonTerm;
import jakarta.mail.search.ReceivedDateTerm;
import jakarta.mail.search.SearchTerm;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.File;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author mozuwen
 * @date 2024-09-04
 * @description 邮件解析服务
 */
@Service
public class EmailParseService {

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

    @Value("${email.file.path}")
    private String path;

    private final EmailTypeRuleMapper emailTypeRuleMapper;
    private final EmailRuleConfig emailRuleConfig;
    private final EmailFieldMappingMapper emailFieldMapper;
    private final EmailParserFactory emailParserFactory;
    private final EmailParseInfoMapper emailParseInfoMapper;
    private final EmailFileInfoMapper emailFileInfoMapper;
    private final EmailFundNavMapper emailFundNavMapper;
    private final EmailFundAssetMapper emailFundAssetMapper;
    private final AssetMapper assetMapper;
    private final NavMapper navMapper;
    private final FundService fundService;
    private final FundAliasMapper fundAliasMapper;

    public EmailParseService(EmailTypeRuleMapper emailTypeRuleMapper, EmailRuleConfig emailRuleConfig,
                             EmailFieldMappingMapper emailFieldMapper, EmailParserFactory emailParserFactory,
                             EmailParseInfoMapper emailParseInfoMapper, EmailFileInfoMapper emailFileInfoMapper,
                             EmailFundNavMapper emailFundNavMapper, EmailFundAssetMapper emailFundAssetMapper,
                             AssetMapper assetMapper, NavMapper navMapper, FundService fundService,
                             FundAliasMapper fundAliasMapper) {
        this.emailTypeRuleMapper = emailTypeRuleMapper;
        this.emailRuleConfig = emailRuleConfig;
        this.emailFieldMapper = emailFieldMapper;
        this.emailParserFactory = emailParserFactory;
        this.emailParseInfoMapper = emailParseInfoMapper;
        this.emailFileInfoMapper = emailFileInfoMapper;
        this.emailFundNavMapper = emailFundNavMapper;
        this.emailFundAssetMapper = emailFundAssetMapper;
        this.assetMapper = assetMapper;
        this.navMapper = navMapper;
        this.fundService = fundService;
        this.fundAliasMapper = fundAliasMapper;
    }

    /**
     * 解析指定邮箱指定时间范围内的邮件
     *
     * @param mailboxInfoDTO 邮箱配置信息
     * @param startDate      邮件起始日期(yyyy-MM-dd HH:mm:ss)
     * @param endDate        邮件截止日期(yyyy-MM-dd HH:mm:ss, 为null,将解析邮件日期小于等于startDate的当天邮件)
     */
    public void parseEmail(MailboxInfoDTO mailboxInfoDTO, Date startDate, Date endDate) {
        // 邮件类型配置
        Map<Integer, List<String>> emailTypeMap = getEmailType();
        // 邮件字段识别映射表
        Map<String, List<String>> emailFieldMap = getEmailFieldMapping();
        Map<String, List<EmailContentInfoDTO>> emailContentMap;
        try {
            emailContentMap = realEmail(mailboxInfoDTO, emailTypeMap, startDate, endDate);
        } catch (Exception e) {
            log.info("采集邮件失败 -> 邮箱配置信息:{},堆栈信息:{}", mailboxInfoDTO, ExceptionUtil.stacktraceToString(e));
            return;
        }
        for (Map.Entry<String, List<EmailContentInfoDTO>> emailEntry : emailContentMap.entrySet()) {
            List<EmailContentInfoDTO> emailContentInfoDTOList = emailEntry.getValue();
            if (CollUtil.isEmpty(emailContentInfoDTOList)) {
                log.warn("未采集到正文或附件");
                continue;
            }
            log.info("开始解析邮件数据 -> 邮件主题:{},邮件日期:{}", emailContentInfoDTOList.get(0).getEmailTitle(), emailContentInfoDTOList.get(0).getEmailDate());
            List<EmailFundNavDTO> emailFundNavDTOList = CollUtil.newArrayList();
            Map<EmailContentInfoDTO, List<EmailFundNavDTO>> fileNameNavMap = MapUtil.newHashMap();
            for (EmailContentInfoDTO emailContentInfoDTO : emailContentInfoDTOList) {
                try {
                    List<EmailFundNavDTO> fundNavDTOList = parseEmail(emailContentInfoDTO, emailFieldMap);
                    fileNameNavMap.put(emailContentInfoDTO, fundNavDTOList);
                    emailFundNavDTOList.addAll(fundNavDTOList);
                } catch (Exception e) {
                    log.error("堆栈信息:{}", ExceptionUtil.stacktraceToString(e));
                }
            }
            // 保存相关信息 -> 邮件信息表,邮件文件表,邮件净值表,邮件规模表,基金净值表
            saveRelatedTable(mailboxInfoDTO.getAccount(), emailContentInfoDTOList, fileNameNavMap);
        }
    }

    public List<EmailFundNavDTO> parseEmail(EmailContentInfoDTO emailContentInfoDTO, Map<String, List<String>> emailFieldMap) {
        Integer emailType = emailContentInfoDTO.getEmailType();
        AbstractEmailParser emailParser = emailParserFactory.getInstance(emailType);
        return emailParser.parse(emailContentInfoDTO, emailFieldMap);
    }

    public void saveRelatedTable(String emailAddress, List<EmailContentInfoDTO> emailContentInfoDTOList, Map<EmailContentInfoDTO, List<EmailFundNavDTO>> fileNameNavMap) {
        String emailTitle = CollUtil.isNotEmpty(emailContentInfoDTOList) ? emailContentInfoDTOList.get(0).getEmailTitle() : null;
        String emailDate = CollUtil.isNotEmpty(emailContentInfoDTOList) ? emailContentInfoDTOList.get(0).getEmailDate() : null;
        Integer emailType = CollUtil.isNotEmpty(emailContentInfoDTOList) ? emailContentInfoDTOList.get(0).getEmailType() : null;
        Integer emailId = CollUtil.isNotEmpty(emailContentInfoDTOList) ? emailContentInfoDTOList.get(0).getEmailId() : null;
        Date parseDate = new Date();
        int emailParseStatus = EmailParseStatusConst.SUCCESS;
        EmailParseInfoDO emailParseInfoDO = buildEmailParseInfo(emailId, emailAddress, emailDate, emailTitle, emailType, emailParseStatus, parseDate);
        emailId = saveEmailParseInfo(emailParseInfoDO);

        for (Map.Entry<EmailContentInfoDTO, List<EmailFundNavDTO>> fileNameNavEntry : fileNameNavMap.entrySet()) {
            // 保存邮件文件表
            EmailContentInfoDTO emailContentInfoDTO = fileNameNavEntry.getKey();
            Integer fileId = saveEmailFileInfo(emailId, emailContentInfoDTO.getFielId(), emailContentInfoDTO.getFileName(), emailContentInfoDTO.getFilePath(), parseDate);

            List<EmailFundNavDTO> fundNavDTOList = fileNameNavEntry.getValue();
            if (CollUtil.isEmpty(fundNavDTOList)) {
                continue;
            }
            for (EmailFundNavDTO fundNavDTO : fundNavDTOList) {
                // 设置净值数据的解析状态
                setNavParseStatus(fundNavDTO, emailTitle);
            }
            // 保存净值表和规模表
            saveNavAndAssetNet(fileId, fundNavDTOList, parseDate);
        }

        // 更新邮件解析结果 -> 存在一条成功解析的净值数据就认为解析成功
        long successNavCount = fileNameNavMap.values().stream().flatMap(List::stream).filter(e -> e.getParseStatus().equals(NavParseStatusConst.SUCCESS)).count();
        emailParseStatus = successNavCount >= 1 ? EmailParseStatusConst.SUCCESS : EmailParseStatusConst.FAIL;
        emailParseInfoMapper.updateParseStatus(emailId, emailParseStatus);
    }

    private void saveNavAndAssetNet(Integer fileId, List<EmailFundNavDTO> fundNavDTOList, Date parseDate) {
        if (CollUtil.isEmpty(fundNavDTOList)) {
            return;
        }
        // 净值数据
        List<EmailFundNavDO> emailFundNavDOList = fundNavDTOList.stream()
                .map(e -> buildEmailFundNavDo(fileId, e, parseDate)).filter(CollUtil::isNotEmpty).flatMap(List::stream).collect(Collectors.toList());
        if (CollUtil.isNotEmpty(emailFundNavDOList)) {
            // 先删除文件id下的净值数据(考虑到重新解析的需求,如果是首次解析,那么fiel_id下不存在净值数据)
            emailFundNavMapper.deleteByFileId(fileId);
            emailFundNavMapper.batchInsert(emailFundNavDOList);
            List<NavDO> navDOList = emailFundNavDOList.stream().filter(e -> StrUtil.isNotBlank(e.getFundId()))
                    .map(e -> BeanUtil.copyProperties(e, NavDO.class)).collect(Collectors.toList());
            saveNavDo(navDOList);
        }
        // 保存规模数据
        List<EmailFundAssetDO> emailFundAssetDOList = fundNavDTOList.stream()
                .map(e -> buildEmailFundAssetDo(fileId, e, parseDate)).filter(CollUtil::isNotEmpty).flatMap(List::stream).collect(Collectors.toList());
        if (CollUtil.isNotEmpty(emailFundAssetDOList)) {
            // 先删除file_id下的规模数据(考虑到重新解析的需求,如果是首次解析,那么fiel_id下不存在规模数据)
            emailFundAssetMapper.deleteByFileId(fileId);
            emailFundAssetMapper.batchInsert(emailFundAssetDOList);
            List<AssetDO> assetDOList = emailFundAssetDOList.stream().filter(e -> StrUtil.isNotBlank(e.getFundId()))
                    .map(e -> BeanUtil.copyProperties(e, AssetDO.class)).collect(Collectors.toList());
            saveAssetDo(assetDOList);
        }
    }

    public void saveNavDo(List<NavDO> navDOList) {
        if (CollUtil.isEmpty(navDOList)) {
            return;
        }
        Map<String, List<NavDO>> fundIdNavMap = navDOList.stream().collect(Collectors.groupingBy(NavDO::getFundId));
        for (Map.Entry<String, List<NavDO>> entry : fundIdNavMap.entrySet()) {
            List<NavDO> navDOS = entry.getValue();
            List<String> priceDateList = navDOS.stream().map(NavDO::getPriceDate).map(e -> DateUtil.format(e, DateConst.YYYY_MM_DD)).collect(Collectors.toList());
            List<String> dateList = navMapper.queryFundNavByDate(entry.getKey(), priceDateList);
            List<NavDO> updateNavDoList = navDOS.stream().filter(e -> dateList.contains(DateUtil.format(e.getPriceDate(), DateConst.YYYY_MM_DD))).collect(Collectors.toList());
            List<NavDO> insertNavDoList = navDOS.stream().filter(e -> !dateList.contains(DateUtil.format(e.getPriceDate(), DateConst.YYYY_MM_DD))).collect(Collectors.toList());
            if (CollUtil.isNotEmpty(insertNavDoList)) {
                navMapper.batchInsert(insertNavDoList);
            }
            if (CollUtil.isNotEmpty(updateNavDoList)) {
                navMapper.batchUpdate(updateNavDoList);
            }
        }
    }

    public void saveAssetDo(List<AssetDO> assetDOList) {
        if (CollUtil.isEmpty(assetDOList)) {
            return;
        }
        Map<String, List<AssetDO>> fundIdNavMap = assetDOList.stream().collect(Collectors.groupingBy(AssetDO::getFundId));
        for (Map.Entry<String, List<AssetDO>> entry : fundIdNavMap.entrySet()) {
            List<AssetDO> assetDOS = entry.getValue();
            List<String> priceDateList = assetDOS.stream().map(AssetDO::getPriceDate).map(e -> DateUtil.format(e, DateConst.YYYY_MM_DD)).collect(Collectors.toList());
            List<String> dateList = assetMapper.queryFundNavByDate(entry.getKey(), priceDateList);
            List<AssetDO> updateAssetDoList = assetDOS.stream().filter(e -> dateList.contains(DateUtil.format(e.getPriceDate(), DateConst.YYYY_MM_DD))).collect(Collectors.toList());
            List<AssetDO> insertAssetDoList = assetDOS.stream().filter(e -> !dateList.contains(DateUtil.format(e.getPriceDate(), DateConst.YYYY_MM_DD))).collect(Collectors.toList());
            if (CollUtil.isNotEmpty(insertAssetDoList)) {
                assetMapper.batchInsert(insertAssetDoList);
            }
            if (CollUtil.isNotEmpty(updateAssetDoList)) {
                assetMapper.batchUpdate(updateAssetDoList);
            }
        }
    }

    private List<EmailFundAssetDO> buildEmailFundAssetDo(Integer fileId, EmailFundNavDTO fundNavDTO, Date parseDate) {
        List<EmailFundAssetDO> fundAssetDOList = CollUtil.newArrayList();
        BigDecimal assetNet = StrUtil.isNotBlank(fundNavDTO.getAssetNet()) ? new BigDecimal(fundNavDTO.getAssetNet()) : null;
        BigDecimal assetShare = StrUtil.isNotBlank(fundNavDTO.getAssetShare()) ? new BigDecimal(fundNavDTO.getAssetShare()) : null;
        if (assetNet == null) {
            return fundAssetDOList;
        }
        Integer isStored = fundNavDTO.getParseStatus() != null
                && (fundNavDTO.getParseStatus().equals(NavParseStatusConst.ASSET_NET_NEGATIVE) || fundNavDTO.getParseStatus().equals(NavParseStatusConst.SUCCESS)) ? 1 : 0;
        Date priceDate = DateUtil.parse(fundNavDTO.getPriceDate(), DateConst.YYYY_MM_DD);
        if (CollUtil.isNotEmpty(fundNavDTO.getFundIdList())) {
            for (String fundId : fundNavDTO.getFundIdList()) {
                EmailFundAssetDO emailFundAssetDO = new EmailFundAssetDO();
                emailFundAssetDO.setFileId(fileId);
                emailFundAssetDO.setPriceDate(priceDate);
                emailFundAssetDO.setFundId(fundId);
                emailFundAssetDO.setFundName(fundNavDTO.getFundName());
                emailFundAssetDO.setRegisterNumber(fundNavDTO.getRegisterNumber());
                emailFundAssetDO.setAssetNet(assetNet);
                emailFundAssetDO.setAssetShare(assetShare);
                emailFundAssetDO.setIsStored(isStored);
                emailFundAssetDO.setExceptionStatus(fundNavDTO.getParseStatus());
                emailFundAssetDO.setIsvalid(1);
                emailFundAssetDO.setCreatorId(0);
                emailFundAssetDO.setCreateTime(parseDate);
                emailFundAssetDO.setUpdaterId(0);
                emailFundAssetDO.setUpdateTime(parseDate);
                fundAssetDOList.add(emailFundAssetDO);
            }
        } else {
            EmailFundAssetDO emailFundAssetDO = new EmailFundAssetDO();
            emailFundAssetDO.setFileId(fileId);
            emailFundAssetDO.setPriceDate(priceDate);
            emailFundAssetDO.setFundName(fundNavDTO.getFundName());
            emailFundAssetDO.setRegisterNumber(fundNavDTO.getRegisterNumber());
            emailFundAssetDO.setAssetNet(assetNet);
            emailFundAssetDO.setAssetShare(assetShare);
            emailFundAssetDO.setIsStored(isStored);
            emailFundAssetDO.setExceptionStatus(fundNavDTO.getParseStatus());
            emailFundAssetDO.setIsvalid(1);
            emailFundAssetDO.setCreatorId(0);
            emailFundAssetDO.setCreateTime(parseDate);
            emailFundAssetDO.setUpdaterId(0);
            emailFundAssetDO.setUpdateTime(parseDate);
            fundAssetDOList.add(emailFundAssetDO);
        }
        return fundAssetDOList;
    }

    private List<EmailFundNavDO> buildEmailFundNavDo(Integer fileId, EmailFundNavDTO fundNavDTO, Date parseDate) {
        List<EmailFundNavDO> fundNavDOList = CollUtil.newArrayList();
        Date priceDate = DateUtil.parse(fundNavDTO.getPriceDate(), DateConst.YYYY_MM_DD);
        BigDecimal nav = StrUtil.isNotBlank(fundNavDTO.getNav()) ? new BigDecimal(fundNavDTO.getNav()) : null;
        BigDecimal cumulativeNavWithdrawal = StrUtil.isNotBlank(fundNavDTO.getCumulativeNavWithdrawal()) ? new BigDecimal(fundNavDTO.getCumulativeNavWithdrawal()) : null;
        if (nav == null && cumulativeNavWithdrawal == null) {
            return CollUtil.newArrayList();
        }
        Integer isStored = fundNavDTO.getParseStatus() != null && !fundNavDTO.getParseStatus().equals(NavParseStatusConst.NAV_DEFICIENCY)
                && !fundNavDTO.getParseStatus().equals(NavParseStatusConst.NOT_MATCH) ? 1 : 0;
        if (CollUtil.isNotEmpty(fundNavDTO.getFundIdList())) {
            for (String fundId : fundNavDTO.getFundIdList()) {
                EmailFundNavDO emailFundNavDO = new EmailFundNavDO();
                emailFundNavDO.setFileId(fileId);
                emailFundNavDO.setIsStored(isStored);
                emailFundNavDO.setPriceDate(priceDate);
                emailFundNavDO.setNav(nav);
                emailFundNavDO.setFundId(fundId);
                emailFundNavDO.setCumulativeNavWithdrawal(cumulativeNavWithdrawal);
                emailFundNavDO.setFundName(fundNavDTO.getFundName());
                emailFundNavDO.setRegisterNumber(fundNavDTO.getRegisterNumber());
                emailFundNavDO.setExceptionStatus(fundNavDTO.getParseStatus());
                emailFundNavDO.setIsvalid(1);
                emailFundNavDO.setCreatorId(0);
                emailFundNavDO.setCreateTime(parseDate);
                emailFundNavDO.setUpdaterId(0);
                emailFundNavDO.setUpdateTime(parseDate);
                fundNavDOList.add(emailFundNavDO);
            }
        } else {
            EmailFundNavDO emailFundNavDO = new EmailFundNavDO();
            emailFundNavDO.setFileId(fileId);
            emailFundNavDO.setPriceDate(priceDate);
            emailFundNavDO.setNav(nav);
            emailFundNavDO.setCumulativeNavWithdrawal(cumulativeNavWithdrawal);
            emailFundNavDO.setFundName(fundNavDTO.getFundName());
            emailFundNavDO.setRegisterNumber(fundNavDTO.getRegisterNumber());
            emailFundNavDO.setExceptionStatus(fundNavDTO.getParseStatus());
            emailFundNavDO.setIsStored(isStored);
            emailFundNavDO.setIsvalid(1);
            emailFundNavDO.setCreatorId(0);
            emailFundNavDO.setCreateTime(parseDate);
            emailFundNavDO.setUpdaterId(0);
            emailFundNavDO.setUpdateTime(parseDate);
            fundNavDOList.add(emailFundNavDO);
        }
        return fundNavDOList;
    }

    private Integer saveEmailFileInfo(Integer emailId, Integer fileId, String fileName, String filePath, Date parseDate) {
        EmailFileInfoDO emailFileInfoDO = buildEmailFileInfoDO(emailId, fileId, fileName, filePath, parseDate);
        if (emailFileInfoDO.getId() != null) {
            emailFileInfoMapper.updateTimeById(fileId, parseDate);
            return emailFileInfoDO.getId();
        }
        emailFileInfoMapper.insert(emailFileInfoDO);
        return emailFileInfoDO.getId();
    }

    private EmailFileInfoDO buildEmailFileInfoDO(Integer emailId, Integer fileId, String fileName, String filePath, Date parseDate) {
        EmailFileInfoDO emailFileInfoDO = new EmailFileInfoDO();
        emailFileInfoDO.setId(fileId);
        emailFileInfoDO.setEmailId(emailId);
        emailFileInfoDO.setFileName(fileName);
        emailFileInfoDO.setFilePath(filePath);
        emailFileInfoDO.setIsvalid(1);
        emailFileInfoDO.setCreatorId(0);
        emailFileInfoDO.setCreateTime(parseDate);
        emailFileInfoDO.setUpdaterId(0);
        emailFileInfoDO.setUpdateTime(parseDate);
        return emailFileInfoDO;
    }

    private void setNavParseStatus(EmailFundNavDTO fundNavDTO, String emailTitle) {
        // 1.单位净值或累计净值缺失
        if (StrUtil.isBlank(fundNavDTO.getNav()) || StrUtil.isBlank(fundNavDTO.getCumulativeNavWithdrawal())) {
            fundNavDTO.setParseStatus(NavParseStatusConst.NAV_DEFICIENCY);
            return;
        }
        // 2.匹配基金(考虑到解析估值表时已经匹配上基金的情况)
        if (CollUtil.isEmpty(fundNavDTO.getFundIdList())) {
            List<String> fundIdList = fundService.getFundIdByNamesAndCode(fundNavDTO.getFundName(), fundNavDTO.getRegisterNumber());
            if (CollUtil.isEmpty(fundIdList)) {
                // 判断是否写入别名管理表fund_alias
                saveFundAlias(fundNavDTO.getFundName(), fundNavDTO.getRegisterNumber());
                fundNavDTO.setParseStatus(NavParseStatusConst.NOT_MATCH);
                return;
            }
            fundNavDTO.setFundIdList(fundIdList);
        }
        // 考虑单独规模文件时 -> 无单位净值和累计净值
        // 3.单位净值或累计净值不大于0
        if (!emailTitle.contains("规模")) {
            if (StrUtil.isBlank(fundNavDTO.getNav()) || StrUtil.isBlank(fundNavDTO.getCumulativeNavWithdrawal())
                    || (fundNavDTO.getNav().compareTo("0") <= 0 || fundNavDTO.getCumulativeNavWithdrawal().compareTo("0") <= 0)) {
                fundNavDTO.setParseStatus(NavParseStatusConst.NAV_NEGATIVE);
                return;
            }
        }
        // 4.资产净值不大于0
        if (StrUtil.isNotBlank(fundNavDTO.getAssetNet()) && fundNavDTO.getAssetNet().compareTo("0") <= 0) {
            fundNavDTO.setParseStatus(NavParseStatusConst.ASSET_NET_NEGATIVE);
            return;
        }
        fundNavDTO.setParseStatus(NavParseStatusConst.SUCCESS);
    }

    private void saveFundAlias(String fundName, String registerNumber) {
        List<FundAliasDO> fundAliasDOList = fundAliasMapper.queryFundByNameAndRegisterNumber(fundName, registerNumber);
        if (CollUtil.isNotEmpty(fundAliasDOList)) {
            return;
        }
        log.info("写入别名表 -> 基金名称:{},备案编码:{}", fundName, registerNumber);
        fundAliasMapper.insert(fundName, registerNumber);
    }

    private Integer saveEmailParseInfo(EmailParseInfoDO emailParseInfoDO) {
        if (emailParseInfoDO == null) {
            return null;
        }
        // 重新邮件功能 -> 修改解析时间和更新时间
        if (emailParseInfoDO.getId() != null) {
            emailParseInfoMapper.updateParseTime(emailParseInfoDO.getId(), emailParseInfoDO.getParseDate());
            return emailParseInfoDO.getId();
        }
        emailParseInfoMapper.insert(emailParseInfoDO);
        return emailParseInfoDO.getId();
    }

    private EmailParseInfoDO buildEmailParseInfo(Integer emailId, String emailAddress, String emailDate,
                                                 String emailTitle, Integer emailType, Integer parseStatus, Date parseDate) {
        EmailParseInfoDO emailParseInfoDO = new EmailParseInfoDO();
        emailParseInfoDO.setId(emailId);
        emailParseInfoDO.setEmail(emailAddress);
        emailParseInfoDO.setEmailDate(DateUtil.parse(emailDate, DateConst.YYYY_MM_DD_HH_MM_SS));
        emailParseInfoDO.setParseDate(parseDate);
        emailParseInfoDO.setEmailTitle(emailTitle);
        emailParseInfoDO.setEmailType(emailType);
        emailParseInfoDO.setParseStatus(parseStatus);
        emailParseInfoDO.setIsvalid(1);
        emailParseInfoDO.setCreatorId(0);
        emailParseInfoDO.setCreateTime(parseDate);
        emailParseInfoDO.setUpdaterId(0);
        emailParseInfoDO.setUpdateTime(parseDate);
        return emailParseInfoDO;
    }

    public Map<String, List<String>> getEmailFieldMapping() {
        List<EmailFieldMappingDO> emailFieldMappingDOList = emailFieldMapper.getEmailFieldMapping();
        return emailFieldMappingDOList.stream()
                .collect(Collectors.toMap(EmailFieldMappingDO::getCode, v -> Arrays.stream(v.getName().split(",")).toList()));
    }

    public Map<Integer, List<String>> getEmailType() {
        Map<Integer, List<String>> emailTypeMap = MapUtil.newHashMap(3);
        EmailTypeRuleDO emailTypeRuleDO = emailTypeRuleMapper.getEmailTypeRule();
        String nav = emailTypeRuleDO != null && StrUtil.isNotBlank(emailTypeRuleDO.getNav()) ? emailTypeRuleDO.getNav() : emailRuleConfig.getNav();
        String valuation = emailTypeRuleDO != null && StrUtil.isNotBlank(emailTypeRuleDO.getValuation()) ? emailTypeRuleDO.getValuation() : emailRuleConfig.getValuation();
        String report = emailTypeRuleDO != null && StrUtil.isNotBlank(emailTypeRuleDO.getReport()) ? emailTypeRuleDO.getReport() : emailRuleConfig.getReport();
        emailTypeMap.put(EmailTypeConst.NAV_EMAIL_TYPE, Arrays.stream(nav.split(",")).toList());
        emailTypeMap.put(EmailTypeConst.VALUATION_EMAIL_TYPE, Arrays.stream(valuation.split(",")).toList());
        emailTypeMap.put(EmailTypeConst.REPORT_EMAIL_TYPE, Arrays.stream(report.split(",")).toList());
        return emailTypeMap;
    }

    /**
     * 读取邮件
     *
     * @param mailboxInfoDTO 邮箱配置信息
     * @param emailTypeMap   邮件类型识别规则映射表
     * @param startDate      邮件起始日期
     * @param endDate        邮件截止日期(为null,将解析邮件日期小于等于startDate的当天邮件)
     * @return 读取到的邮件信息
     * @throws Exception 异常信息
     */
    private Map<String, List<EmailContentInfoDTO>> realEmail(MailboxInfoDTO mailboxInfoDTO, Map<Integer, List<String>> emailTypeMap, Date startDate, Date endDate) throws Exception {
        Store store = EmailUtil.getStoreNew(mailboxInfoDTO);
        if (store == null) {
            return MapUtil.newHashMap();
        }
        // 默认读取收件箱的邮件
        Folder folder = store.getFolder("INBOX");
        folder.open(Folder.READ_ONLY);
        // 获取邮件日期大于等于startDate的邮件(搜索条件只支持按天)
        SearchTerm startDateTerm = new ReceivedDateTerm(ComparisonTerm.GE, startDate);
        Message[] messages = folder.search(startDateTerm);
        Map<String, List<EmailContentInfoDTO>> emailMessageMap = MapUtil.newHashMap();
        for (Message message : messages) {
            List<EmailContentInfoDTO> emailContentInfoDTOList = CollUtil.newArrayList();
            String uuidKey = UUID.randomUUID().toString().replaceAll("-", "");
            Integer emailType;
            try {
                Date emailDate = message.getSentDate();
                boolean isParseConditionSatisfied = emailDate == null || (endDate == null && emailDate.compareTo(startDate) > 0)
                        || emailDate.compareTo(startDate) < 0 || (endDate != null && emailDate.compareTo(endDate) > 0);
                if (isParseConditionSatisfied) {
                    continue;
                }
                emailType = EmailUtil.getEmailTypeBySubject(message.getSubject(), emailTypeMap);
                String emailDateStr = DateUtil.format(emailDate, DateConst.YYYY_MM_DD_HH_MM_SS);
                if (emailType == null) {
                    log.info("邮件不满足解析条件 -> 邮件主题:{},邮件日期:{}", message.getSubject(), emailDateStr);
                    continue;
                }
                log.info("邮件采集成功 -> 邮件主题:{},邮件日期:{}", message.getSubject(), emailDateStr);
                Object content = message.getContent();
                // 1.邮件为MIME多部分消息体:可能既有邮件又有正文
                if (content instanceof MimeMultipart) {
                    emailContentInfoDTOList = EmailUtil.collectMimeMultipart(message, mailboxInfoDTO.getAccount(), path);
                }
                // 2.邮件只有正文
                if (content instanceof String) {
                    EmailContentInfoDTO emailContentInfoDTO = new EmailContentInfoDTO();
                    emailContentInfoDTO.setEmailContent(content.toString());
                    emailContentInfoDTO.setEmailDate(emailDateStr);
                    String fileName = message.getSubject() + DateUtil.format(emailDate, DateConst.YYYYMMDDHHMMSS24);
                    String filePath = path + mailboxInfoDTO.getAccount() + "/" + DateUtil.format(emailDate, DateConst.YYYY_MM_DD) + "/" + fileName + ".html";
                    File savefile = new File(filePath);
                    savefile.setReadable(true);
                    if (!savefile.exists()) {
                        if (!savefile.getParentFile().exists()) {
                            savefile.getParentFile().mkdirs();
                            savefile.getParentFile().setExecutable(true);
                        }
                    }
                    FileUtil.writeFile(filePath, content.toString());
                    emailContentInfoDTO.setFilePath(filePath);
                    emailContentInfoDTOList.add(emailContentInfoDTO);
                }
                if (CollUtil.isNotEmpty(emailContentInfoDTOList)) {
                    emailContentInfoDTOList.forEach(e -> e.setEmailType(emailType));
                    emailMessageMap.put(uuidKey, emailContentInfoDTOList);
                }
            } catch (Exception e) {
                log.error("获取邮箱的邮件报错,堆栈信息:{}", ExceptionUtil.stacktraceToString(e));
            }
        }
        folder.close(false);
        store.close();
        return emailMessageMap;
    }

}
