package com.saye.hospitalgd.scheduler.jobMethod;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.saye.hospitalgd.commons.date.DateDUtil;
import com.saye.hospitalgd.commons.getBean.GetBeanUtil;
import com.saye.hospitalgd.commons.log.LogUtil;
import com.saye.hospitalgd.commons.string.StringDUtil;
import com.saye.hospitalgd.commons.uuid.UUIDGenerator;
import com.saye.hospitalgd.service.HisDetailService;
import com.saye.hospitalgd.service.MedicalInsuranceReconciliationService;
import com.saye.hospitalgd.service.impl.HisDetailServiceImpl;
import com.saye.hospitalgd.service.impl.MedicalInsuranceReconciliationServiceImpl;
import com.saye.hospitalgd.service.system.DicinfoService;
import com.saye.hospitalgd.service.system.impl.DicinfoServiceImpl;
import com.saye.hospitalgd.util.SSLUtil;
import com.saye.hospitalgd.util.pojo.HttpResult;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * @author thuang
 * @version 1.0
 * @description: 医保对账方法
 * @date 2025/10/24
 */
public class MedicalInsuranceReconciliationMethod {

    // 普通医保局对账接口地址
    private static final String MEDICAL_INSURANCE_URL = "https://10.134.97.159/fsi/api/ybSettlementStmtService/stmtTotal";
    
    // 工商医保对账接口地址（险种99410专用）
    private static final String GONGSHANG_INSURANCE_URL = "http://10.150.4.74:8111/gssetl/api/hisbiz";
    
    // 工商医保险种代码
    private static final String GONGSHANG_INSUTYPE = "99410";
    
    // 固定参数
    private static final String SETL_OPTINS = "640104";
    private static final String INFNO = "3201";
    private static final String INFVER = "1.0";
    private static final String FIXMEDINS_CODE = "H64010400022";
    private static final String FIXMEDINS_NAME = "中国人民武装警察部队宁夏回族自治区总队医院";
    private static final String INSUPLC_ADMDVS = "410000";
    private static final String MDTRTAREA_ADMVS = "411302";
    private static final String OPTER = "ADMIN";
    private static final String OPTER_NAME = "系统管理员";
    private static final String OPTER_TYPE = "1";
    private static final String RECER_SYS_CODE = "HIS";
    private static final String SIGNTYPE = "SM3";

    /**
     * 执行医保对账
     * @param trade_date 对账日期
     * @return 对账结果
     */
    public HashMap<Object, Object> executeMedicalInsuranceReconciliation(String trade_date) {
        HashMap<Object, Object> responseMap = new HashMap<>();
        String errCode = "0";
        String errMsg = "";

        try {
            LogUtil.info(this.getClass(), "开始执行医保对账，日期：" + trade_date);

            LogUtil.info(this.getClass(), "开始查询医保数据（仅统计PayType为9和10的记录，包含账户支付和统筹支付，按HisTransId去重合并统计）");

            // 1. 从数据库查询医保数据，按险种和清算类别分组统计
            // 只统计PayType为'9'（账户支付）和'10'（统筹支付）的记录
            // 同一个HisTransId可能有多条记录（账户支付和统筹支付），需要合并统计金额
            // 通过HisTransId去重，确保每个交易只统计一次
            HisDetailService hisDetailService = GetBeanUtil.getBean(HisDetailServiceImpl.class);
            
            HashMap<Object, Object> queryMap = new HashMap<>();
            queryMap.put("trade_date", trade_date);
            
            // 查询医保数据（仅统计PayType为9和10的记录，按HisTransId去重合并统计）
            List<HashMap<Object, Object>> medicalRecords = hisDetailService.findMedicalInsuranceGroupData(queryMap);
            
            if (medicalRecords == null || medicalRecords.size() == 0) {
                LogUtil.info(this.getClass(), "医保对账：当日没有医保数据，日期：" + trade_date);
                errMsg = "当日没有医保数据";
                responseMap.put("errCode", errCode);
                responseMap.put("errMsg", errMsg);
                responseMap.put("reconciliationResults", new ArrayList<>());
                return responseMap;
            }

            LogUtil.info(this.getClass(), "查询到医保分组数据：" + medicalRecords.size() + " 组");

            // 先查询当日已存在的结果（用于判断是否为二次核对）
            MedicalInsuranceReconciliationService reconciliationService = 
                GetBeanUtil.getBean(MedicalInsuranceReconciliationServiceImpl.class);
            HashMap<Object, Object> prevQueryMap = new HashMap<>();
            prevQueryMap.put("trade_date", trade_date);
            List<HashMap<Object, Object>> prevResults = reconciliationService.findMedicalInsuranceReconciliationResult(prevQueryMap);
            HashMap<String, String> prevStatusMap = new HashMap<>();
            HashMap<String, String> prevRemarkMap = new HashMap<>();
            if (prevResults != null) {
                for (HashMap<Object, Object> prev : prevResults) {
                    String key = StringDUtil.changeNullToEmpty(prev.get("insutype")) + "|" + StringDUtil.changeNullToEmpty(prev.get("clr_type"));
                    prevStatusMap.put(key, StringDUtil.changeNullToEmpty(prev.get("stmt_rslt")));
                    prevRemarkMap.put(key, StringDUtil.changeNullToEmpty(prev.get("recheck_user")));
                }
            }

            // 删除当天旧的对账结果，再写入新结果
            HashMap<Object, Object> deleteMap = new HashMap<>();
            deleteMap.put("trade_date", trade_date);
            reconciliationService.deleteMedicalInsuranceReconciliationResult(deleteMap);

            // 2. 遍历每个险种和清算类别的组合，调用医保局接口
            List<HashMap<Object, Object>> reconciliationResults = new ArrayList<>();
            
            for (HashMap<Object, Object> record : medicalRecords) {
                String insutype = StringDUtil.changeNullToEmpty(record.get("INSUTYPE"));
                String clrType = StringDUtil.changeNullToEmpty(record.get("CLR_TYPE"));
                String medfee_sumamt = StringDUtil.changeNullToEmpty(record.get("MEDFEE_SUMAMT"));
                String fund_pay_sumamt = StringDUtil.changeNullToEmpty(record.get("FUND_PAY_SUMAMT"));
                String acct_pay = StringDUtil.changeNullToEmpty(record.get("ACCT_PAY"));
                String fixmedins_setl_cnt = StringDUtil.changeNullToEmpty(record.get("FIXMEDINS_SETL_CNT"));

                // 跳过空的险种或清算类别
                if ("".equals(insutype) || "".equals(clrType)) {
                    LogUtil.info(this.getClass(), "跳过险种或清算类别为空的记录");
                    continue;
                }

                LogUtil.info(this.getClass(), String.format("处理险种：%s，清算类别：%s，交易笔数：%s，总金额：%s", 
                    insutype, clrType, fixmedins_setl_cnt, medfee_sumamt));

                try {
                    // 3. 调用医保局对账接口
                    HashMap<Object, Object> callResult = callMedicalInsuranceAPI(
                        trade_date, insutype, clrType, medfee_sumamt, 
                        fund_pay_sumamt, acct_pay, fixmedins_setl_cnt
                    );

                    // 4. 记录对账结果
                    HashMap<Object, Object> resultMap = new HashMap<>();
                    resultMap.put("id", UUIDGenerator.getUUID());
                    resultMap.put("trade_date", trade_date);
                    resultMap.put("insutype", insutype);
                    resultMap.put("clr_type", clrType);
                    resultMap.put("medfee_sumamt", medfee_sumamt);
                    resultMap.put("fund_pay_sumamt", fund_pay_sumamt);
                    resultMap.put("acct_pay", acct_pay);
                    resultMap.put("fixmedins_setl_cnt", fixmedins_setl_cnt);
                    resultMap.put("setl_optins", callResult.get("setl_optins"));
                    String newStmtRslt = StringDUtil.changeNullToEmpty(callResult.get("stmt_rslt"));
                    resultMap.put("stmt_rslt", newStmtRslt);
                    resultMap.put("stmt_rslt_dscr", callResult.get("stmt_rslt_dscr"));
                    // 二次核对标志计算
                    String key = insutype + "|" + clrType;
                    String prevStmt = prevStatusMap.get(key);
                    String recheckFlag = ("1".equals(prevStmt) && "0".equals(newStmtRslt)) ? "1" : "0";
                    int checkRound = prevStatusMap.containsKey(key) ? 2 : 1;
                    resultMap.put("execute_type", "manual");
                    resultMap.put("check_round", checkRound);
                    resultMap.put("recheck_flag", recheckFlag);
                    resultMap.put("prev_stmt_rslt", prevStmt);
                    resultMap.put("recheck_time", "1".equals(recheckFlag) ? DateDUtil.getCurrentDate(DateDUtil.yyyy_MM_dd_HH_mm_ss) : null);
                    resultMap.put("recheck_user", prevRemarkMap.getOrDefault(key, ""));
                    resultMap.put("infcode", callResult.get("infcode"));
                    resultMap.put("err_msg", callResult.get("err_msg"));
                    resultMap.put("warn_msg", callResult.get("warn_msg"));
                    resultMap.put("refmsg_time", callResult.get("refmsg_time"));
                    resultMap.put("respond_time", callResult.get("respond_time"));
                    resultMap.put("inf_refmsgid", callResult.get("inf_refmsgid"));
                    resultMap.put("api_result", callResult.get("api_result"));
                    resultMap.put("create_time", DateDUtil.getCurrentDate(DateDUtil.yyyy_MM_dd_HH_mm_ss));
                    resultMap.put("modify_time", DateDUtil.getCurrentDate(DateDUtil.yyyy_MM_dd_HH_mm_ss));

                    reconciliationResults.add(resultMap);

                } catch (Exception e) {
                    LogUtil.error(this.getClass(), String.format("医保对账失败，险种：%s，清算类别：%s，错误：%s", 
                        insutype, clrType, e.getMessage()));
                    
                    // 记录失败结果
                    HashMap<Object, Object> resultMap = new HashMap<>();
                    resultMap.put("id", UUIDGenerator.getUUID());
                    resultMap.put("trade_date", trade_date);
                    resultMap.put("insutype", insutype);
                    resultMap.put("clr_type", clrType);
                    resultMap.put("medfee_sumamt", medfee_sumamt);
                    resultMap.put("fund_pay_sumamt", fund_pay_sumamt);
                    resultMap.put("acct_pay", acct_pay);
                    resultMap.put("fixmedins_setl_cnt", fixmedins_setl_cnt);
                    resultMap.put("stmt_rslt", "9");
                    resultMap.put("stmt_rslt_dscr", "调用医保局接口失败：" + e.getMessage());
                    resultMap.put("execute_type", "manual");
                    resultMap.put("check_round", prevStatusMap.size() > 0 ? 2 : 1);
                    resultMap.put("recheck_flag", "0");
                    resultMap.put("prev_stmt_rslt", "");
                    resultMap.put("recheck_time", null);
                    resultMap.put("recheck_user", prevRemarkMap.getOrDefault(insutype + "|" + clrType, ""));
                    resultMap.put("api_result", "ERROR");
                    resultMap.put("create_time", DateDUtil.getCurrentDate(DateDUtil.yyyy_MM_dd_HH_mm_ss));
                    resultMap.put("modify_time", DateDUtil.getCurrentDate(DateDUtil.yyyy_MM_dd_HH_mm_ss));

                    reconciliationResults.add(resultMap);
                }
            }

            // 5. 批量保存对账结果到数据库
            if (reconciliationResults.size() > 0) {
                reconciliationService.insertMedicalInsuranceReconciliationResultBatch(reconciliationResults);
            }

            responseMap.put("reconciliationResults", reconciliationResults);
            LogUtil.info(this.getClass(), "医保对账完成，共处理 " + reconciliationResults.size() + " 组数据");

        } catch (Exception e) {
            e.printStackTrace();
            errCode = "999";
            errMsg = "医保对账失败：" + e.getMessage();
            LogUtil.error(this.getClass(), errMsg);
        }

        responseMap.put("errCode", errCode);
        responseMap.put("errMsg", errMsg);
        return responseMap;
    }

    /**
     * 调用医保局对账接口
     */
    private HashMap<Object, Object> callMedicalInsuranceAPI(
            String trade_date, String insutype, String clr_type,
            String medfee_sumamt, String fund_pay_sumamt, String acct_pay, 
            String fixmedins_setl_cnt) throws Exception {

        HashMap<Object, Object> result = new HashMap<>();

        // 计算日期范围（当日到当日）
        String stmt_begndate = trade_date;
        String stmt_enddate = trade_date;

        // 生成当前时间
        String inf_time = DateDUtil.getCurrentDate(DateDUtil.yyyy_MM_dd_HH_mm_ss);
        
        // 生成消息ID（机构编码+时间戳+序号）
        String msgid = FIXMEDINS_CODE + DateDUtil.DateToStr("yyyyMMddHHmmss", new Date()) + "0001";

        // 构建请求JSON
        JSONObject requestJson = new JSONObject();
        requestJson.put("cainfo", "");
        requestJson.put("dev_no", "");
        requestJson.put("dev_safe_info", "");
        requestJson.put("fixmedins_code", FIXMEDINS_CODE);
        requestJson.put("fixmedins_name", FIXMEDINS_NAME);
        requestJson.put("inf_time", inf_time);
        requestJson.put("infno", INFNO);
        requestJson.put("infver", INFVER);
        requestJson.put("insuplc_admdvs", INSUPLC_ADMDVS);
        requestJson.put("mdtrtarea_admvs", MDTRTAREA_ADMVS);
        requestJson.put("msgid", msgid);
        requestJson.put("opter", OPTER);
        requestJson.put("opter_name", OPTER_NAME);
        requestJson.put("opter_type", OPTER_TYPE);
        requestJson.put("recer_sys_code", RECER_SYS_CODE);
        requestJson.put("sign_no", "");
        requestJson.put("signtype", SIGNTYPE);

        // 构建input.data
        JSONObject inputData = new JSONObject();
        inputData.put("acct_pay", acct_pay);
        inputData.put("clr_type", clr_type);
        inputData.put("fixmedins_setl_cnt", fixmedins_setl_cnt);
        inputData.put("fund_pay_sumamt", fund_pay_sumamt);
        inputData.put("insutype", insutype);
        inputData.put("medfee_sumamt", medfee_sumamt);
        inputData.put("setl_optins", SETL_OPTINS);
        inputData.put("stmt_begndate", stmt_begndate);
        inputData.put("stmt_enddate", stmt_enddate);

        JSONObject input = new JSONObject();
        input.put("data", inputData);
        requestJson.put("input", input);

        LogUtil.info(this.getClass(), "医保对账请求（日期范围：" + stmt_begndate + " 到 " + stmt_enddate + "）：" + requestJson.toJSONString());

        // 根据险种选择接口地址
        String apiUrl;
        boolean isGongshang = GONGSHANG_INSUTYPE.equals(insutype);
        if (isGongshang) {
            apiUrl = GONGSHANG_INSURANCE_URL;
            LogUtil.info(this.getClass(), "险种99410使用工商医保接口（当日到当日）：" + apiUrl);
        } else {
            apiUrl = MEDICAL_INSURANCE_URL;
            LogUtil.info(this.getClass(), "使用普通医保接口（当日到当日）：" + apiUrl);
        }

        // 发送HTTP请求（使用信任所有SSL证书的HttpClient）
        CloseableHttpClient httpClient = null;
        try {
            httpClient = SSLUtil.createSSLClientDefault();
        } catch (Exception e) {
            LogUtil.error(this.getClass(), "创建SSL HttpClient失败：" + e.getMessage());
            // 如果创建SSL HttpClient失败，使用默认的HttpClient
            httpClient = HttpClients.createDefault();
        }
        
        HttpPost httpPost = new HttpPost(apiUrl);
        httpPost.setHeader("Content-Type", "application/json;charset=UTF-8");
        httpPost.setEntity(new StringEntity(requestJson.toJSONString(), StandardCharsets.UTF_8));

        CloseableHttpResponse response = null;
        try {
            response = httpClient.execute(httpPost);
            String responseStr = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
            
            LogUtil.info(this.getClass(), "医保对账响应：" + responseStr);

            // 解析响应
            JSONObject responseJson = JSON.parseObject(responseStr);
            
            // 存储基础响应信息
            result.put("infcode", responseJson.getInteger("infcode"));
            result.put("err_msg", responseJson.getString("err_msg"));
            result.put("warn_msg", responseJson.getString("warn_msg"));
            result.put("refmsg_time", responseJson.getString("refmsg_time"));
            result.put("respond_time", responseJson.getString("respond_time"));
            result.put("inf_refmsgid", responseJson.getString("inf_refmsgid"));
            
            Integer infcode = responseJson.getInteger("infcode");
            if (infcode != null && infcode == 0) {
                // 请求成功
                JSONObject output = responseJson.getJSONObject("output");
                if (output != null && output.containsKey("stmtinfo")) {
                    JSONObject stmtinfo = output.getJSONObject("stmtinfo");
                    result.put("setl_optins", stmtinfo.getString("setl_optins"));
                    
                    String stmt_rslt = stmtinfo.getString("stmt_rslt");
                    String stmt_rslt_dscr = stmtinfo.getString("stmt_rslt_dscr");
                    
                    // 工商医保的对账结果处理（99410险种）
                    if (isGongshang) {
                        // 工商医保返回格式：
                        // stmt_rslt: "101" 表示有差异（对应普通医保的"1"失败）
                        // stmt_rslt: "0" 表示一致（对应普通医保的"0"成功）
                        if ("101".equals(stmt_rslt)) {
                            result.put("stmt_rslt", "1"); // 转换为标准的失败标识
                            LogUtil.info(this.getClass(), "工商医保对账结果：有差异 - " + stmt_rslt_dscr);
                        } else if ("0".equals(stmt_rslt)) {
                            result.put("stmt_rslt", "0"); // 成功
                            LogUtil.info(this.getClass(), "工商医保对账结果：一致");
                        } else {
                            result.put("stmt_rslt", stmt_rslt); // 保持原值
                            LogUtil.info(this.getClass(), "工商医保对账结果：" + stmt_rslt);
                        }
                    } else {
                        // 普通医保直接使用返回值
                        result.put("stmt_rslt", stmt_rslt);
                    }
                    
                    result.put("stmt_rslt_dscr", stmt_rslt_dscr);
                    result.put("api_result", "SUCCESS");
                } else {
                    result.put("stmt_rslt", "9");
                    result.put("stmt_rslt_dscr", "返回数据格式错误");
                    result.put("api_result", "ERROR");
                }
            } else {
                // 请求失败
                String err_msg = responseJson.getString("err_msg");
                result.put("stmt_rslt", "9");
                result.put("stmt_rslt_dscr", "医保局返回错误：" + err_msg);
                result.put("api_result", "ERROR");
            }

        } finally {
            if (response != null) {
                response.close();
            }
            httpClient.close();
        }

        return result;
    }
}
