package com.docus.server.report.api.impl;

import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.docus.core.util.DateUtil;
import com.docus.core.util.ExceptionUtils;
import com.docus.core.util.Func;
import com.docus.infrastructure.web.api.CommonResult;
import com.docus.server.common.entity.RemoteCallResult;
import com.docus.server.common.service.RemoteCallResultService;
import com.docus.server.report.api.ShunDePeopleService;
import com.docus.server.report.api.dto.*;
import com.docus.server.report.api.vo.SdRyBloodReportVO;
import com.docus.server.report.client.JaxWsDynamicClient;
import com.docus.server.report.config.SdRyReportQueryConfig;
import com.docus.server.report.job.ReportJob;
import com.docus.server.report.util.IdUtil;
import com.docus.server.report.util.TableJsonRead;
import com.docus.server.report.util.XmlUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 顺德人医调用
 *
 * @author wyb
 */
@Component
@Slf4j
public class ShunDePeopleServiceImpl implements ShunDePeopleService {
    private static final Lock INDEX_LOCK = new ReentrantLock();
    private static final Lock PACS_PDF_LOCK = new ReentrantLock();
    private static final Lock INSPECT_REPORT_LOCK = new ReentrantLock();
    private static final Lock LIS_REPORT_LOCK = new ReentrantLock();
    private static final Lock NURSE_INSERT_SUGAR_LOCK = new ReentrantLock();
    private static final Lock NURSE_INSERT_SUGAR_PUSH_ERR_LOCK = new ReentrantLock();

    @Resource
    private SdRyReportQueryConfig sdRyReportQueryConfig;
    @Resource
    private ReportJob reportJob;
    @Resource
    private RemoteCallResultService remoteCallResultService;


    @Override
    public String getInspectReportByJxId(SdJxReportDto sdJxReportDto) {
        String requestParam = organizationQuerySdRyInspectReportParam(sdJxReportDto);
        String url = organizationQuerySdRyInspectReportUrl(sdRyReportQueryConfig.getReportQueryInspectUrl());
        log.info("查询检查报告请求地址：{} ,请求body参数:{}", url, requestParam);
        INSPECT_REPORT_LOCK.lock();
        try {
            String respBody = this.sendPost(url, requestParam);
            log.info("查询检查报告请求成功,响应参数：{}", respBody);
            TimeUnit.MILLISECONDS.sleep(sdRyReportQueryConfig.getReportQueryInspectInterval());
            return respBody;
        } catch (Exception ex) {
            log.error("查询检查报告失败：" + ex.getMessage(), ex);
            return "";
        } finally {
            INSPECT_REPORT_LOCK.unlock();
        }
    }

    @Override
    public String getLisReportByJxId(SdJxReportDto sdJxReportDto) {
        String requestParam = organizationQuerySdRyLisReportParam(sdJxReportDto);
        String url = organizationQuerySdRyLisReportUrl(sdRyReportQueryConfig.getReportQueryInspectUrl());
        log.info("查询LIS检验报告请求地址：{} ,请求body参数:{}", url, requestParam);
        LIS_REPORT_LOCK.lock();
        try {
            String respBody = this.sendPost(url, requestParam);
            log.info("查询LIS检验报告请求成功,响应参数：{}", respBody);
            TimeUnit.MILLISECONDS.sleep(sdRyReportQueryConfig.getReportQueryLisInterval());
            return respBody;
        } catch (Exception ex) {
            log.error("查询LIS检验报告失败：" + ex.getMessage(), ex);
            return "";
        } finally {
            LIS_REPORT_LOCK.unlock();
        }
    }

    @Override
    public List<String> getSdRyMzReportJxIds(String sdRyIndex) {
        List<String> indexes = new ArrayList<>();
        try {
            String mzParam = organizationQuerySdRyReportIndexParam(sdRyIndex, 1);
            String[] mzParams = {"HIP1179", mzParam};
            return getSdRyReportJxIds(mzParams);
        } catch (Exception ex) {
            log.error(ex.getMessage(), ex);
        }
        return indexes;
    }

    @Override
    public List<String> getSdRyZyReportJxIds(String sdRyIndex) {
        List<String> indexes = new ArrayList<>();
        try {
            String zyParam = organizationQuerySdRyReportIndexParam(sdRyIndex, 3);
            String[] zyParams = {"HIP1179", zyParam};
            return getSdRyReportJxIds(zyParams);
        } catch (Exception ex) {
            log.error(ex.getMessage(), ex);
        }
        return indexes;
    }

    private List<String> getSdRyReportJxIds(String[] qrIndexParams) {
        List<String> indexes = new ArrayList<>();
        try {
            List<String> zyIndex = getIndex(qrIndexParams);
            indexes.addAll(zyIndex);
        } catch (Exception ex) {
            log.error(ex.getMessage(), ex);
        }
        return indexes;
    }

    @Override
    public String getBase64PdfFromPacs(String examNo, String reportNo) {
        TableJsonRead jsonRead = new TableJsonRead();
        SdPacsServerConfig config = jsonRead.Read("data-config", "sdry-pacs-server.json", SdPacsServerConfig.class);
        String url = config.getUrl();
        String namespaceUri = config.getNamespaceUri();
        String operationName = config.getOperationName();
        String functionType = config.getFunctionType();
        String functionName = config.getFunctionName();
        String caller = config.getCaller();
        int interval = config.getInterval();
        String inputString = getPdfReportBase64InputString(examNo, reportNo, config);
        //  参数1（FunctionName）：此参数用于区分不同的场景，具体看每个场景说明
        //	参数2（InputString）：请求消息串，看具体场景说明
        //	参数3（FunctionType）：传空，暂未启用
        //	参数4（Caller）：服务调用者名称，由PACS提供，验证令牌时
        String[] param = {functionName, inputString, functionType, caller};

        RemoteCallResult remoteCallResult = new RemoteCallResult();
        remoteCallResult.setUrl(url+" | "+namespaceUri+" | "+operationName);
        remoteCallResult.setRequest(Arrays.toString(param));
        remoteCallResult.setDescription("根据检查流水和报告序列号，指定要获取的报告，PACS将生成PDF格式的报告，并将PDF文件的BASE64编码串返回。");
        remoteCallResult.setKeyword(examNo + "_" + reportNo);
        remoteCallResult.setCallStartTime(new Date());


        PACS_PDF_LOCK.lock();
        try {
            String result = JaxWsDynamicClient.send(url, namespaceUri, operationName, param);
            if (interval > 0) {
                TimeUnit.MILLISECONDS.sleep(interval);
            }
            remoteCallResult.setCallStatus(1);
            String base64 = reportJob.parsePacsGetBase64(result);
            if(Func.isBlank(base64)){
                remoteCallResult.setRemark("查询base64结果为空!");
                remoteCallResult.setCallStatus(3);
            }
            remoteCallResult.setResponse(result);
            remoteCallResult.setCallEndTime(new Date());
            try {
                remoteCallResultService.save(remoteCallResult);
            }catch (Exception e){
                e.printStackTrace();
            }
            return result;
        } catch (Exception ex) {
            log.error(ex.getMessage(), ex);
            String exceptionMessage = ExceptionUtils.getExceptionMessage(ex);
            remoteCallResult.setRemark(ex.getMessage());
            remoteCallResult.setCallStatus(2);
            remoteCallResult.setResponse(exceptionMessage);
            remoteCallResult.setCallEndTime(new Date());
            try {
                remoteCallResultService.save(remoteCallResult);
            }catch (Exception e){
                e.printStackTrace();
            }
            return null;
        } finally {
            PACS_PDF_LOCK.unlock();

        }
    }

    public static void main(String[] args) {
        SdNurseInsertSugarRequest request = new SdNurseInsertSugarRequest("10086", "1", "nur");
        String requestParam = Func.toJson(request);
        System.out.println(requestParam);
    }

    @Override
    public SdNurseInsertSugarResponse getNurseInsertSugarReport(SdNurseInsertSugarRequest request) {
        String randomUuid = Func.randomUUID();
        NURSE_INSERT_SUGAR_LOCK.lock();
        try {
            String requestParam = Func.toJson(request);
            log.info("[{}]查询移动护理，请求地址：{}，参数：{}", randomUuid, sdRyReportQueryConfig.getReportQueryNurseInsertSugarUrl(), requestParam);
            String respBody = sendPost(sdRyReportQueryConfig.getReportQueryNurseInsertSugarUrl(), requestParam);
            log.info("[{}]查询移动护理，响应：{}", randomUuid, respBody);
            TimeUnit.MILLISECONDS.sleep(sdRyReportQueryConfig.getReportQueryNurseInsertSugarInterval());
            return JSON.parseObject(respBody, SdNurseInsertSugarResponse.class);
        } catch (Exception ex) {
            log.error("[" + randomUuid + "]" + "查询移动护理出错了：" + ex.getMessage(), ex);
            return new SdNurseInsertSugarResponse();
        } finally {
            NURSE_INSERT_SUGAR_LOCK.unlock();
        }
    }

    @Override
    public SdNurDocFailResponse pushSdNurDocFail(SdNurDocFailRequest request) {
        String randomUuid = Func.randomUUID();
        NURSE_INSERT_SUGAR_PUSH_ERR_LOCK.lock();
        try {
            String body = Func.toJson(request);
            log.info("[{}] 移动护理推送错误，请求地址：{}，参数：{}", randomUuid, sdRyReportQueryConfig.getReportQueryNurseInsertSugarErrPushUrl(), body);
            String respBody = sendPost(sdRyReportQueryConfig.getReportQueryNurseInsertSugarErrPushUrl(), body);
            log.info("[{}] 移动护理推送错误，响应：{}", randomUuid, respBody);
            TimeUnit.MILLISECONDS.sleep(sdRyReportQueryConfig.getReportQueryNurseInsertSugarErrPushInterval());
            return JSON.parseObject(respBody, SdNurDocFailResponse.class);
        } catch (Exception ex) {
            log.error("[" + randomUuid + "]" + "移动护理推送异常，出错了：" + ex.getMessage(), ex);
            return null;
        } finally {
            NURSE_INSERT_SUGAR_PUSH_ERR_LOCK.unlock();
        }
    }


    @Override
    public CommonResult<List<SdRyBloodReportVO>> getBloodView(String inpatientNo, Integer admissTimes) {
        String rqId = Func.randomUUID();
        String url = sdRyReportQueryConfig.getBloodUrl() + "?inpatientNo=" + inpatientNo + "&admissTimes=" + admissTimes;
        try {
            log.info("[{}]查询血液报告地址：{}", rqId, url);
            String resp = HttpUtil.get(url);
            log.info("[{}]查询血液报告结果：{}", rqId, resp);
            return JSON.parseObject(resp, new TypeReference<CommonResult<List<SdRyBloodReportVO>>>() {
            });
        } catch (Exception ex) {
            log.error("[" + rqId + "]" + "查询血液报告出错了：" + ex.getMessage(), ex);
            return CommonResult.failed("");
        }
    }

    private String organizationQuerySdRyInspectReportUrl(String reportQueryLisUrl) {
        return reportQueryLisUrl + "/query?uuid=" + IdUtil.standardUUID() +
                "&action=" + sdRyReportQueryConfig.getReportQueryInspectAction() +
                "&accessKey=" + sdRyReportQueryConfig.getReportQueryInspectAccessKey()
                + "&creationTime=" + Func.format(new Date(), DateUtil.PATTERN_DATETIME_MINI);

    }

    private String organizationQuerySdRyInspectReportParam(SdJxReportDto dto) {
        String patientId = dto.getPatientId();
        if ("1".equals(dto.getPatentTypeCode())) {
            patientId = "m" + patientId;
        }
        if ("3".equals(dto.getPatentTypeCode())) {
            patientId = "z" + patientId;
        }
        HashMap<Object, Object> map = new HashMap<>(7);
        map.put("isPage", dto.isPage());
        map.put("pageNo", dto.getPageNo());
        map.put("pageSize", dto.getPageSize());
        map.put("OrgCode", dto.getOrgCode());
        map.put("StartTime", dto.getStartTime());
        map.put("EndTime", dto.getEndTime());
        map.put("PatientId", patientId);
        map.put("PatentTypeCode", dto.getPatentTypeCode());
        return Func.toJson(map);
    }

    private String organizationQuerySdRyLisReportUrl(String reportQueryLisUrl) {
        return reportQueryLisUrl + "/query?uuid=" + IdUtil.standardUUID() +
                "&action=" + sdRyReportQueryConfig.getReportQueryLisAction() +
                "&accessKey=" + sdRyReportQueryConfig.getReportQueryLisAccessKey()
                + "&creationTime=" + Func.format(new Date(), DateUtil.PATTERN_DATETIME_MINI);

    }

    private String organizationQuerySdRyLisReportParam(SdJxReportDto dto) {
        String patientId = dto.getPatientId();
        if ("1".equals(dto.getPatentTypeCode())) {
            patientId = "m" + patientId;
        }
        if ("3".equals(dto.getPatentTypeCode())) {
            patientId = "z" + patientId;
        }
        HashMap<Object, Object> map = new HashMap<>(7);
        map.put("isPage", dto.isPage());
        map.put("pageNo", dto.getPageNo());
        map.put("pageSize", dto.getPageSize());
        map.put("OrgCode", dto.getOrgCode());
        map.put("StartTime", dto.getStartTime());
        map.put("EndTime", dto.getEndTime());
        map.put("PatientId", patientId);
        map.put("PatentTypeCode", dto.getPatentTypeCode());
        return Func.toJson(map);
    }


    private String getPdfReportBase64InputString(String examNo, String reportNo, SdPacsServerConfig config) {
        return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
                "<!-- PDF报告请求消息 -->\n" +
                "<QueryInfo>\n" +
                "<!-- 调用系统 -->\n" +
                "\t<LicenseInfo>\n" +
                "\t\t<!-- 调用系统 -->\n" +
                "\t\t<Caller>" + config.getLicenseInfo().getCaller() + "</Caller>\n" +
                "\t\t<!-- 调用令牌（由PACS提供） -->\n" +
                "\t\t<License>" + config.getLicenseInfo().getLicense() + "</License>\n" +
                "\t</LicenseInfo>\n" +
                "\t<!-- 检查流水号 -->\n" +
                "\t<ExamNo>" + examNo + "</ExamNo>\n" +
                "\t<!-- 报告序号 -->\n" +
                "\t<ReportNo>" + reportNo + "</ReportNo>\n" +
                "</QueryInfo>";
    }


    private List<String> getIndex(String[] params) {
        INDEX_LOCK.lock();
        try {
            String namespaceUri = sdRyReportQueryConfig.getQueryReportIndexWsdlNamespaceUri();
            String wsdlAddr = sdRyReportQueryConfig.getQueryReportIndexWsdlAddr();
            String operationName = sdRyReportQueryConfig.getQueryReportIndexWsdlOperationName();
            String result = JaxWsDynamicClient.send(wsdlAddr, namespaceUri, operationName, params);
            log.info("查询顺德人医患者交叉索引数据，返回值：{}", result);
            if (result == null) {
                return new ArrayList<>();
            }
            TimeUnit.MILLISECONDS.sleep(sdRyReportQueryConfig.getQueryReportIndexInterval());
            return parseQuerySdRyReportIndex(result);
        } catch (Exception ex) {
            log.error(ex.getMessage(), ex);
            return new ArrayList<>();
        } finally {
            INDEX_LOCK.unlock();
        }
    }

    private List<String> parseQuerySdRyReportIndex(String result) {
        try {
            XmlUtil xmlUtil = XmlUtil.of(result);
            NodeList nodeList = xmlUtil.getNodeList("/PRPA_HIP1179/controlActProcess/subject/empiIndex/patientIds/patientId");
            int nodeListLength = nodeList.getLength();
            ArrayList<String> patientIds = new ArrayList<>();
            if (nodeListLength > 0) {
                for (int i = 0; i < nodeListLength; i++) {
                    Node node = nodeList.item(i);
                    String patientId = node.getTextContent();
                    if (Func.isNotBlank(patientId)) {
                        patientIds.add(patientId);
                    }
                }
            }
            return patientIds;
        } catch (Exception ex) {
            log.error("顺德人医解析患者交叉索引失败。", ex);
            return new ArrayList<>();
        }
    }

    /**
     * 组织顺德人医查询交叉索引的请求参数
     *
     * @param sDryIndex 顺德人医关联索引
     * @param type      1:门诊 3:住院
     * @return 顺德人医查询交叉索引的请求参数
     */
    private String organizationQuerySdRyReportIndexParam(String sDryIndex, int type) {
        sDryIndex = sDryIndex.toLowerCase()
                .replace("m", "")
                .replace("z", "");
        String receiver = "Orion-001";
        String sender = "PaperlessManagementMRIS-001";
        String now = Func.format(new Date(), DateUtil.PATTERN_DATETIME_MINI);

        return "<PRPA_HIP1179 xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" ITSVersion=\"XML_1.0\" xmlns=\"urn:hl7-org:v3\" xsi:schemaLocation=\"urn:hl7-org:v3 ../multicacheschemas/PRPA_IN201306UV02.xsd\">\n" +
                "\t<id root=\"2.16.156.10011.0\" extension=\"" + IdUtil.standardUUID() + "\"/>\n" +
                "\t<creationTime value=\"" + now + "\"/>\n" +
                "\t<interactionId root=\"2.16.840.1.113883.1.6\" extension=\"PRPA_IHIP1179\"/>\n" +
                "\t<processingCode code=\"P\"/>\n" +
                "\t<processingModeCode/>\n" +
                "\t<acceptAckCode code=\"AL\"/>\n" +
                "\t<receiver typeCode=\"RCV\">\n" +
                "\t\t<telecom/>\n" +
                "\t\t<device classCode=\"DEV\" determinerCode=\"INSTANCE\">\n" +
                "\t\t\t<id>\n" +
                "\t\t\t\t<item root=\"2.16.156.10011.0.1.1\" extension=\"" + receiver + "\"/>\n" +
                "\t\t\t</id>\n" +
                "\t\t</device>\n" +
                "\t</receiver>\n" +
                "\t<sender typeCode=\"SND\">\n" +
                "\t\t<telecom/>\n" +
                "\t\t<device classCode=\"DEV\" determinerCode=\"INSTANCE\">\n" +
                "\t\t\t<id>\n" +
                "\t\t\t\t<item root=\"2.16.156.10011.0.1.2\" extension=\"" + sender + "\"/>\n" +
                "\t\t\t</id>\n" +
                "\t\t</device>\n" +
                "\t</sender>\n" +
                "\t<controlActProcess classCode=\"CACT\" moodCode=\"EVN\">\n" +
                "\t\t<code code=\"empiIndexQuery\">\n" +
                "\t\t\t<displayName value=\"交叉索引查询\"/>\n" +
                "\t\t</code>\n" +
                "\t\t<queryByParameter>\n" +
                "\t\t\t<!-- 查询条件：type：类型 value：值 -->\n" +
                "\t\t\t<!--type 1:患者id 2:身份证 3::电子健康码-->\n" +
                "\t\t\t<queryParam type=\"1\" value=\"" + sDryIndex + "\"/>\n" +
                "\t\t\t<!--1:门诊 3:住院-->\n" +
                "\t\t\t<visitType type=\"" + type + "\"/>\n" +
                "\t\t</queryByParameter>\n" +
                "\t</controlActProcess>\n" +
                "</PRPA_HIP1179>\n";
    }


    private String sendPost(String url, String body) {
        HttpRequest post = HttpUtil.createPost(url);
        post.timeout(60 * 1000);
        post.header("Content-Type", "application/json; charset=utf-8");
        post.body(body);
        HttpResponse response = post.execute();
        return response.body();
    }
}
