package nccloud.web.uapbd.material.action;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import nc.bs.dao.DAOException;
import nc.bs.logging.Log;
import nc.bs.trade.business.HYSuperDMO;
import nc.vo.bd.defdoc.DefdocVO;
import nc.vo.cmp.util.StringUtils;
import nc.vo.ml.NCLangRes4VoTransl;
import nc.vo.pub.BusinessException;
import nccloud.baseapp.core.log.NCCForPrintLogger;
import nccloud.framework.core.exception.ExceptionUtils;
import nccloud.framework.core.io.WebFile;
import nccloud.framework.core.io.WebFileDisposition;
import nccloud.framework.web.action.itf.ICommonAction;
import nccloud.framework.web.container.IRequest;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.util.EntityUtils;
import org.owasp.esapi.ESAPI;

import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * 获取物料PLM文件
 *
 * @author mzr
 * @date 2025/8/4
 */
public class MaterialPlmDownloadAction implements ICommonAction {
    private static final String LOG_INFO_NAME = "dldzlog";
    private static final Log logger = Log.getInstance(LOG_INFO_NAME);
    private String plmBaseUrl = "";
    private String plmUser = "";
    private String token = "";
    private static final String tokenUrl = "/sipmweb/api/oauth";
    // 根据物料编码获取零部件ID
    private String materialIdUrl = "/sipmweb/api/{rid}/search/{t}";
    // 根据零部件ID获取二维图档ID及信息
    private String materialFileIdUrl = "/sipmweb/api/{rid}/relation/{t}/{id}/data";
    // 下载文件
    private String downlownUrl = "/sipmweb/web/download";

    @Override
    public Object doAction(IRequest request) {
        WebFile files = null;
        try {
            Map<String, String[]> params_1 = request.readParameters();
            String[] materialCodeArr = params_1.get("materialCode"); // 获取所有物料编码
            if (materialCodeArr == null || materialCodeArr.length == 0) {
                ExceptionUtils.wrapBusinessException("物料编码不能为空");
            }
            // NCCForUAPLogger.debug("MaterialPlmDownloadAction-pk = " + Arrays.toString(materialCodeArr));
            // String materialCode = "101092250323,101092250321,101092250322";
            // String[] materialCodeArr = materialCode.split(",", -1);
            files = this.getPlmFiles(materialCodeArr);
        } catch (Exception ex) {
            logger.error("MaterialPlmDownloadAction-exp:" + ex.getMessage());
            // 将异常转换为WebFile的形式返回，以便前端展示报错
            NCCForPrintLogger.error(ex);
            Throwable e = org.apache.commons.lang3.exception.ExceptionUtils.getRootCause(ex);
            String mess = ESAPI.encoder().encodeForHTML(NCLangRes4VoTransl.getNCLangRes().getStrByID("1501003_0", "01501003-0121")) + ESAPI.encoder().encodeForHTML(e.getMessage());
            if (e.getMessage() == null || "".equals(e.getMessage())) {
                StringWriter sw = new StringWriter();
                e.printStackTrace(new PrintWriter(sw, true));
                mess = ESAPI.encoder().encodeForHTML(NCLangRes4VoTransl.getNCLangRes().getStrByID("1501003_0", "01501003-0122")) + ESAPI.encoder().encodeForHTML(sw.toString());
                mess = "<span style= \"color:red \">" + mess + "</span>";
            }

            InputStream in = new ByteArrayInputStream(mess.getBytes());
            WebFile errorFile = new WebFile("print_error.html", in);
            errorFile.setDisposition(WebFileDisposition.Inline);
            files = errorFile;
        }
        return files;
    }

    public WebFile getPlmFiles(String[] materialCodeArr) throws Exception {
        WebFile file = null;
        if (materialCodeArr == null || materialCodeArr.length == 0) {
            return file;
        }
        // 获取PLM的参数
        Map<String, String> configParams = getConfigParams("Dldz-config");
        if (configParams == null || configParams.isEmpty()) {
            throw new BusinessException("未配置PLM参数");
        }
        plmBaseUrl = configParams.get("plmBaseUrl");
        plmUser = configParams.get("plmUser");
        token = getToken();
        if (materialCodeArr.length == 1) {
            String materialCode = materialCodeArr[0];
            String materialId = getMaterialId(materialCode);
            if (StringUtils.isEmpty(materialId)) {
                throw new BusinessException("物料编码:" + materialCode + ",获取PLM物料id失败,请检查PLM是否存在对应的物料");
            }
            String fileRes = this.getFileId(materialId);
            JSONObject fileJson = JSONObject.parseObject(fileRes);
            String list = fileJson.getString("list");
            JSONArray jsonArray = JSONObject.parseArray(list);
            if (jsonArray == null || jsonArray.isEmpty()) {
                throw new BusinessException("物料编码:" + materialCode + ",获取PLM物料的文件信息失败");
            }
            JSONObject plmFileJson = jsonArray.getJSONObject(0);
            String objId = plmFileJson.getString("objId");
            if (objId == null || objId.isEmpty()) {
                throw new BusinessException("物料编码:" + materialCode + ",获取PLM物料的文件信息失败");
            }
            String fname = plmFileJson.getString("fname");

            byte[] fileBytes = this.doDownloadPlmFile(objId);
            if (fileBytes.length == 0) {
                throw new BusinessException("物料编码:" + materialCode + ",未查询到PLM文件");
            }
            InputStream ins = new ByteArrayInputStream(fileBytes);
            file = new WebFile(fname, ins);
        } else {
            file = processMultipleFiles(materialCodeArr);
        }

        return file;
    }

    /**
     * 处理多个文件并打包
     *
     * @param materialCodeArr 物料编码数组
     * @return 打包后的zip文件WebFile对象
     * @throws Exception 异常
     */
    private WebFile processMultipleFiles(String[] materialCodeArr) throws Exception {
        // 在内存中的 ZIP 输出流
        ByteArrayOutputStream zipOut = new ByteArrayOutputStream();
        ZipOutputStream zipStream = new ZipOutputStream(zipOut);
        int successCount = 0; // 成功处理的文件数
        try {
            for (int i = 0; i < materialCodeArr.length; i++) {
                String materialCode = materialCodeArr[i];
                try {
                    boolean success = processSingleMaterialToZip(materialCode, i, zipStream);
                    if (success) {
                        successCount++;
                    }
                } catch (BusinessException e) {
                    logger.error("处理物料编码 " + materialCode + " 时出现异常: " + e.getMessage());
                    // 继续处理下一个
                }
            }
            zipStream.finish();
        } finally {
            zipStream.close();
        }

        // 如果没有任何文件成功处理
        if (successCount == 0) {
            throw new BusinessException("没有有效的文件可以打包");
        }

        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss");
        String zipName = "图纸文档打包_" + sdf.format(new Date());
        // 创建 WebFile 对象用于 ZIP 文件
        InputStream ins = new ByteArrayInputStream(zipOut.toByteArray());
        return new WebFile(zipName + ".zip", ins);
    }

    /**
     * 将单个物料文件添加到ZIP流中
     *
     * @param materialCode 物料编码
     * @param index        索引
     * @param zipStream    ZIP输出流
     * @return boolean 是否成功处理
     * @throws Exception 异常
     */
    private boolean processSingleMaterialToZip(String materialCode, int index, ZipOutputStream zipStream) throws Exception {
        String materialId = getMaterialId(materialCode);
        if (StringUtils.isEmpty(materialId)) {
            return false;
        }
        String fileRes = this.getFileId(materialId);
        JSONObject fileJson = JSONObject.parseObject(fileRes);
        String list = fileJson.getString("list");
        JSONArray jsonArray = JSONObject.parseArray(list);
        if (jsonArray == null || jsonArray.isEmpty()) {
            return false;
        }
        JSONObject plmFileJson = jsonArray.getJSONObject(0);
        String objId = plmFileJson.getString("objId");
        if (objId == null || objId.isEmpty()) {
            return false;
        }
        String name = plmFileJson.getString("name");
        String suffix = plmFileJson.getString("suffix");
        byte[] fileBytes = this.doDownloadPlmFile(objId);
        if (fileBytes.length == 0) {
            return false;
        }
        String fname = name + "_" + materialCode + "_" + index + "." + suffix;
        zipStream.putNextEntry(new ZipEntry(fname));
        zipStream.write(fileBytes);
        zipStream.closeEntry();
        return true;
    }


    /**
     * 获取token
     */
    private String getToken() throws IOException, BusinessException {
        Map<String, String> tokenMap = new HashMap<>();
        tokenMap.put("uname", plmUser);
        tokenMap.put("f", "true");
        String tokenStr = doGet(plmBaseUrl + tokenUrl, tokenMap);
        // logger.error("GetPlmFileUtil-getToken-tokenStr = " + tokenStr);
        JSONObject jsonObject = JSONObject.parseObject(tokenStr);
        String token = jsonObject.getString("errmsg");
        if (token == null || token.isEmpty()) {
            throw new BusinessException("PLM鉴权失败");
        }
        return token;
    }

    /**
     * 根据物料编码获取零部件ID
     */
    private String getMaterialId(String materialCode) throws IOException, BusinessException {
        String fileUrl = plmBaseUrl + materialIdUrl;
        fileUrl = fileUrl.replace("{rid}", token);
        // 对象表名	MPART（零部件）
        fileUrl = fileUrl.replace("{t}", "MPART");
        Map<String, String> map = new HashMap<>();
        map.put("key", materialCode);// 搜索关键字
        map.put("start", "0");// 偏移量
        map.put("size", "10");// 数量
        String result = doGet(fileUrl, map);
        logger.error("GetPlmFileUtil-getMaterialId-result = " + result);
        JSONObject jsonObject = JSONObject.parseObject(result);
        String list = jsonObject.getString("list");
        JSONArray jsonArray = JSONObject.parseArray(list);
        if (jsonArray == null || jsonArray.isEmpty()) {
            return "";
        }
        JSONObject listJson = jsonArray.getJSONObject(0);
        String objId = listJson.getString("objId");
        if (objId == null || objId.isEmpty()) {
            return "";
        }
        return objId;
    }

    /**
     * 根据零部件ID获取文件ID
     */
    private String getFileId(String materialId) throws IOException, BusinessException {
        String fileUrl = plmBaseUrl + materialFileIdUrl;
        fileUrl = fileUrl.replace("{rid}", token);
        // 对象表名	MPART（零部件）
        fileUrl = fileUrl.replace("{t}", "MPART");
        // 对象id
        fileUrl = fileUrl.replace("{id}", materialId);
        Map<String, String> map = new HashMap<>();
        map.put("re", "MPART_SIPM1");// 关系ID 二维图档：MPART_SIPM1
        map.put("start", "0");// 偏移量
        map.put("item", "SIPM1");// 关联对象表名
        String result = doGet(fileUrl, map);
        logger.error("GetPlmFileUtil-getFileId-result = " + result);
        return result;
    }

    /**
     * 调用PLM的下载文件接口
     */
    private byte[] doDownloadPlmFile(String fileId) throws IOException {
        String fileUrl = plmBaseUrl + downlownUrl;
        Map<String, String> map = new HashMap<>();
        map.put("rid", token);
        map.put("id", fileId);// 对象id
        map.put("t", "SIPM1");// 对象表名
        map.put("type", "D");// 文件类别（BD,D）（D是文件本身的文件，BD是PDF图）
        return getFileFromPlm(fileUrl, map);
    }

    public Map<String, String> getConfigParams(String code) {
        Map<String, String> map = new HashMap<>();
        String strWhere = " pk_defdoclist in (select pk_defdoclist from bd_defdoclist where code='[code]' and dr=0 ) and dr=0";
        strWhere = strWhere.replace("[code]", code);
        try {
            DefdocVO[] defdocVOs = (DefdocVO[]) new HYSuperDMO().queryByWhereClause(DefdocVO.class, strWhere);
            if (defdocVOs != null) {
                for (DefdocVO defdocVO : defdocVOs) {
                    String value = StringUtils.isEmpty(defdocVO.getMemo()) ? defdocVO.getName() : defdocVO.getMemo();
                    map.put(defdocVO.getCode().trim(), value);
                }
            }
        } catch (DAOException e) {
            logger.error("Failed to get config parameters for code: " + code, e);
        }
        return map;
    }


    private String doGet(String requestUrl, Map<String, String> paramMap) throws IOException {
        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
        cm.setMaxTotal(5000);
        cm.setDefaultMaxPerRoute(500);

        RequestConfig globalConfig = RequestConfig.custom().setConnectionRequestTimeout(50000) // 连接池获取连接超时
                .setConnectTimeout(50000) // 连接建立超时
                .setSocketTimeout(200000) // 等待响应超时
                .setCookieSpec(CookieSpecs.IGNORE_COOKIES).build();

        CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(cm)
                .setDefaultRequestConfig(globalConfig).build();
        String url = buildUrlWithParams(requestUrl, paramMap);
        HttpGet get = new HttpGet(url);
        String responseString = httpClient.execute(get, response -> EntityUtils.toString(response.getEntity()));
        get.releaseConnection();
        return responseString;
    }

    /**
     * 调用第三方文件接口并接收文件流
     *
     * @param requestUrl 文件接口URL
     * @return 文件流
     */
    private byte[] getFileFromPlm(String requestUrl, Map<String, String> paramMap) throws IOException {
        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
        cm.setMaxTotal(5000);
        cm.setDefaultMaxPerRoute(500);

        RequestConfig globalConfig = RequestConfig.custom().setConnectionRequestTimeout(50000) // 连接池获取连接超时
                .setConnectTimeout(50000) // 连接建立超时
                .setSocketTimeout(200000) // 等待响应超时
                .setCookieSpec(CookieSpecs.IGNORE_COOKIES).build();

        CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(cm)
                .setDefaultRequestConfig(globalConfig).build();
        String url = buildUrlWithParams(requestUrl, paramMap);
        HttpGet get = new HttpGet(url);
        byte[] responseString = httpClient.execute(get, response -> EntityUtils.toByteArray(response.getEntity()));
        get.releaseConnection();
        return responseString;
    }

    /**
     * 构建带参数的URL
     *
     * @param baseUrl  基础URL
     * @param paramMap 参数Map
     * @return 完整URL
     */
    private String buildUrlWithParams(String baseUrl, Map<String, String> paramMap) {
        StringBuilder param = new StringBuilder("?");
        if (paramMap != null && !paramMap.isEmpty()) {
            for (Map.Entry<String, String> entry : paramMap.entrySet()) {
                param.append(entry.getKey());
                param.append("=");
                param.append(entry.getValue());
                param.append("&");
            }
            param.deleteCharAt(param.length() - 1);
        }
        return baseUrl + param;
    }

}
