package com.simuwang.daq.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.simuwang.base.mapper.EmailFieldMappingMapper;
import com.simuwang.base.pojo.dto.report.*;
import com.simuwang.daq.components.report.parser.ReportParserConstant;
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.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 static final List<String> INDUSTRY_COLUMN_NAMES = ListUtil.list(false);
    protected static final List<String> SHARE_CHANGE_COLUMN_NAMES = ListUtil.list(false);

    static {
        // 中国证监会行业标准
        INDUSTRY_COLUMN_NAMES.add("农、林、牧、渔业");
        INDUSTRY_COLUMN_NAMES.add("采矿业");
        INDUSTRY_COLUMN_NAMES.add("制造业");
        INDUSTRY_COLUMN_NAMES.add("电力、热力、燃气及水生产和供应业");
        INDUSTRY_COLUMN_NAMES.add("建筑业");
        INDUSTRY_COLUMN_NAMES.add("批发和零售业");
        INDUSTRY_COLUMN_NAMES.add("交通运输、仓储和邮政业");
        INDUSTRY_COLUMN_NAMES.add("住宿和餐饮业");
        INDUSTRY_COLUMN_NAMES.add("信息传输、软件和信息技术服务业");
        INDUSTRY_COLUMN_NAMES.add("金融业");
        INDUSTRY_COLUMN_NAMES.add("房地产业");
        INDUSTRY_COLUMN_NAMES.add("租赁和商务服务业");
        INDUSTRY_COLUMN_NAMES.add("科学研究和技术服务业");
        INDUSTRY_COLUMN_NAMES.add("水利、环境和公共设施管理业");
        INDUSTRY_COLUMN_NAMES.add("居民服务、修理和其他服务业");
        INDUSTRY_COLUMN_NAMES.add("教育");
        INDUSTRY_COLUMN_NAMES.add("卫生和社会工作");
        INDUSTRY_COLUMN_NAMES.add("文化、体育和娱乐业");
        INDUSTRY_COLUMN_NAMES.add("综合");

        INDUSTRY_COLUMN_NAMES.add("港股通");

        // 以下为国际标准
        INDUSTRY_COLUMN_NAMES.add("能源");
        INDUSTRY_COLUMN_NAMES.add("原材料");
        INDUSTRY_COLUMN_NAMES.add("工业");
        INDUSTRY_COLUMN_NAMES.add("非日常生活消费品");
        INDUSTRY_COLUMN_NAMES.add("日常消费品");
        INDUSTRY_COLUMN_NAMES.add("医疗保健");
        INDUSTRY_COLUMN_NAMES.add("金融");
        INDUSTRY_COLUMN_NAMES.add("信息技术");
        INDUSTRY_COLUMN_NAMES.add("通讯服务");
        INDUSTRY_COLUMN_NAMES.add("公用事业");
        INDUSTRY_COLUMN_NAMES.add("房地产");

        // 份额变动表格识别列
        SHARE_CHANGE_COLUMN_NAMES.add("报告期期初基金份额总额");
        SHARE_CHANGE_COLUMN_NAMES.add("减:报告期期间基金总赎回份额");
        SHARE_CHANGE_COLUMN_NAMES.add("期末基金总份额/期末基金实缴总额");
        SHARE_CHANGE_COLUMN_NAMES.add("报告期期间基金拆分变动份额");
        SHARE_CHANGE_COLUMN_NAMES.add("报告期期间基金总申购份额");
    }

    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 initTableInfo(List<Table> tables) {
        this.financialIndicatorsTables = ListUtil.list(true);
        this.shareChangeTables = ListUtil.list(true);
        this.assetAllocationTables = ListUtil.list(true);
        this.investmentIndustryTables = ListUtil.list(true);
        for (Table table : tables) {
            int colCount = table.getColCount();
            int rowCount = table.getRowCount();
            if (colCount == 0 && rowCount == 0) {
                continue;
            }
            if (rowCount == 13 && colCount == 2) {
                this.fundInfoTable = table;
            } else if (colCount == 2) {
                // 用表格的第一列的数据判断是否份额变动记录
                List<String> texts = this.getTableColTexts(table, 0);
                // 主要财务指标或份额变动
                if (CollUtil.containsAny(texts, SHARE_CHANGE_COLUMN_NAMES)) {
                    this.shareChangeTables.add(table);
                } else {
                    this.financialIndicatorsTables.add(table);
                }
            } else if (colCount == 4) {
                // 行业配置
                this.investmentIndustryTables.add(table);
            } else if (colCount == 3) {
                // 用表格的第一列单元格判断是否资产配置表
                List<String> texts = this.getTableColTexts(table, 0);
                if (CollUtil.containsAny(texts, INDUSTRY_COLUMN_NAMES)) {
                    this.investmentIndustryTables.add(table);
                } else {
                    this.assetAllocationTables.add(table);
                }
            }
        }
    }

    @Override
    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++) {
                baseInfoMap.put(cols.get(j).getText(), cols.get(j + 1).getText());
            }
        }
        return baseInfoMap;
    }

    @Override
    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 = 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.buildLevelDto(fileId, this.financialIndicatorsTables,
                ReportFinancialIndicatorsDTO.class, function);
        // 资产配置
        List<ReportAssetAllocationDTO> assetAllocations = this.buildAssetAllocationInfo(fileId);
        // 行业配置
        List<ReportInvestmentIndustryDTO> investmentIndustries = this.buildInvestmentIndustryInfo(fileId);
        // 返回数据构建
        QuarterlyReportData reportData = new QuarterlyReportData(reportInfo, fundInfo);
        reportData.setShareChange(shareChanges);
        reportData.setFinancialIndicators(financialIndicators);
        reportData.setAssetAllocation(assetAllocations);
        reportData.setInvestmentIndustry(investmentIndustries);
        return this.buildExtData(reportInfo, fundInfo, shareChanges, assetAllocations, investmentIndustries, function);
    }

    protected T buildExtData(ReportBaseInfoDTO reportInfo, ReportFundInfoDTO fundInfo,
                             List<ReportShareChangeDTO> shareChanges,
                             List<ReportAssetAllocationDTO> assetAllocations,
                             List<ReportInvestmentIndustryDTO> investmentIndustries,
                             Function<Table, Map<String, Object>> function) {
        Integer fileId = reportInfo.getFileId();
        // 主要财务指标
        List<ReportFinancialIndicatorsDTO> financialIndicators = this.buildLevelDto(fileId, this.financialIndicatorsTables,
                ReportFinancialIndicatorsDTO.class, function);
        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;
    }

    @Override
    protected void cleaningReportData(T reportData) {
        // todo 数据清洗
    }

    /**
     * 构建基金行业配置解析数据
     *
     * @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 = this.cleaningValue(table.getCell(i, 0).getText());
                if (StrUtil.containsAny(text, "序号", "行业类别")) {
                    continue;
                }
                ReportInvestmentIndustryDTO dto = new ReportInvestmentIndustryDTO(fileId);
                dto.setInvestType(investType);
                dto.setIndustryName(this.cleaningValue(table.getCell(i, j).getText()));
                dto.setMarketValue(this.cleaningValue(table.getCell(i, j + 1).getText()));
                dto.setRatio(this.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);
        String assetType = null;
        for (Table table : this.assetAllocationTables) {
            // 按行遍历
            for (@SuppressWarnings("all") List<RectangularTextContainer> row : table.getRows()) {
                // x坐标升序（防止部分行乱序问题）
                row.sort(Comparator.comparing(Rectangle2D.Float::getX));
                // 大类
                String type = this.cleaningValue(row.get(0).getText());
                if (StrUtil.isNotBlank(type)) {
                    assetType = type;
                }
                // 金额、市值，有时是 “备注#金额”的格式
                String marketValueAndRemark = this.cleaningValue(row.get(2).getText());
                if (StrUtil.isBlank(marketValueAndRemark) || StrUtil.isBlank(assetType)) {
                    continue;
                }
                // 资产明细
                String detail = this.cleaningValue(row.get(1).getText(), false);
                if (StrUtil.contains(marketValueAndRemark, "#")) {
                    // 有#表示有备注，而且可能有多个，多个用分号分隔的.
                    List<String> marketValueAndRemarks = StrUtil.split(marketValueAndRemark, ";");
                    for (String mr : marketValueAndRemarks) {
                        if (StrUtil.isBlank(mr)) {
                            continue;
                        }
                        List<String> mrs = StrUtil.split(mr, "#");
                        ReportAssetAllocationDTO dto = new ReportAssetAllocationDTO(fileId);
                        dto.setAssetType(assetType);
                        dto.setAssetDetails(detail);
                        dto.setMarketValue(mrs.get(1));
                        dto.setRemark(mrs.get(0));
                        dtos.add(dto);
                    }
                } else {
                    ReportAssetAllocationDTO dto = new ReportAssetAllocationDTO(fileId);
                    dto.setAssetType(assetType);
                    dto.setAssetDetails(detail);
                    dto.setMarketValue(marketValueAndRemark);
                    dtos.add(dto);
                }
            }
        }
        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 = this.cleaningValue(row.get(col).getText(), false);
            if (StrUtil.isNotBlank(detail)) {
                details.add(detail);
            }
        }
        return details;
    }
}
