package nc.bs.mmpac.pmo.pac0002.bp.rule;


import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import nc.bs.dao.BaseDAO;
import nc.bs.framework.common.NCLocator;
import nc.bs.logging.Log;
import nc.impl.pubapp.pattern.rule.IRule;
import nc.jdbc.framework.SQLParameter;
import nc.jdbc.framework.processor.ColumnProcessor;
import nc.vo.bc.pmpub.project.ProjectHeadVO;
import nc.vo.bd.bom.bom0202.entity.BomVO;
import nc.vo.bd.material.MaterialVO;
import nc.vo.bd.material.MaterialVersionVO;
import nc.vo.cmp.util.StringUtils;
import nc.vo.mmpac.pmo.pac0002.entity.PMOAggVO;
import nc.vo.mmpac.pmo.pac0002.entity.PMOHeadVO;
import nc.vo.mmpac.pmo.pac0002.entity.PMOItemVO;
import nc.vo.org.FactoryVO;
import nc.vo.pub.BusinessException;
import nc.vo.pubapp.pattern.exception.ExceptionUtils;
import nc.vo.pubapp.pattern.pub.SqlBuilder;
import nc.vo.scmpub.util.ArrayUtil;
import nc.vo.so.m30.entity.SaleOrderBVO;
import nc.vo.vorg.DeptVersionVO;
import nccloud.pubift.commen.itf.utils.IHttpPostOtherSys;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * 流程生产订单审批后推送MES
 */
public class AfterApproveRuleSyncMes implements IRule<PMOAggVO> {

    public static final IHttpPostOtherSys HTTP_POST_OTHER_SYS = NCLocator.getInstance().lookup(IHttpPostOtherSys.class);

    private static final String LOG_INFO_NAME = "OALOG";

    private static final Log obmlog = Log.getInstance(LOG_INFO_NAME);

    private static final BaseDAO dao = new BaseDAO();

    private static final String MES_PMO_SYNC_URL = "/GTHINKING/AjaxService/N_SCSJJSA/102525006.ashx/receive_woinfo_insert";

    private static final String PK_VIOLATION_ERROR_SUBSTRING = "违反了 PRIMARY KEY 约束“PK_WO”";

    @Override
    public void process(PMOAggVO[] pmoAggVOS) {
        if (ArrayUtil.isEmpty(pmoAggVOS)) {
            return;
        }
        try {
            List<PMOAggVO> filteredOrders = checkAndFilterBillSrcOrg(pmoAggVOS);
            if (filteredOrders.isEmpty()) {
                obmlog.info("没有符合条件的生产订单需要同步到MES系统。");
                return;
            }

            obmlog.info("开始同步生产订单到MES系统，符合条件的订单数量: " + filteredOrders.size());
            // 推送到MES系统
            for (PMOAggVO aggVO : filteredOrders) {
                PMOHeadVO head = aggVO.getParentVO();
                PMOItemVO[] bodys = aggVO.getChildrenVO();

                if (bodys == null || bodys.length == 0) {
                    obmlog.warn("生产订单 " + head.getVbillcode() + " 没有行信息，跳过同步。");
                    continue;
                }

                for (PMOItemVO item : bodys) {
                    obmlog.debug("处理生产订单行 " + item.getVrowno());
                    obmlog.debug(item);
                    if (!Objects.equals(item.getVsrctype(),"55A2")) {
                        syncOrderItemToMes(head, item);
                    }else {
                        obmlog.warn("生产订单 " + head.getVbillcode() + " 行 " + item.getVrowno() + " 跳过同步:不是成品");
                    }
                }
            }
            obmlog.info("生产订单同步到MES系统处理完成。");


        } catch (Exception e) {
            obmlog.error("同步生产订单到MES系统失败: " + e.getMessage(), e);
            ExceptionUtils.wrappException(e);
        }
    }

    /**
     * 调用MES接口并处理特定错误（如主键冲突）
     *
     * @param apiPath        MES接口路径
     * @param requestPayload 请求体
     * @param orderNo        订单号 (用于日志)
     * @param itemRow        行号 (用于日志)
     * @throws BusinessException 如果发生非特定可忽略的错误
     */
    private void callMesWithCustomErrorHandling(String apiPath, JSONObject requestPayload, String orderNo, String itemRow) throws BusinessException {
        String responseString = null;

        if (HTTP_POST_OTHER_SYS.checkIfExcludeUser()) {
            obmlog.debug("当前用户为测试用户被配置为不同步，跳过同步MES系统");
            return;
        }

        try {
            responseString = HTTP_POST_OTHER_SYS.callMes(apiPath, requestPayload);
            obmlog.info("生产订单 " + orderNo + " 行 " + itemRow + " MES系统原始返回: " + responseString);

            if (responseString == null || responseString.trim().isEmpty()) {
                obmlog.warn("生产订单 " + orderNo + " 行 " + itemRow + " MES系统返回为空。");
                // 根据业务需求，空响应可能也算一种错误
                // throw new BusinessException("MES系统返回为空。");
                return; // 或者认为空响应不是致命错误，继续执行
            }

            JSONObject jsonResponse = JSONObject.parseObject(responseString);

            // 检查是否存在 "Data" 数组
            if (jsonResponse.containsKey("Data") && jsonResponse.get("Data") instanceof JSONArray) {
                JSONArray dataArray = jsonResponse.getJSONArray("Data");
                for (int i = 0; i < dataArray.size(); i++) {
                    JSONObject dataObject = dataArray.getJSONObject(i);
                    String successInData = dataObject.getString("Success");
                    if ("false".equalsIgnoreCase(successInData)) {
                        String errorMessage = dataObject.getString("ErrorMessage");
                        if (StringUtils.isEmpty(errorMessage)) {
                            errorMessage = dataObject.getString("Message");
                        }
                        if (StringUtils.isEmpty(errorMessage)) {
                            errorMessage = "外部系统在Data数组中未提供明确错误消息，但Success为false。";
                        }

                        if (errorMessage.contains(PK_VIOLATION_ERROR_SUBSTRING)) {
                            obmlog.warn("生产订单 " + orderNo + " 行 " + itemRow + " MES返回主键冲突错误（Item " + i + "），视为可忽略： " + errorMessage);
                            // 特定错误，记录并继续
                        } else {
                            throw new BusinessException("同步MES系统失败 (Item " + i + "), 错误消息：" + errorMessage);
                        }
                    }
                }
            } else {
                // 处理顶层Success字段
                String success = jsonResponse.getString("Success");
                if ("false".equalsIgnoreCase(success)) {
                    String errorMessage = jsonResponse.getString("ErrorMessage");
                    if (StringUtils.isEmpty(errorMessage)) {
                        errorMessage = jsonResponse.getString("Message");
                    }
                    if (StringUtils.isEmpty(errorMessage)) {
                        errorMessage = "外部系统未提供明确错误消息，但顶层Success为false。";
                    }

                    if (errorMessage.contains(PK_VIOLATION_ERROR_SUBSTRING)) {
                        obmlog.warn("生产订单 " + orderNo + " 行 " + itemRow + " MES返回主键冲突错误，视为可忽略： " + errorMessage);
                        // 特定错误，记录并继续
                    } else {
                        throw new BusinessException("同步MES系统失败, 错误消息：" + errorMessage);
                    }
                }
            }
            // 如果所有检查通过或特定错误被忽略，则方法正常结束
        } catch (BusinessException e) {
            // 直接抛出已捕获的业务异常
            throw e;
        } catch (Exception e) {
            // 捕获调用callMes或JSON解析等其他异常
            obmlog.error("生产订单 " + orderNo + " 行 " + itemRow + " 调用MES或处理响应时发生错误。原始响应: " + responseString + " 错误: " + e.getMessage(), e);
            throw new BusinessException("调用MES或处理响应时发生错误：" + e.getMessage(), e);
        }
    }

    /**
     * 检查并筛选需要同步的单据
     */
    private List<PMOAggVO> checkAndFilterBillSrcOrg(PMOAggVO[] pmoAggVOS) throws BusinessException {
        List<PMOAggVO> aggvoList = new ArrayList<>();
        for (PMOAggVO aggvo : pmoAggVOS) {
            String pkOrg = aggvo.getParentVO().getPk_org();
            String orgCode = transferCodeByPk(FactoryVO.getDefaultTableName(), FactoryVO.CODE, FactoryVO.PK_FACTORY, pkOrg);
//            // 防止mes传bip，bip再推送给mes
//            String billmaker = aggvo.getParentVO().getBillmaker();
//            String userCode = transferCodeByPk("sm_user", UserVO.USER_CODE, UserVO.CUSERID, billmaker);
//
//            if (HTTP_POST_OTHER_SYS.checkIfIncludeOrg(orgCode)&&(!"gaoning".equals(userCode))) {
//                aggvoList.add(aggvo);
//            }

            if (HTTP_POST_OTHER_SYS.checkIfIncludeOrg(orgCode)) {
                aggvoList.add(aggvo);
            }
        }
        return aggvoList;
    }

    private void syncOrderItemToMes(PMOHeadVO head, PMOItemVO item) throws BusinessException {
        JSONObject data = new JSONObject();
        String vbillcode = head.getVbillcode(); // 单据号
        String itemRow = item.getVrowno(); // 行号        obmlog.info("开始为生产订单 " + vbillcode + " 行 " + itemRow + " 构建同步MES数据。");

        // 检查是否需要同步到MES系统
        if (!shouldSyncToMes(item)) {
            obmlog.info("生产订单 " + vbillcode + " 行 " + itemRow + " 不满足同步条件，跳过同步到MES系统。");
            return;
        }

        // orderNo String 是 生产订单号+行号 vbillcode+itemRow
        String orderNoWithRow = vbillcode + itemRow;
        if (orderNoWithRow.length() > 18) {
            throw new BusinessException("MES同步要求:生产订单 " + vbillcode + " 行 " + itemRow + " 单据号+行号长度不能超过18位，当前长度：" + orderNoWithRow.length());
        }

        data.put("orderNo", orderNoWithRow);

        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");

        // startDate String 是 计划开工日期 格式yyyy-MM-dd tplanstarttime
        if (item.getTplanstarttime() != null && item.getTplanstarttime().toStdString() != null) {
            data.put("startDate", item.getTplanstarttime().getDate().toStdString().toString());
        } else {
            data.put("startDate", null);
        }

        // endDate String 是 计划完工日期 格式yyyy-MM-dd tplanendtime
        if (item.getTplanendtime() != null && item.getTplanendtime().getDate().toStdString() != null) {
            data.put("endDate", item.getTplanendtime().getDate().toStdString().toString());
        } else {
            data.put("endDate", null); // Or handle as error if required
        }

        // type String 是 类型 有传入销售订单号时传入MPS；否则传入EXT 默认：MPS
        // status String 是 状态 有传入销售订单号时传入P；否则传入I 默认：P
        // MRPMark String 是 MRP标志 有传入销售订单号时传入Y；否则传入N 默认：Y
        data.put("type", "MPS");
        data.put("status", "P");
        data.put("MRPMark", "Y");

        // materialId String 是 物料编码 cmaterialvid (物料版本PK)
        if (!StringUtils.isEmpty(item.getCmaterialvid())) {
            String materialCode = transferCodeByPk(MaterialVO.getDefaultTableName(), MaterialVO.CODE, MaterialVO.PK_MATERIAL, item.getCmaterialvid());
            data.put("materialId", materialCode);
        } else {
            throw new BusinessException("生产订单 " + vbillcode + " 行 " + itemRow + " 物料版本信息获取失败。");
        }


        // bomNum String 是 产品结构号 cbbomversionid (BOM版本PK) -
        if (item.getCbomversionid() != null) {
            String hcmaterialid = transferCodeByPk(BomVO.TABLE_NAME, BomVO.HCMATERIALID, nc.vo.bd.bom.bom0202.entity.BomVO.CBOMID, item.getCbomversionid());
            String bomNum = transferCodeByPk(MaterialVersionVO.getDefaultTableName(), MaterialVersionVO.CODE, MaterialVersionVO.PK_SOURCE, hcmaterialid);
            data.put("bomNum", bomNum);

            // routeNum String 是 工艺路线号 vrtversion (工艺路线版本号)// 2025年5月23日9点50分，段京岗：routeNum和bomNum取相同值
            data.put("routeNum", bomNum);

        }

        // departmentId String 是 部门编码 cdeptvid (生产车间PK)
        if (!StringUtils.isEmpty(item.getCdeptvid())) {
            String deptCode = transferCodeByPk(DeptVersionVO.getDefaultTableName(), DeptVersionVO.CODE, DeptVersionVO.PK_VID, item.getCdeptvid());
            data.put("departmentId", deptCode);
        } else {
            throw new BusinessException("生产订单 " + vbillcode + " 行 :MES系统要求" + itemRow + " 部门信息不能为空。");
        }


        // factoryId String 是 工厂编码 pk_org (库存组织PK)
        if (!StringUtils.isEmpty(head.getPk_org())) {
            String factoryCode = transferCodeByPk(FactoryVO.getDefaultTableName(), FactoryVO.CODE, FactoryVO.PK_FACTORY, head.getPk_org());
            data.put("factoryId", factoryCode);
        } else {
            throw new BusinessException("生产订单 " + vbillcode + " 行 : MES系统要求" + itemRow + " 工厂信息不能为空。");
        }

        // productNum String 制令号 vsalebillcode 2025年6月13日刘成&程猛:流程生产订单制令号取明细中的cprojectid的编码

        if(!StringUtils.isEmpty(item.getCprojectid())){
            data.put("productNum", transferCodeByPk(ProjectHeadVO.getDefaultTableName(),  ProjectHeadVO.PROJECT_CODE, ProjectHeadVO.PK_PROJECT, item.getCprojectid()));
        }

        // saleOrderNo String 销售订单号 vsalebillcode
        data.put("saleOrderNo", item.getVsalebillcode());

        // saleSequenceNum Double 销售订单序号 vfirstrowno
        if (!StringUtils.isEmpty(item.getVfirstrowno())) {
            try {
                data.put("saleSequenceNum", Double.parseDouble(item.getVfirstrowno()));
            } catch (NumberFormatException e) {
                obmlog.warn("生产订单 " + vbillcode + " 行 " + itemRow + "MES系统同步: 销售订单序号 (vfirstrowno) '" + item.getVfirstrowno() + "' 无法解析为小数。" + e.getMessage());
                data.put("saleSequenceNum", null);
            }
        } else {
            data.put("saleSequenceNum", null);
        }

        // 备注
        data.put("remark", item.getVnote());


        // quantity double 是 需求数量 nnum
        data.put("quantity", item.getNnum() != null ? item.getNnum().doubleValue() : 0.0);
        // assitQuantity double 辅助需求数量 nastnum
        data.put("assitQuantity", item.getNastnum() != null ? item.getNastnum().doubleValue() : 0.0);

        // scaleFactor double 换算系数 vchangerate
        data.put("scaleFactor", transferSpecialField(item.getVchangerate()));

        // method String 是 排产方式 N|排产时指定 S|顺排 D|倒排 默认：N
        data.put("method", "N");
        // priority Double 排产优先级 默认：空
        data.put("priority", null);

        // compileDate String 编制日期 格式yyyy-MM-dd 默认传入当前日期 dbilldate
        String compileDateStr;
        if (head.getDbilldate() != null) {
            compileDateStr = head.getDbilldate().toStdString();
            data.put("compileDate", compileDateStr);
        }

        // 通过自定义属性传
        JSONArray properties = buildProperties(head, item);

        data.put("properties", properties);
        JSONObject requestPayload = new JSONObject();
        JSONArray dataArr = new JSONArray();
        dataArr.add(data);
        requestPayload.put("data", dataArr);


        obmlog.info("生产订单 " + vbillcode + " 行 " + itemRow + " 同步MES请求数据: " + requestPayload.toJSONString());

        // Send to MES using custom error handling
        callMesWithCustomErrorHandling(MES_PMO_SYNC_URL, requestPayload, vbillcode, itemRow);

        obmlog.info("生产订单 " + vbillcode + " 行 " + itemRow + " 已成功发送到MES系统或特定错误已被记录。");
    }

    /**
     * 构建MES同步所需的properties数组
     * 
     * @param head 生产订单表头
     * @param item 生产订单明细
     * @return JSONArray properties数组
     */
    private JSONArray buildProperties(PMOHeadVO head, PMOItemVO item) {
        JSONArray properties = new JSONArray();

        // SXZF16 - 项目主键
        addProperty(properties, "SXZF16", item.getCprojectid());

        // SXZF17 - 单据行号
        addProperty(properties, "SXZF17", item.getVrowno());

        // SXZF18 - 生产订单交易类型编码
        addProperty(properties, "SXZF18", head.getVtrantypecode());
        
        // SXZF22 - 生产订单交易类型编码
//        addProperty(properties, "SXZF22", head.getVtrantypecode());
        
        // SXZF23 - 生产订单主键
        addProperty(properties, "SXZF23", head.getCpmohid());
        
        // SXZF24 - 生产订单明细主键
        addProperty(properties, "SXZF24", item.getCmoid());

        
        return properties;
    }
    
    /**
     * 添加单个property到properties数组
     * 
     * @param properties JSONArray数组
     * @param propertyField 属性字段名
     * @param propertyValue 属性值
     */
    private void addProperty(JSONArray properties, String propertyField, String propertyValue) {
        JSONObject property = new JSONObject();
        property.put("propertyFiled", propertyField);
        property.put("propertyValue", propertyValue);
        properties.add(property);
    }


    /**
     * 根据主键查询编码
     */
    private String transferCodeByPk(String tableName, String selectField, String pkField, String pk) throws BusinessException {
        if (StringUtils.isEmpty(pk)) {
            return null;
        }
        SqlBuilder sqlBuilder = new SqlBuilder();
        sqlBuilder.append(" select " + selectField);
        sqlBuilder.append(" from " + tableName);
        sqlBuilder.append(" where ");
        sqlBuilder.append(pkField, pk);
        Object o = dao.executeQuery(sqlBuilder.toString(), new ColumnProcessor());
        if (o == null) {
            throw new BusinessException("未查询到编码信息，sql【" + sqlBuilder + "】");
        }
        return o.toString();
    }

    /**
     * 转换特殊字段  如 1/1 转换为小数 1.0
     */

    private String transferSpecialField(String field) {
        if (field == null || field.trim().isEmpty()) {
            return null;
        }
        String[] split = field.split("/");
        if (split.length == 2) {
            String numStr = split[0].trim();
            String denStr = split[1].trim();
            if (denStr.equals("0")) {
                return "0.00"; // 分母不能为零
            }
            try {
                BigDecimal numerator = new BigDecimal(numStr);
                BigDecimal denominator = new BigDecimal(denStr);
                return numerator.divide(denominator, 2, RoundingMode.HALF_UP).toString();
            } catch (NumberFormatException e) {
                return field; // 非法数字，返回原字段
            }
        }
        return field;
    }

    /**
     * 判断是否需要同步到MES系统
     * 业务逻辑：
     * 1. 通过源头单据类型判断是否为销售订单（源头单据类型为30）
     * 2. 如果是销售订单，需要联查销售订单子实体
     * 3. 判断销售订单是否有定制标识(vbdef18)
     * 4. 如果有定制标识，需要检查工艺配置完成时间(vbdef17)是否有值
     *    - 有值：传递给MES
     *    - 无值：不传递给MES
     * 5. 如果无定制标识：传递给MES
     * 
     * @param item 生产订单明细
     * @return true-需要同步，false-不需要同步
     * @throws BusinessException
     */
    private boolean shouldSyncToMes(PMOItemVO item) throws BusinessException {
        // 检查源头单据类型是否为销售订单（30）
        if (!"30".equals(item.getVfirsttype())) {
            // 非销售订单来源，直接同步
            obmlog.debug("生产订单明细行来源不是销售订单（源头单据类型：" + item.getVfirsttype() + "），允许同步。");
            return true;
        }
        
        // 是销售订单来源，需要进一步检查
        String vfirstbid = item.getVfirstbid(); // 源头单据子表ID
        if (StringUtils.isEmpty(vfirstbid)) {
            obmlog.warn("生产订单明细行来源为销售订单，但源头单据子表ID为空，默认允许同步。");
            return true;
        }
        
        try {
            // 查询销售订单子实体的定制标识和工艺配置完成时间
            SqlBuilder sqlBuilder = new SqlBuilder();
            sqlBuilder.append(" SELECT vbdef18, vbdef17 ");
            sqlBuilder.append(" FROM so_saleorder_b ");            sqlBuilder.append(" WHERE csaleorderbid = ? ");
            sqlBuilder.append(" AND dr = 0 ");
            SQLParameter sqlParam = new SQLParameter();
            sqlParam.addParam(vfirstbid);
            SaleOrderBVO saleOrderBVO = (SaleOrderBVO) dao.retrieveByPK(SaleOrderBVO.class, vfirstbid);
            if (Objects.isNull(saleOrderBVO)) {
                obmlog.warn("未查询到销售订单子实体信息（ID：" + vfirstbid + "），默认允许同步。");
                return true;
            }
            
            String vbdef18 = saleOrderBVO.getVbdef18();
            String vbdef17 = saleOrderBVO.getVbdef17();

            obmlog.debug("销售订单定制标识：" + vbdef18 + "，工艺配置完成时间：" + vbdef17);
            
            // 如果无定制标识，允许同步
            if (StringUtils.isEmpty(vbdef18)) {
                obmlog.debug("销售订单无定制标识，允许同步。");
                return true;
            }

            if ("N".equals(vbdef18)) {
                obmlog.debug("销售订单定制标识为N，允许同步。");
                return true;
            }

            // 有定制标识，检查工艺配置完成时间
            if (StringUtils.isEmpty(vbdef17)) {
                obmlog.info("销售订单有定制标识但工艺配置完成时间为空，不允许同步。");
                return false;
            } else {
                obmlog.debug("销售订单有定制标识且工艺配置完成时间有值，允许同步。");
                return true;
            }
            
        } catch (Exception e) {
            obmlog.error("查询销售订单信息失败：" + e.getMessage(), e);
            // 查询失败时，为了避免影响正常业务，默认允许同步
            throw new BusinessException("查询销售订单信息失败：" + e.getMessage());
        }
    }

}
