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

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import com.smppw.modaq.application.components.ReportParseUtils;
import com.smppw.modaq.application.components.report.parser.ReportParserConstant;
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.mapper.EmailFieldMappingMapper;
import org.springframework.stereotype.Component;
import technology.tabula.RectangularTextContainer;
import technology.tabula.Table;

import java.awt.geom.Rectangle2D;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;

/**
 * @author wangzaijun
 * @date 2024/9/29 17:53
 * @description pdf格式的季报解析逻辑
 */
@Component(ReportParserConstant.PARSER_PDF_QUARTERLY)
public class PDQuarterlyReportParser<T extends QuarterlyReportData> extends AbstractPDReportParser<T> {
    protected Table fundInfoTable;
    protected List<Table> financialIndicatorsTables;
    protected List<Table> shareChangeTables;
    protected List<Table> assetAllocationTables;
    protected List<Table> investmentIndustryTables;

    public PDQuarterlyReportParser(EmailFieldMappingMapper fieldMappingMapper) {
        super(fieldMappingMapper);
    }

    @Override
    public String getParser() {
        return ReportParserConstant.PARSER_PDF_QUARTERLY;
    }

    @Override
    protected void init() {
        super.init();
        this.fundInfoTable = null;
        this.financialIndicatorsTables = ListUtil.list(true);
        this.shareChangeTables = ListUtil.list(true);
        this.assetAllocationTables = ListUtil.list(true);
        this.investmentIndustryTables = ListUtil.list(true);
    }

    @Override
    protected void initTableInfo(List<Table> tables) {
        Map<Integer, List<Table>> spanningPageFinancialIndicatorsTableMap = MapUtil.newHashMap(8, true);
        Map<Integer, List<Table>> spanningPageShareChangeTableMap = MapUtil.newHashMap(8, true);
        int fi = 0;
        int sci = 0;
        for (Table table : tables) {
            int colCount = table.getColCount();
            int rowCount = table.getRowCount();
            if (colCount == 0 && rowCount == 0) {
                continue;
            }
            if (colCount == 2) {
                // 用表格的第一列的数据判断是否份额变动记录
                List<String> texts = this.getTableColTexts(table, 0);
                if (CollUtil.containsAny(texts, ReportParseUtils.FUND_INFO_COLUMN_NAMES)) {
                    this.fundInfoTable = table;
                    continue;
                }
                // 主要财务指标或份额变动
                if (CollUtil.containsAny(texts, ReportParseUtils.SHARE_CHANGE_COLUMN_NAMES)) {
                    sci = this.splitTables(table, 5, sci, this.shareChangeTables, spanningPageShareChangeTableMap);
                } else if (CollUtil.containsAny(texts, ReportParseUtils.FINANCIAL_INDICATORS_COLUMN_NAMES)) {
                    fi = this.splitTables(table, 10, fi, this.financialIndicatorsTables, spanningPageFinancialIndicatorsTableMap);
                }
            } else if (colCount == 4) {
                // 行业配置
                this.investmentIndustryTables.add(table);
            } else if (colCount == 3) {
                // 用表格的第一列单元格判断是否资产配置表
                List<String> texts = this.getTableColTexts(table, 0);
                if (CollUtil.containsAny(texts, ReportParseUtils.INDUSTRY_COLUMN_NAMES)) {
                    this.investmentIndustryTables.add(table);
                } else {
                    texts = this.getTableColTexts(table, 1);
                    Set<String> keys = ReportParseUtils.ASSET_ALLOCATION_TYPE_MAPPER.keySet();
                    if (CollUtil.containsAny(texts, keys)) {
                        this.assetAllocationTables.add(table);
                    }
                }
            }
        }
        // 跨页的财务信息记录表（包括表头一共有10行）
        this.handleSpanningPageTables(this.financialIndicatorsTables, spanningPageFinancialIndicatorsTableMap);
        // 跨页的份额变动记录表（包括表头一共有5行）
        this.handleSpanningPageTables(this.shareChangeTables, spanningPageShareChangeTableMap);
    }

    @Override
    protected ReportFundInfoDTO buildFundInfo(ReportParserParams params) {
        Table fundInfoTable = this.fundInfoTable;
        if (fundInfoTable == null) {
            throw new ReportParseException(ReportParseStatus.PARSE_FUND_INFO_FAIL, params.getFilename());
        }
        // 基金基本信息映射
        Map<String, Object> extInfoMap = this.parseFundInfo(fundInfoTable);
        return this.buildDto(params.getFileId(), ReportFundInfoDTO.class, extInfoMap);
    }

    protected Map<String, Object> parseFundInfo(Table fundInfoTable) {
        // 季报和年报的基金基本信息是两列的表格
        Map<String, Object> baseInfoMap = MapUtil.newHashMap(32);
        for (int i = 0; i < fundInfoTable.getRows().size(); i++) {
            @SuppressWarnings("all")
            List<RectangularTextContainer> cols = fundInfoTable.getRows().get(i);
            for (int j = 0; j < 1; j++) {
                String key = ReportParseUtils.cleaningValue(cols.get(j).getText());
                baseInfoMap.put(key, cols.get(j + 1).getText());
            }
        }
        return baseInfoMap;
    }

    protected T parseExtInfoAndSetData(ReportBaseInfoDTO reportInfo, ReportFundInfoDTO fundInfo) {
        Integer fileId = reportInfo.getFileId();
        // 表格转换数据获取函数
        Function<Table, Map<String, Object>> function = t -> {
            Map<String, Object> extInfoMap = MapUtil.newHashMap(16);
            for (int i = 0; i < t.getRowCount(); i++) {
                String key = ReportParseUtils.cleaningValue(t.getCell(i, 0).getText());
                String value = t.getCell(i, 1).getText();
                extInfoMap.put(key, value);
            }
            return extInfoMap;
        };
        // 份额变动
        List<ReportShareChangeDTO> shareChanges = this.buildLevelDto(fileId, this.shareChangeTables,
                ReportShareChangeDTO.class, function);
        // 主要财务指标
        List<ReportFinancialIndicatorsDTO> financialIndicators = this.buildFinancialIndicatorsInfo(fileId, function);
        // 资产配置
        List<ReportAssetAllocationDTO> assetAllocations = this.buildAssetAllocationInfo(fileId);
        // 行业配置
        List<ReportInvestmentIndustryDTO> investmentIndustries = this.buildInvestmentIndustryInfo(fileId);
        // 返回数据构建
        return this.buildReportData(reportInfo, fundInfo, shareChanges, financialIndicators, assetAllocations, investmentIndustries);
    }

    /**
     * 主要财务指标数据构建（包括分级基金，并且一个表格可能跨页）
     *
     * @param fileId   文件id
     * @param function 字段映射关系
     * @return /
     */
    protected List<ReportFinancialIndicatorsDTO> buildFinancialIndicatorsInfo(Integer fileId,
                                                                              Function<Table, Map<String, Object>> function) {
        return this.buildLevelDto(fileId, this.financialIndicatorsTables, ReportFinancialIndicatorsDTO.class, function);
    }

    /**
     * 子类重写，放在cast异常
     *
     * @param reportInfo           报告基本信息
     * @param fundInfo             基金基本信息
     * @param shareChanges         份额变动
     * @param financialIndicators  基本财务指标
     * @param assetAllocations     资产配置
     * @param investmentIndustries 行业配置
     * @return /
     */
    protected T buildReportData(ReportBaseInfoDTO reportInfo, ReportFundInfoDTO fundInfo,
                                List<ReportShareChangeDTO> shareChanges,
                                List<ReportFinancialIndicatorsDTO> financialIndicators,
                                List<ReportAssetAllocationDTO> assetAllocations,
                                List<ReportInvestmentIndustryDTO> investmentIndustries) {
        QuarterlyReportData reportData = new QuarterlyReportData(reportInfo, fundInfo);
        reportData.setShareChange(shareChanges);
        reportData.setFinancialIndicators(financialIndicators);
        reportData.setAssetAllocation(assetAllocations);
        reportData.setInvestmentIndustry(investmentIndustries);
        @SuppressWarnings("unchecked")
        T t = (T) reportData;
        return t;
    }

    /**
     * 构建基金行业配置解析数据
     *
     * @return /
     */
    private List<ReportInvestmentIndustryDTO> buildInvestmentIndustryInfo(Integer fileId) {
        List<ReportInvestmentIndustryDTO> dtos = ListUtil.list(false);
        for (Table table : this.investmentIndustryTables) {
            int colCount = table.getColCount();
            // 投资地区: 1-境内, 2-港股通
            int investType = colCount == 4 ? 1 : 2;
            int j = colCount == 4 ? 1 : 0;
            // 按行遍历
            for (int i = 0; i < table.getRowCount(); i++) {
                String text = ReportParseUtils.cleaningValue(table.getCell(i, 0).getText());
                if (StrUtil.containsAny(text, "序号", "行业类别")) {
                    continue;
                }
                String industryName = ReportParseUtils.cleaningValue(table.getCell(i, j).getText());
                if (StrUtil.isBlank(industryName) || !ReportParseUtils.INDUSTRY_COLUMN_NAMES.contains(industryName)) {
                    continue;
                }
                ReportInvestmentIndustryDTO dto = new ReportInvestmentIndustryDTO(fileId);
                dto.setInvestType(investType);
                dto.setIndustryName(industryName);
                dto.setMarketValue(ReportParseUtils.cleaningValue(table.getCell(i, j + 1).getText()));
                dto.setRatio(ReportParseUtils.cleaningValue(table.getCell(i, j + 2).getText()));
                dtos.add(dto);
            }
        }
        return dtos;
    }

    /**
     * 构建基金资产配置解析数据
     *
     * @param fileId 文件id
     * @return /
     */
    private List<ReportAssetAllocationDTO> buildAssetAllocationInfo(Integer fileId) {
        List<ReportAssetAllocationDTO> dtos = ListUtil.list(false);
        for (Table table : this.assetAllocationTables) {
            // 按行遍历
            for (@SuppressWarnings("all") List<RectangularTextContainer> row : table.getRows()) {
                // x坐标升序（防止部分行乱序问题）
                row.sort(Comparator.comparing(Rectangle2D.Float::getX));
                // 金额、市值，有时是 “备注#金额”的格式
                String marketValueAndRemark = ReportParseUtils.cleaningValue(row.get(2).getText(), false);
                // 资产明细
                String detail = ReportParseUtils.cleaningValue(row.get(1).getText(), false);
                if (!ReportParseUtils.ASSET_ALLOCATION_TYPE_MAPPER.containsKey(detail)) {
                    continue;
                }
                // 大类
                ReportParseUtils.buildAssetAllocation(fileId, detail, marketValueAndRemark, dtos);
            }
        }
        return dtos;
    }

    /**
     * 获取表格指定列的所有文字内容
     *
     * @param table 表格
     * @param col   指定列
     * @return /
     */
    protected List<String> getTableColTexts(Table table, Integer col) {
        List<String> details = ListUtil.list(false);
        for (@SuppressWarnings("all") List<RectangularTextContainer> row : table.getRows()) {
            String detail = ReportParseUtils.cleaningValue(row.get(col).getText(), false);
            if (StrUtil.isNotBlank(detail)) {
                details.add(detail);
            }
        }
        return details;
    }
}
