package com.yihu.hos.rest.services.crawler;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.yihu.ehr.dbhelper.common.DBList;
import com.yihu.ehr.dbhelper.common.MongodbQuery;
import com.yihu.ehr.dbhelper.common.QueryCondition;
import com.yihu.ehr.dbhelper.common.QueryEntity;
import com.yihu.hos.core.datatype.DateUtil;
import com.yihu.hos.core.datatype.StringUtil;
import com.yihu.hos.core.file.FileUtil;
import com.yihu.hos.core.log.Logger;
import com.yihu.hos.core.log.LoggerFactory;
import com.yihu.hos.rest.common.format.DataSetTransformer;
import com.yihu.hos.rest.models.crawler.adapter.AdapterDataSet;
import com.yihu.hos.rest.models.crawler.adapter.AdapterMetaData;
import com.yihu.hos.rest.models.crawler.config.SysConfig;
import com.yihu.hos.rest.models.crawler.patient.Patient;
import com.yihu.hos.rest.models.crawler.patient.PatientIdentity;
import com.yihu.hos.rest.models.crawler.transform.EhrCondition;
import com.yihu.hos.rest.models.crawler.transform.LogicValues;
import com.yihu.hos.rest.models.standard.adapter.AdapterMetadataModel;
import com.yihu.hos.web.framework.constant.SqlConstants;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.apache.http.NameValuePair;
import org.apache.http.message.BasicNameValuePair;
import org.bson.types.ObjectId;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;


public class DataCollectDispatcher {

    private static DataCollectDispatcher ourInstance = new DataCollectDispatcher();
    private static Logger logger = LoggerFactory.getLogger(DataCollectDispatcher.class);
    private String token;
    private static String unstructured_main = "unstructured_main";
    private static String unstructured_thin = "unstructured_thin";
    private static String gridFsDB = "files";
    private DataCollectDispatcher() {
    }

    public static DataCollectDispatcher getInstance() {
        return ourInstance;
    }

    public void finalize() throws Throwable {

    }

    public Boolean getToken () {
        try {
            token = EsbHttp.getToken();
            if (StringUtil.isEmpty(token)) {
                return false;
            }
            return true;
        } catch (Exception e) {
            logger.error("本次任务执行失败，获取token失败！");
            return false;
        }
    }

    public Boolean getRemoteVersion (String orgCode) {
        try {
            if (StringUtil.isEmpty(SysConfig.getInstance().getVersionMap().get(orgCode))) {
                String stdVersion = EsbHttp.getRemoteVersion(orgCode);
                if (StringUtil.isEmpty(stdVersion)) {
                    return false;
                }
                SysConfig.getInstance().getVersionMap().put(orgCode, stdVersion);
            }
            return true;
        } catch (Exception e) {
            logger.error("本次任务执行失败，获取token失败！");
            return false;
        }
    }

    /**
     * 获取病人列表
     *
     * @param condition
     * @return List<PatientT>
     */
    public List<Patient> getPatientList(Map<String, Object> condition, Map<String, AdapterDataSet> adapterDataSetMap) {

        ArrayList<Patient> patientList = new ArrayList<>();
        SimpleDateFormat df = new SimpleDateFormat(DateUtil.DEFAULT_YMDHMSDATE_FORMAT);
        for (String key : adapterDataSetMap.keySet()) {
            PatientIdentity patientIdentity = SysConfig.getInstance().getPatientIdentity(key);
            if (patientIdentity == null) {
                continue;
            }
            try {
                //获取病人列表字段检验
                Map<String, String> propertyMap = getItemList(adapterDataSetMap.get(key));
                if (propertyMap == null) {
                    return patientList;
                }
                //请求参数
                Date beginDate = (Date) condition.get("beginDate");
                String beginTime = df.format(beginDate);
                Date endDate = (Date) condition.get("endDate");
                String endTime = df.format(endDate);
                List<EhrCondition> queryParams = new ArrayList<>();
                queryParams.add(new EhrCondition(" > ", patientIdentity.getRefTimeCode(), beginTime));
                queryParams.add(new EhrCondition(" < ", patientIdentity.getRefTimeCode(), endTime));

                //Rest 接口请求
                ObjectMapper objectMapper = new ObjectMapper();
                String rootStr = collectionData(adapterDataSetMap.get(key).getAdapterDataSetT().getStdDatasetCode(), objectMapper.writeValueAsString(queryParams));
                if (StringUtil.isEmpty(rootStr)) {
                    return null;
                }
                ObjectMapper mapper = new ObjectMapper();
                JsonNode resultNode = mapper.readValue(rootStr, JsonNode.class);

                JsonNode patientNode = resultNode.path("detailModelList");
                boolean isArr = patientNode.isArray();
                if (isArr) {
                    Iterator<JsonNode> array = patientNode.iterator();
                    while (array.hasNext()) {
                        JsonNode node = array.next();
                        Patient patient = new Patient();
                        String patientId = node.path(propertyMap.get(SqlConstants.PATIENT_ID)).asText();
                        String eventNo = node.path(propertyMap.get(SqlConstants.EVENT_NO)).asText();
                        String refTime = node.path(propertyMap.get(SqlConstants.EVENT_TIME)).asText();
                        String orgCode = node.path(SqlConstants.ORG_CODE.toUpperCase()).asText();
                        patient.setPatientId(patientId);
                        patient.setEventNo(eventNo);
                        patient.setReferenceTime(refTime);
                        patient.setOrgCode(orgCode);
                        patientList.add(patient);
                    }
                }
            } catch (Exception e) {
                logger.error("采集病人失败", e);
            }
        }

        return patientList;
    }

    public Map<String, String> getItemList(AdapterDataSet adapterDataSet) throws Exception {
        Map<String, String> propertyMap = new HashMap<>();
        PatientIdentity patientIdentity = SysConfig.getInstance().getPatientIdentity(adapterDataSet.getAdapterDataSetT().getStdDatasetCode());

        if (adapterDataSet.isHavePatientID()) {
            AdapterMetaData adapterMetaData = adapterDataSet.getAdapterMetaDataMap().get(PatientIdentity.getPatientIDCode());
            propertyMap.put(SqlConstants.PATIENT_ID, adapterMetaData.getAdapterMetadataModel().getStdMetadataCode().toUpperCase());

        } else {
            logger.error("", new Exception("采集病人列表数据集必须有patient_id."));
            return null;
        }

        if (adapterDataSet.isHaveEventNo()) {
            AdapterMetaData adapterMetaData = adapterDataSet.getAdapterMetaDataMap().get(patientIdentity.getEventNoCode());
            propertyMap.put(SqlConstants.EVENT_NO,  adapterMetaData.getAdapterMetadataModel().getStdMetadataCode().toUpperCase());
        } else {
            logger.error("采集病人列表数据集必须有event_no.");
            return null;
        }

        AdapterMetaData adapterRefMetaData = adapterDataSet.getAdapterMetaDataMap().get(patientIdentity.getRefTimeCode());
        if (adapterRefMetaData == null) {
            logger.error("采集病人列表数据集必须有采集时间.");
            return null;
        }

        propertyMap.put(SqlConstants.EVENT_TIME, adapterRefMetaData.getAdapterMetadataModel().getStdMetadataCode().toUpperCase());
        return propertyMap;
    }

    public String fecthData(Patient patient, AdapterDataSet adapterDataSet, List<EhrCondition> queryParams) {
        try {
            ObjectMapper mapper = new ObjectMapper();
            //调用资源服务网关
            String rootStr = collectionData(adapterDataSet.getAdapterDataSetT().getStdDatasetCode(), mapper.writeValueAsString(queryParams));

            JsonNode resultNode = mapper.readValue(rootStr, JsonNode.class);
            JsonNode result = resultNode.path("detailModelList");
            JsonNode data = matchAdapterData(result, adapterDataSet);
            ObjectNode jsonObject = mapper.createObjectNode();
            if (data != null && data.size() > 0) {
                jsonObject.set("data", data);
                jsonObject.put("code", adapterDataSet.getAdapterDataSetT().getStdDatasetCode());
                jsonObject.put("patient_id", patient.getPatientId());
                jsonObject.put("event_no", patient.getEventNo());
                String agencyCode = patient.getOrgCode();
                jsonObject.put("org_code", agencyCode);
                jsonObject.put("inner_version", SysConfig.getInstance().getVersionMap().get(patient.getOrgCode()));
                jsonObject.put("create_date", DateUtil.toString(new Date(), DateUtil.DEFAULT_YMDHMSDATE_FORMAT));
                jsonObject.put("event_time", patient.getReferenceTime());//DateUtil.toString(patient.getReferenceTime(), DateUtil.DEFAULT_YMDHMSDATE_FORMAT)
                if (StringUtil.isEmpty(patient.getReUploadFlg())) {
                    jsonObject.put("reUploadFlg", LogicValues.LOGIC_FALSE);
                } else {
                    jsonObject.put("reUploadFlg", patient.getReUploadFlg());
                }
                return jsonObject.toString();
            } else {
                return SqlConstants.EMPTY;
            }
        } catch (Exception e) {
            return SqlConstants.EMPTY;
        }
    }

    public String fecthData(Patient patient, AdapterDataSet adapterDataSet, Map<String, String> relationValueMap) {
        List<EhrCondition> queryParams = new ArrayList<>();
        for (String key : relationValueMap.keySet()) {
            queryParams.add(new EhrCondition(" = ", key, relationValueMap.get(key)));
        }
        return fecthData(patient, adapterDataSet, queryParams);
    }

    /**
     * 根据编排任务进行采集
     * @param patient
     * @param adapterDataSet
     * @return
     */
    public JsonNode fecthData(Patient patient, AdapterDataSet adapterDataSet) {
        try {
            ObjectMapper mapper = new ObjectMapper();
            List<EhrCondition> queryParams = new ArrayList<>();
            boolean patientId = true;
            if (adapterDataSet.isHavePatientID()) {
                AdapterMetaData adapterMetaData = adapterDataSet.getAdapterMetaDataMap().get(PatientIdentity.getPatientIDCode());
                queryParams.add(new EhrCondition(" = ", adapterMetaData.getAdapterMetadataModel().getStdMetadataCode(), patient.getPatientId()));
            } else {
                patientId = false;
            }

            boolean eventNo = true;
            if (adapterDataSet.isHaveEventNo()) {
                AdapterMetaData adapterMetaData = adapterDataSet.getAdapterMetaDataMap().get(adapterDataSet.getEventNoCode());
                queryParams.add(new EhrCondition(" = ", adapterMetaData.getAdapterMetadataModel().getStdMetadataCode(), patient.getEventNo()));
            } else {
                eventNo = false;
            }

            if (!patientId && !eventNo) {
                logger.error("采集病人数据集至少需要一项病人标识.数据集名:" + adapterDataSet.getAdapterDataSetT().getStdDatasetCode());
                return null;
            }

            ObjectNode paramsNode = mapper.createObjectNode();
            paramsNode.put("tableCode", adapterDataSet.getAdapterDataSetT().getStdDatasetCode());
            paramsNode.put("condition", mapper.writeValueAsString(queryParams));

            List<NameValuePair> formParams = new ArrayList<>();
//            formParams.add(new BasicNameValuePair("secret", secret));
            formParams.add(new BasicNameValuePair("api", "collectionData"));
            formParams.add(new BasicNameValuePair("param", mapper.writeValueAsString(paramsNode)));
            //调用资源服务网关
            String rootStr = collectionData(adapterDataSet.getAdapterDataSetT().getStdDatasetCode(), mapper.writeValueAsString(queryParams));

            JsonNode resultNode = mapper.readValue(rootStr, JsonNode.class);
            JsonNode result = resultNode.path("detailModelList");
            JsonNode data = matchAdapterData(result, adapterDataSet);
            ObjectNode jsonObject = mapper.createObjectNode();
            if (data != null && data.size() > 0) {
                jsonObject.set("data", data);
                jsonObject.put("code", adapterDataSet.getAdapterDataSetT().getStdDatasetCode());
                jsonObject.put("patient_id", patient.getPatientId());
                jsonObject.put("event_no", patient.getEventNo());
                String agencyCode = patient.getOrgCode();
                jsonObject.put("org_code", agencyCode);
                jsonObject.put("inner_version", SysConfig.getInstance().getVersionMap().get(patient.getOrgCode()));
                jsonObject.put("create_date", DateUtil.toString(new Date(), DateUtil.DEFAULT_YMDHMSDATE_FORMAT));
                jsonObject.put("event_time", patient.getReferenceTime());//DateUtil.toString(patient.getReferenceTime(), DateUtil.DEFAULT_YMDHMSDATE_FORMAT)
                if (StringUtil.isEmpty(patient.getReUploadFlg())) {
                    jsonObject.put("reUploadFlg", LogicValues.LOGIC_FALSE);
                } else {
                    jsonObject.put("reUploadFlg", patient.getReUploadFlg());
                }
                return jsonObject;
            } else {
                return null;
            }

        } catch (Exception e) {
            logger.error("", e);
        }

        return null;
    }

    public JsonNode matchAdapterData(JsonNode data, AdapterDataSet adapterDataSet) {
        ObjectMapper mapper = new ObjectMapper();
        ArrayNode result = mapper.createArrayNode();
        Iterator<JsonNode> array = data.iterator();
        while (array.hasNext()) {
            JsonNode dataNode = array.next();
            ObjectNode jsonNode = mapper.createObjectNode();
            for (AdapterMetaData adapterMetaData : adapterDataSet.getAdapterMetaDataList()) {
                AdapterMetadataModel adapterMetadataModel = adapterMetaData.getAdapterMetadataModel();
                String orgMetaDataCode = adapterMetadataModel.getAdapterMetadataCode();
                String stdMetaDataCode = adapterMetadataModel.getStdMetadataCode();
                if (!StringUtil.isEmpty(orgMetaDataCode)) {
                    jsonNode.put(orgMetaDataCode, dataNode.path(stdMetaDataCode).asText());
                }
            }
            result.add(jsonNode);
        }
        return result;
    }

    public Boolean register(Patient patient, String data) {
        return EsbHttp.register(patient, data, token);
    }

    public Boolean upload(Map<String, JsonNode> dataMap, Patient patient, Map<String, AdapterDataSet> dataSetMap) {
        Boolean result = true;
        try {
            DataSetTransformer dataTransformer = new DataSetTransformer();
            for (String key : dataMap.keySet()) {
                dataTransformer.setData(dataMap.get(key));
                if (!toFile(dataTransformer, patient, "origin")) {
                    logger.info("存储原始文件失败:patient_id=" + patient.getPatientId()
                            + "event_no=" + patient.getEventNo());
                    result = false;
                    break;
                }
                dataTransformer.transfer(dataSetMap);
                if (!toFile(dataTransformer, patient, "standard")) {
                    logger.info("存储标准文件失败:patient_id=" + patient.getPatientId()
                            + "event_no=" + patient.getEventNo());
                    result = false;
                    break;
                }
            }
            PatientCDAUpload patientCDAUpload = new PatientCDAUpload();
            if (!patientCDAUpload.upload(patient, token)) {
                result = false;
            }
        } catch (Exception e) {
            result = false;
        }
        return result;
    }

    public boolean toFile(DataSetTransformer dataTransformer, Patient patient, String fileName) {
        JsonNode jsonObject = dataTransformer.getJsonObject();
        PatientCDAIndex patientCDAIndex = new PatientCDAIndex(patient);
        String filePath = patientCDAIndex.createDataIndex(fileName, PatientCDAIndex.FileType.JSON);
        boolean writeFile = false;
        try {
            writeFile = FileUtil.writeFile(filePath, jsonObject.toString(), "UTF-8");
        } catch (IOException e) {
            logger.info("存储临时文件失败.");
            logger.error("", e);
        }
        return writeFile;
    }

    /**
     * 解析token内容
     *
     * @param responToken
     * @return
     */
    public Map<String, Object> parseToken(String responToken) {
        ObjectMapper mapper = new ObjectMapper();
        Map<String, Object> tokenMap = null;
        try {
            Map<String, Object> map = mapper.readValue(responToken, Map.class);
            String code = (String) map.get("code");
            if (SqlConstants.OK.equals(code)) {
                tokenMap = (Map<String, Object>) map.get("result");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return tokenMap;
    }

    public String collectionData(String tableCode, String condition) throws Exception {
        try {
            MongodbQuery mdb = new MongodbQuery();
            QueryEntity qe = new QueryEntity(tableCode);

            //[{andOr:"and",field:"code",condition:"=",value:"111"}]

            //设置参数
            if (!StringUtils.isEmpty(condition) && !"{}".equals(condition)) {
                JSONArray ar = JSONArray.fromObject(condition);
                for (int i = 0; i < ar.size(); i++) {
                    JSONObject jo = (JSONObject) ar.get(i);
                    String andOr = String.valueOf(jo.get("andOr"));
                    String field = String.valueOf(jo.get("field"));
                    String cond = String.valueOf(jo.get("condition"));
                    String value = String.valueOf(jo.get("value"));
                    qe.addCondition(new QueryCondition(andOr, cond, field, value));
                }
            }
            DBList list = mdb.query(qe);
            MongodbQuery mdbSecond = new MongodbQuery("origin");
            DBList listSecond = mdbSecond.query(qe);

            JSONObject jo=new JSONObject();
            jo.put("detailModelList", list.getList().toString());
            jo.put("originList",listSecond.getList().toString());
            return jo.toString();
        } catch (Exception e) {
            return "";
        }
    }

    /* **********************************   add in 2016/11/15 ********************************************* */

    //TOADD 数据采集（包含blob）
    public JsonNode fecthData2(Patient patient, AdapterDataSet adapterDataSet) {
        try {
            ObjectMapper mapper = new ObjectMapper();
            List<EhrCondition> queryParams = new ArrayList<>();
            boolean patientId = true;
            if (adapterDataSet.isHavePatientID()) {
                AdapterMetaData adapterMetaData = adapterDataSet.getAdapterMetaDataMap().get(PatientIdentity.getPatientIDCode());
                queryParams.add(new EhrCondition(" = ", adapterMetaData.getAdapterMetadataModel().getStdMetadataCode(), patient.getPatientId()));
            } else {
                patientId = false;
            }

            boolean eventNo = true;
            if (adapterDataSet.isHaveEventNo()) {
                AdapterMetaData adapterMetaData = adapterDataSet.getAdapterMetaDataMap().get(adapterDataSet.getEventNoCode());
                queryParams.add(new EhrCondition(" = ", adapterMetaData.getAdapterMetadataModel().getStdMetadataCode(), patient.getEventNo()));
            } else {
                eventNo = false;
            }

            if (!patientId && !eventNo) {
                logger.error("采集病人数据集至少需要一项病人标识.数据集名:" + adapterDataSet.getAdapterDataSetT().getStdDatasetCode());
                return null;
            }

            ObjectNode paramsNode = mapper.createObjectNode();
            paramsNode.put("tableCode", adapterDataSet.getAdapterDataSetT().getStdDatasetCode());
            paramsNode.put("condition", mapper.writeValueAsString(queryParams));

            Map<String,String> formParams = new HashMap<>();
//            formParams.add(new BasicNameValuePair("secret", secret));
            formParams.put("api", "collectionData");
            formParams.put("param", mapper.writeValueAsString(paramsNode));
            //调用资源服务网关
            String rootStr = EsbHttp.getFecthData(formParams);

            JsonNode resultNode = mapper.readValue(rootStr, JsonNode.class);
            JsonNode result = resultNode.path("detailModelList");
            JsonNode data = matchAdapterData(result, adapterDataSet);
            ObjectNode jsonObject = mapper.createObjectNode();
            if (data != null && data.size() > 0) {
                jsonObject.set("data", data);
                jsonObject.put("code", adapterDataSet.getAdapterDataSetT().getStdDatasetCode());
                jsonObject.put("patient_id", patient.getPatientId());
                jsonObject.put("event_no", patient.getEventNo());
                String agencyCode = patient.getOrgCode();
                jsonObject.put("org_code", agencyCode);
                jsonObject.put("inner_version", SysConfig.getInstance().getVersionMap().get(patient.getOrgCode()));
                jsonObject.put("create_date", DateUtil.toString(new Date(), DateUtil.DEFAULT_YMDHMSDATE_FORMAT));
                jsonObject.put("event_time", patient.getReferenceTime());//DateUtil.toString(patient.getReferenceTime(), DateUtil.DEFAULT_YMDHMSDATE_FORMAT)
                if (StringUtil.isEmpty(patient.getReUploadFlg())) {
                    jsonObject.put("reUploadFlg", LogicValues.LOGIC_FALSE);
                } else {
                    jsonObject.put("reUploadFlg", patient.getReUploadFlg());
                }
                return jsonObject;
            } else {
                return null;
            }

        } catch (Exception e) {
            logger.error("", e);
        }

        return null;
    }

    /**
     * 生成非结构化索引文件document.json
     * @param dataTransformer
     * @param patient
     * @param fileName
     * @return
     */
    public boolean toDoucmentFile(DataSetTransformer dataTransformer, Patient patient, String fileName) {
        JsonNode jsonObject = dataTransformer.getJsonObject();
        PatientCDAIndex patientCDAIndex = new PatientCDAIndex(patient);
        String filePath = patientCDAIndex.createDataIndex(fileName, PatientCDAIndex.FileType.JSON);
        boolean writeFile = false;
        try {
            writeFile = FileUtil.writeFile(filePath, jsonObject.toString(), "UTF-8");
        } catch (IOException e) {
            logger.info("存储临时文件失败.");
            logger.error("", e);
        }
        return writeFile;
    }

    public List<ObjectId> getObjectIds(JsonNode jsonNode){
        List<ObjectId> objectIds = new ArrayList<>();
        if (jsonNode.isArray()){
            for (JsonNode data:jsonNode){
                objectIds.add(new ObjectId(data.get("CONTENT").toString()));
            }
        }
        return objectIds;
    }
}//end DataCollectDispatcher