package com.yihu.hos.standard.service.standard;

import com.fasterxml.jackson.databind.JsonNode;
import com.yihu.hos.web.framework.dao.SQLGeneralDAO;
import com.yihu.hos.web.framework.constant.ErrorCode;
import com.yihu.hos.web.framework.constant.SqlConstants;
import com.yihu.hos.web.framework.exception.ApiException;
import com.yihu.hos.web.framework.model.DetailModelResult;
import com.yihu.hos.web.framework.model.Result;
import com.yihu.hos.core.datatype.CollectionUtil;
import com.yihu.hos.core.datatype.StringUtil;
import com.yihu.hos.web.framework.util.sql.BeanTransformer;
import com.yihu.hos.web.framework.util.sql.RequestParamTransformer;
import com.yihu.hos.web.framework.util.sql.SqlCreator;
import com.yihu.hos.standard.model.Select2;
import com.yihu.hos.standard.model.standard.*;
import com.yihu.hos.standard.model.standard.resultModel.StandardParamResultModel;
import com.yihu.hos.standard.service.bo.StandardVersion;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 数据元管理接口实现。
 *
 * @author Sand
 * @version 1.0
 * @created 2015.07.10 15:22
 */
@Transactional
@Service("StdMetadataService")
public class StdMetadataService extends SQLGeneralDAO {

    public static final String BEAN_ID = "StdMetadataService";
    @Autowired
    StandardService standardService;

    public StdMetadataService() {
    }

    public List<StdMetaDataModel> getList(String stdVersion, Integer datasetId, String condition, String order, Integer limit, Integer offset) {

        try {
            Map<String, Object> queryMap = RequestParamTransformer.parseJsonToMap(condition);
            Map<String, String> orderMap = RequestParamTransformer.parseJsonToMap(order);
            StandardVersion standardVersion = new StandardVersion(stdVersion);
            return this.getList(standardVersion, datasetId, queryMap, orderMap, limit, offset);
        } catch (Exception e) {
            e.printStackTrace();
        }

        throw new ApiException(ErrorCode.StandardMetaDataGetListFailed);
    }

    public List<StdMetaDataModel> getList(String schemaName, String stdVersion, Integer datasetId, String condition, String order, Integer limit, Integer offset) {

        try {
            Map<String, Object> queryMap = RequestParamTransformer.parseJsonToMap(condition);
            Map<String, String> orderMap = RequestParamTransformer.parseJsonToMap(order);
            StandardVersion standardVersion = new StandardVersion(stdVersion);
            return this.getList(schemaName,standardVersion, datasetId, queryMap, orderMap, limit, offset);
        } catch (Exception e) {
            e.printStackTrace();
        }

        throw new ApiException(ErrorCode.StandardMetaDataGetListFailed);
    }

    public Result add(String stdVersion, String metadata) {

        Boolean existFlg = false;
        try {
            StandardVersion standardVersion = new StandardVersion(stdVersion);
            StdMetaDataModel stdMetaDataModel = objectMapper.readValue(metadata, StdMetaDataModel.class);
            existFlg = metadataCodeValidate(standardVersion, stdMetaDataModel.getDatasetId(), stdMetaDataModel.getCode());
            if (!existFlg) {
                this.add(standardVersion, stdMetaDataModel);
                stdMetaDataModel.setMessage("保存数据元成功");
                return stdMetaDataModel;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (existFlg) {
            return Result.error("已存在标准数据元编码");
        } else {
            return Result.error("保存数据元失败");
        }
    }

    public Boolean metadataCodeValidate(StandardVersion standardVersion, Integer datasetId, String code) {
        Map<String, Object> query = new HashMap<>();
        Map<String, String> order = new HashMap<>();
        query.put("code", code);

        List<StdMetaDataModel> list = this.getList(standardVersion, datasetId, query, order, 1, 1);
        if (!CollectionUtil.isEmpty(list)) {
            return true;
        }
        return false;
    }

    public Result delete(String stdVersion, Integer metadataId) {
        try {
            StandardVersion standardVersion = new StandardVersion(stdVersion);
            if (metadataId != null) {
                this.delete(standardVersion, metadataId);
                return Result.success("删除数据元成功");
            }
        } catch (Exception e) {
            throw new ApiException(ErrorCode.StandardMetaDataDeleteFailed);
        }
        return Result.error("删除数据元失败");
    }

    public Boolean metadataCodeValidate(StandardVersion standardVersion, Integer datasetId, Integer metadataId, String code) {
        SqlCreator sqlCreator = new SqlCreator(StdMetaDataModel.class);
        sqlCreator.equalCondition("datasetId", datasetId);
        sqlCreator.equalCondition("code", code);
        sqlCreator.neCondition("id", metadataId);
        String sql = sqlCreator.selectData(standardVersion.getMetaDataTableName());
        Query sessionQuery = getQuery(sqlCreator, sql);
        List<StdMetaDataModel> list = sessionQuery.list();
        if (!CollectionUtil.isEmpty(list)) {
            return true;
        }
        return false;
    }

    public Result update(String stdVersion, String metadata) {
        Boolean existFlg = false;
        try {
            StandardVersion standardVersion = new StandardVersion(stdVersion);
            StdMetaDataModel stdMetaDataModel = objectMapper.readValue(metadata, StdMetaDataModel.class);
            existFlg = metadataCodeValidate(standardVersion, stdMetaDataModel.getDatasetId(), stdMetaDataModel.getId(), stdMetaDataModel.getCode());
            if (!existFlg) {
                this.modify(standardVersion, stdMetaDataModel);
                stdMetaDataModel.setSuccessFlg(true);
                stdMetaDataModel.setMessage("修改数据元成功");
                return stdMetaDataModel;
            } else {
                return Result.error("已存在标准数据元编码");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return Result.error("修改数据元失败");
    }

    public Result get(String stdVersion, Integer metadataId) {
        try {
            StandardVersion standardVersion = new StandardVersion(stdVersion);
            if (metadataId != null) {
                String metaDataTableName = standardVersion.getMetaDataTableName();
                SqlCreator sqlCreator = new SqlCreator(StdMetaDataModel.class);
                sqlCreator.equalCondition("id", metadataId);
                String sql = sqlCreator.selectData(metaDataTableName);
                Query query = getQuery(sqlCreator, sql);
                StdMetaDataModel stdMetaDataModel = (StdMetaDataModel) query.uniqueResult();
                stdMetaDataModel.setSuccessFlg(true);
                stdMetaDataModel.setMessage("获取标准数据元成功");
                if (stdMetaDataModel != null) {
                    stdMetaDataModel.checkValue();
                }
                return stdMetaDataModel;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return Result.error("获取标准数据元失败");
    }


    private void modify(StandardVersion standardVersion, StdMetaDataModel stdMetaDataModel) throws IOException {
        JsonNode jsonNode = objectMapper.readTree(objectMapper.writeValueAsString(stdMetaDataModel));
        SqlCreator sqlCreator = new SqlCreator(StdMetaDataModel.class);
        String metaDataTableName = standardVersion.getMetaDataTableName();
        String sql = sqlCreator.updateDataByTableKey(metaDataTableName, jsonNode);
        Query query = getExeuteQuery(sqlCreator, sql);
        query.executeUpdate();
    }

    private void delete(StandardVersion standardVersion, Integer metadataId) {
        Session session = getCurrentSession();
        String sql = "delete from " + standardVersion.getMetaDataTableName() + " where id = :id";
        Query query = session.createSQLQuery(sql);
        query.setInteger("id", metadataId);
        query.executeUpdate();
    }

    public void add(StandardVersion standardVersion, StdMetaDataModel stdMetaDataModel) throws IOException {

        Session session = getCurrentSession();
        String metaDataTableName = standardVersion.getMetaDataTableName();
        String sql = "select max(id) from " + metaDataTableName;
        Query query = session.createSQLQuery(sql);
        Object object = query.uniqueResult();
        stdMetaDataModel.setId(object == null ? 1 : Integer.parseInt(object.toString()) + 1);

        JsonNode jsonNode = objectMapper.readTree(objectMapper.writeValueAsString(stdMetaDataModel));
        SqlCreator sqlCreator = new SqlCreator(StdMetaDataModel.class);
        sql = sqlCreator.insertData(metaDataTableName, jsonNode);
        query = getExeuteQuery(sqlCreator, sql);
        query.executeUpdate();
    }

    private List<StdMetaDataModel> getList(StandardVersion standardVersion, Integer datasetId, Map<String, Object> query, Map<String, String> order, Integer limit, Integer offset) {
        SqlCreator sqlCreator = new SqlCreator(StdMetaDataModel.class);
        if(null!=datasetId){
            sqlCreator.equalCondition("datasetId", datasetId);
        }
        for (String key : query.keySet()) {
            sqlCreator.equalCondition(key, query.get(key));
        }
        for (String key : order.keySet()) {
            sqlCreator.order(key, order.get(key));
        }

        String sql = sqlCreator.selectData(standardVersion.getMetaDataTableName());
        Query sessionQuery = getQuery(sqlCreator, sql);
        if (limit != null) {
            sessionQuery.setMaxResults(limit);
            if (offset != null) {
                sessionQuery.setFirstResult((offset - 1) * limit);
            }
        }
        return (List<StdMetaDataModel>) sessionQuery.list();
    }

    private List<StdMetaDataModel> getList(String schemaName,StandardVersion standardVersion, Integer datasetId, Map<String, Object> query, Map<String, String> order, Integer limit, Integer offset) {
        SqlCreator sqlCreator = new SqlCreator(StdMetaDataModel.class);
        if(null!=datasetId){
            sqlCreator.equalCondition("datasetId", datasetId);
        }
        for (String key : query.keySet()) {
            sqlCreator.equalCondition(key, query.get(key));
        }
        for (String key : order.keySet()) {
            sqlCreator.order(key, order.get(key));
        }

        String sql = sqlCreator.selectData(standardVersion.getMetaDataTableName());

        String myCatAnnotation = "/*#mycat\\:schema=" + schemaName + "*/ ";
        String completeSql = myCatAnnotation + sql;

        Query sessionQuery = getQuery(sqlCreator, completeSql);
        if (limit != null) {
            sessionQuery.setMaxResults(limit);
            if (offset != null) {
                sessionQuery.setFirstResult((offset - 1) * limit);
            }
        }
        return (List<StdMetaDataModel>) sessionQuery.list();
    }

    public DetailModelResult getDetailModelList(String stdVersion, Integer dictId, String condition, String order, Integer rows, Integer page) {
        try {
            List<StdDictionaryEntryModel> detailModelList = getMetadataList(StdMetaDataModel.class, stdVersion, dictId, condition, order, rows, page);
            Integer count = getMetadataCount(StdMetaDataModel.class, stdVersion, dictId, condition);
            DetailModelResult result = DetailModelResult.success("获取标准字数据元列表成功");
            result.setDetailModelList(detailModelList);
            result.setTotalCount(count);
            return result;
        } catch (Exception e) {
            return DetailModelResult.error("获取标准数据元列表失败");
        }
    }

    public List getMetadataList(Class tClass, String stdVersion, Integer datasetId, String condition, String order, Integer limit, Integer offset) {
        StandardVersion standardVersion = new StandardVersion(stdVersion);
        String tableName = standardVersion.getMetaDataTableName();
        SqlCreator sqlCreator = new SqlCreator(tClass);
        sqlCreator.equalCondition("datasetId", datasetId);
        if (!StringUtil.isEmpty(condition)) {
            try {
                JsonNode jsonNode = objectMapper.readTree(condition);
                String name = jsonNode.get("name").asText();
                sqlCreator.likeOrCondition("code", "name", name);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        try {
            if (!StringUtil.isEmpty(order)) {
                JsonNode jsonNode = objectMapper.readTree(order);
                Iterator<String> fieldNames = jsonNode.fieldNames();
                while (fieldNames.hasNext()) {
                    String fieldName = fieldNames.next();
                    String value = jsonNode.get(fieldName).asText().toUpperCase();
                    sqlCreator.order(fieldName, value);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        String sql = sqlCreator.selectData(tableName);
        Query query = getQuery(sqlCreator, sql);
        if (limit != null) {
            query.setMaxResults(limit);
            if (offset != null) {
                query.setFirstResult((offset - 1) * limit);
            }
        }

        return query.list();
    }

    public Integer getMetadataCount(Class tClass, String stdVersion, Integer datasetId, String condition) {
        StandardVersion standardVersion = new StandardVersion(stdVersion);
        String tableName = standardVersion.getMetaDataTableName();
        SqlCreator sqlCreator = new SqlCreator(tClass);
        sqlCreator.equalCondition("datasetId", datasetId);
        if (!StringUtil.isEmpty(condition)) {
            try {
                JsonNode jsonNode = objectMapper.readTree(condition);
                String name = jsonNode.get("name").asText();
                sqlCreator.likeOrCondition("code", "name", name);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        String sql = sqlCreator.countData(tableName);
        Query query = getCurrentSession().createSQLQuery(sql);
        for (String key : sqlCreator.getKeyValueMap().keySet()) {
            query.setParameter(key, sqlCreator.getKeyValueMap().get(key));
        }
        Integer count = Integer.parseInt(StringUtil.toString(query.list().get(0)));
        return count;
    }

    public DetailModelResult getMetadataSelectList(String stdVersion, Integer dictId, String condition, String order, Integer rows, Integer page) {
        try {
            List<StdMetaDataModel> metadataList = getMetadataList(StdMetaDataModel.class, stdVersion, dictId, condition, order, rows, page);
            List<Select2> detailModelList = new ArrayList<>();
            for (StdMetaDataModel metaDataModel : metadataList) {
                Select2 select2 = new Select2();
                if (metaDataModel.getDictId() != null) {
                    select2.setId(metaDataModel.getId() + SqlConstants.COMMA + metaDataModel.getCode() + SqlConstants.COMMA + metaDataModel.getDictId());
                } else {
                    select2.setId(metaDataModel.getId() + SqlConstants.COMMA + metaDataModel.getCode());
                }
                select2.setText(metaDataModel.getName());
                detailModelList.add(select2);
            }
            DetailModelResult detailModelResult = DetailModelResult.success("获取标准数据元下拉列表成功");
            detailModelResult.setDetailModelList(detailModelList);
            detailModelResult.setTotalCount(detailModelList.size());
            return detailModelResult;
        } catch (Exception e) {
            return DetailModelResult.error("获取标准数据元下拉列表失败");
        }
    }

    public List getAllVersionMetadataList(String condition) {
        //数据元列表应该是所选择的数据集列表的数据元并集去重 @modify by Yingjie Chen
        try {
            Map<String,List<StdDataSetModel>> version2DataSetMap = new HashMap<>();
            String sql;
            String code = SqlConstants.EMPTY;
            if (!StringUtil.isEmpty(condition)) {
                JsonNode jsonNode = objectMapper.readTree(condition);
                code = jsonNode.get("code").asText();
            }

            Criteria criteria = getCurrentSession().createCriteria(StandardModel.class);
            StandardParamResultModel standardParam = standardService.getStandardParam(SqlConstants.STANDARD);
            String VersionCode = standardParam.getCode();
            criteria.add(Restrictions.eq("code", VersionCode));
            List<StandardModel> standardModelList = criteria.list();
            List<Integer> idList = standardModelList.stream().map(StandardModel::getId).collect(Collectors.toList());
            List<StandardVersionModel> versionModelList;
            Session session = getCurrentSession();
            if (!CollectionUtil.isEmpty(idList)) {
                String hql = "FROM StandardVersionModel WHERE publishTime is not null AND standardId IN :idList order by publishTime";
                Query query = getCurrentSession().createQuery(hql);
                query.setParameterList("idList", idList);
                versionModelList = query.list();
                for (StandardVersionModel versionModel : versionModelList) {
                    SqlCreator sqlDatasetCreator = new SqlCreator(StdDataSetModel.class);
                    StandardVersion version = new StandardVersion(versionModel.getVersion());
                    sqlDatasetCreator.equalCondition("code", code);
                    sql = sqlDatasetCreator.selectData(version.getDataSetTableName());
                    SQLQuery sqlQuery = session.createSQLQuery(sql);
                    BeanTransformer datasetTransformer = new BeanTransformer(StdDataSetModel.class);
                    sqlQuery.setResultTransformer(datasetTransformer);
                    sqlQuery.setParameter("code", code);
                    List<StdDataSetModel> dataList = sqlQuery.list();
                    if (dataList.size() > 0) {
                        version2DataSetMap.put(versionModel.getVersion(),dataList);
                    }
                }
            }

            if (!version2DataSetMap.isEmpty()) {
                sql = SqlConstants.EMPTY;
                Integer count = 1;
                Map<Integer, String> datasetIdMap = new HashMap<>();

                Iterator iter = version2DataSetMap.entrySet().iterator();
                while (iter.hasNext()) {
                    Map.Entry entry = (Map.Entry) iter.next();
                    SqlCreator sqlMetadataCreator = new SqlCreator(StdMetaDataModel.class);
                    StandardVersion version = new StandardVersion((String) entry.getKey());
                    List<StdDataSetModel> dataList = (List<StdDataSetModel>) entry.getValue();
                    datasetIdMap.put(count++, StringUtil.toString(dataList.get(0).getId()));
                    sqlMetadataCreator.equalConditionQue("datasetId", dataList.get(0).getId());
                    sql = sql + sqlMetadataCreator.selectData(version.getMetaDataTableName()) + SqlConstants.UNION;

                }

                BeanTransformer metadataTransformer = new BeanTransformer(StdMetaDataModel.class);
                List<StdMetaDataModel> distinctModelList = new ArrayList<>();
                if (!StringUtil.isEmpty(sql)) {
                    sql = sql.substring(0, sql.length() - SqlConstants.UNION.length());
                    SQLQuery sqlQuery;
                    sqlQuery = getCurrentSession().createSQLQuery(sql);
                    sqlQuery.setResultTransformer(metadataTransformer);
                    for (int i = 1; i < count; i++) {
                        sqlQuery.setParameter(i - 1, datasetIdMap.get(i));
                    }
                    List<StdMetaDataModel> metadataList = sqlQuery.list();
                    Map<String, StdMetaDataModel> meatadataModelMap = new HashMap<>();
                    for (StdMetaDataModel model : metadataList) {
                        meatadataModelMap.put(model.getCode(), model);
                    }
                    distinctModelList.addAll(meatadataModelMap.keySet().stream().map(meatadataModelMap::get).collect(Collectors.toList()));
                }

                return distinctModelList;
            } else {
                return new ArrayList<>();
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new ApiException(ErrorCode.GetDataSetListFailed);
        }
    }


}