package com.smppw.modaq.application.components;

import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.smppw.modaq.common.enums.ReportMonthlyType;
import com.smppw.modaq.common.enums.ReportParseStatus;
import com.smppw.modaq.common.exception.ReportParseException;
import com.smppw.modaq.domain.dto.report.ocr.OCRLetterParseData;
import com.smppw.modaq.domain.dto.report.ocr.OCRParseData;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Map;
import java.util.Objects;

public class OCRReportParser {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    public OCRLetterParseData parseLetterData(String filename, String ocrApi, String ocrImgUrl) throws ReportParseException {
        Map<String, Object> paramsMap = MapUtil.newHashMap(4);
        paramsMap.put("image_url", ocrImgUrl);
        paramsMap.put("user_msg", """
                请提取文件中的基金名称、产品代码、投资人姓名、证件类型、证件号码、基金账户、交易账号、业务类型、申请日期、申请金额、申请份额、确认日期、确认金额、确认份额、单位净值。
                要求准确无误的提取上述关键信息、不要遗漏和捏造虚假信息。
                返回数据格式以json方式输出，格式为：{"基金名称":"","产品代码":"","投资人姓名":"","证件类型":"","证件号码":"","基金账户":"","交易账号":"","业务类型":"","申请日期":"","申请金额":"","申请份额":"","确认日期":"","确认金额":"","确认份额":"","单位净值":""}
                """);
        OCRLetterParseData res = new OCRLetterParseData();
        String objectStr = null;
        try {
            objectStr = this.parseOcrResult(ocrApi, paramsMap);
            JSONObject jsonObject = JSONUtil.parseObj(objectStr);
            String fundName = this.cleanData(jsonObject.getStr("基金名称"));
            String fundCode = this.cleanData(jsonObject.getStr("产品代码"));
            String investorName = this.cleanData(jsonObject.getStr("投资人姓名"));
            String certificateType = this.cleanData(jsonObject.getStr("证件类型"));
            String certificateNumber = this.cleanData(jsonObject.getStr("证件号码"));
            String fundAccount = this.cleanData(jsonObject.getStr("基金账户"));
            String tradingAccount = this.cleanData(jsonObject.getStr("交易账号"));
            String transactionType = this.cleanData(jsonObject.getStr("业务类型"));
            String applyDate = this.cleanData(jsonObject.getStr("申请日期"));
            String applyAmount = this.cleanData(jsonObject.getStr("申请金额"));
            String applyShare = this.cleanData(jsonObject.getStr("申请份额"));
            String holdingDate = this.cleanData(jsonObject.getStr("确认日期"));
            String amount = this.cleanData(jsonObject.getStr("确认金额"));
            String share = this.cleanData(jsonObject.getStr("确认份额"));
            String nav = this.cleanData(jsonObject.getStr("单位净值"));
            if (StrUtil.isNotBlank(fundName) && (fundName.contains("基金") || fundName.contains("资产管理")) && !fundName.contains("公司")) {
                res.setFundName(fundName);
            }
            if (StrUtil.isNotBlank(fundCode)) {
                res.setFundCode(ReportParseUtils.matchFundCode(fundCode));
            }
            if (StrUtil.isNotBlank(investorName)) {
                res.setInvestorName(investorName);
            }
            if (StrUtil.isNotBlank(certificateType)) {
                res.setCertificateType(certificateType);
            }
            if (StrUtil.isNotBlank(certificateNumber)) {
                res.setCertificateNumber(certificateNumber);
            }
            if (StrUtil.isNotBlank(fundAccount)) {
                res.setFundAccount(fundAccount);
            }
            if (StrUtil.isNotBlank(tradingAccount)) {
                res.setTradingAccount(tradingAccount);
            }
            if (StrUtil.isNotBlank(transactionType)) {
                res.setTransactionType(transactionType);
            }
            if (StrUtil.isNotBlank(applyDate)) {
                res.setApplyDate(applyDate);
            }
            if (StrUtil.isNotBlank(applyAmount)) {
                res.setApplyAmount(applyAmount);
            }
            if (StrUtil.isNotBlank(applyShare)) {
                res.setApplyShare(applyShare);
            }
            if (StrUtil.isNotBlank(holdingDate)) {
                res.setHoldingDate(holdingDate);
            }
            if (StrUtil.isNotBlank(amount)) {
                res.setAmount(amount);
            }
            if (StrUtil.isNotBlank(share)) {
                res.setShare(share);
            }
            if (StrUtil.isNotBlank(nav)) {
                res.setNav(nav);
            }
            return res;
        } catch (IORuntimeException e) {
            this.logger.warn("确认单{} OCR解析错误：{}", filename, ReportParseStatus.AI_NOT_FOUND.getMsg());
            throw new ReportParseException(ReportParseStatus.AI_NOT_FOUND);
        } catch (Exception e) {
            this.logger.warn("确认单{} OCR识别错误：{}", filename, ExceptionUtil.stacktraceToString(e));
            throw new ReportParseException(ReportParseStatus.SYSTEM_ERROR);
        } finally {
            if (logger.isInfoEnabled()) {
                this.logger.info("确认单{} OCR识别参数{}，OCR识别结果：{}，处理后的结果是：{}",
                        filename, paramsMap, objectStr, res);
            }
        }
    }

    public ReportMonthlyType parseMonthlyType(String filename, String ocrApi, String ocrImgUrl) throws ReportParseException {
        Map<String, Object> paramsMap = MapUtil.newHashMap(4);
        paramsMap.put("image_url", ocrImgUrl);
        paramsMap.put("user_msg", """
                请帮我判断报告的类型，判断依据是：如果有基金概况和净值月报则为协会版，如果有业绩曲线或者基金概况和净值月报都没有则为管理人版，都不满足是返回null。
                返回数据格式以json方式输出，格式为：{"报告类型":""}
                """);
        ReportMonthlyType res = ReportMonthlyType.FAILED;
        String objectStr = null;
        try {
            objectStr = this.parseOcrResult(ocrApi, paramsMap);
            JSONObject jsonObject = JSONUtil.parseObj(objectStr);
            String type = this.cleanData(jsonObject.getStr("报告类型"));
            if (StrUtil.isNotBlank(type) && Objects.equals("协会版", type)) {
                res = ReportMonthlyType.AMAC;
            } else if (StrUtil.isNotBlank(type) && Objects.equals("管理人版", type)) {
                res = ReportMonthlyType.MANAGER;
            }
            return res;
        } catch (IORuntimeException e) {
            this.logger.warn("报告{} 解析出错：{}", filename, ReportParseStatus.AI_NOT_FOUND.getMsg());
            throw new ReportParseException(ReportParseStatus.AI_NOT_FOUND);
        } catch (Exception e) {
            this.logger.warn("报告{} OCR提取月报类型错误：{}", filename, ExceptionUtil.stacktraceToString(e));
            throw new ReportParseException(ReportParseStatus.SYSTEM_ERROR);
        } finally {
            if (logger.isInfoEnabled()) {
                this.logger.info("报告{} OCR提取月报类型参数{}，OCR提取月报类型结果：{}，处理后的结果是：{}",
                        filename, paramsMap, objectStr, res);
            }
        }
    }

    public OCRParseData parse(String filename, String ocrApi, String ocrImgUrl) throws ReportParseException {
        Map<String, Object> paramsMap = MapUtil.newHashMap(4);
        paramsMap.put("image_url", ocrImgUrl);
        paramsMap.put("user_msg", """
                请提取文件中的基金名称、基金公司、产品代码，并判断是否有红色印章和是否有电话。
                要求准确无误的提取上述关键信息、不要遗漏和捏造虚假信息。
                返回数据格式以json方式输出，格式为：{"基金名称":"","基金公司":"","产品代码":"","是否有红色印章":"","是否有电话":""}
                """);
        OCRParseData res = new OCRParseData();
        String objectStr = null;
        try {
            objectStr = this.parseOcrResult(ocrApi, paramsMap);
            JSONObject jsonObject = JSONUtil.parseObj(objectStr);
            String fundName = this.cleanData(jsonObject.getStr("基金名称"));
            String fundCode = this.cleanData(jsonObject.getStr("产品代码"));
            String companyName = ReportParseUtils.cleaningValue(jsonObject.getStr("基金公司"));
            String seals = this.cleanData(jsonObject.getStr("是否有红色印章"));
            String phone = this.cleanData(jsonObject.getStr("是否有电话"));
            if (StrUtil.isNotBlank(fundName) && (fundName.contains("基金") || fundName.contains("资产管理")) && !fundName.contains("公司")) {
                res.setFundName(fundName);
            }
            if (StrUtil.isNotBlank(companyName) && companyName.contains("有限公司")) {
                res.setCompanyName(StrUtil.subBefore(companyName, "有限公司", true) + "有限公司");
            }
            if (StrUtil.isNotBlank(fundCode)) {
                res.setFundCode(ReportParseUtils.matchFundCode(fundCode));
            }
            if (StrUtil.isNotBlank(seals)) {
                res.setWithSeals(true);
            }
            if (StrUtil.isNotBlank(phone)) {
                res.setWithContacts(true);
            }
            return res;
        } catch (IORuntimeException e) {
            this.logger.warn("报告{} 解析错误：{}", filename, ReportParseStatus.AI_NOT_FOUND.getMsg());
            throw new ReportParseException(ReportParseStatus.AI_NOT_FOUND);
        } catch (Exception e) {
            this.logger.warn("报告{} OCR识别错误：{}", filename, ExceptionUtil.stacktraceToString(e));
            throw new ReportParseException(ReportParseStatus.SYSTEM_ERROR);
        } finally {
            if (logger.isInfoEnabled()) {
                this.logger.info("报告{} OCR识别参数{}，OCR识别结果：{}，处理后的结果是：{}",
                        filename, paramsMap, objectStr, res);
            }
        }
    }

    private String parseOcrResult(String ocrApi, Map<String, Object> paramsMap) {
        String body = HttpUtil.get(ocrApi, paramsMap);
        JSONObject jsonResult = JSONUtil.parseObj(body);
        String content = StrUtil.split(jsonResult.getStr("content"), "```").get(1);
        return "{" + StrUtil.subAfter(content, "{", false) + "}";
    }

    private String cleanData(String text) {
        if (text == null) {
            return null;
        }
        String trim = StrUtil.trim(text);
        if (StrUtil.isBlank(trim)) {
            return null;
        }
        if ("无".equals(trim) || "否".equals(trim)) {
            return null;
        }
        String value = ReportParseUtils.cleaningValue(trim);
        if (value == null) {
            return null;
        }
        // 识别到多个基金
        if (value.contains("、") || value.contains("，")) {
            return value.replaceAll("[、，]", ",");
        }
        return value;
    }
}
