package com.purui.prims.headless.server.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.purui.prims.auth.api.authentication.pojo.User;
import com.purui.prims.common.jsqlparser.SqlAddHelper;
import com.purui.prims.common.jsqlparser.SqlSelectHelper;
import com.purui.prims.common.pojo.Aggregator;
import com.purui.prims.common.pojo.DateConf;
import com.purui.prims.common.pojo.QueryColumn;
import com.purui.prims.common.pojo.enums.AggOperatorEnum;
import com.purui.prims.common.pojo.enums.FilterOperatorEnum;
import com.purui.prims.common.pojo.enums.QueryType;
import com.purui.prims.common.pojo.enums.TaskStatusEnum;
import com.purui.prims.common.pojo.enums.TimeDimensionEnum;
import com.purui.prims.common.util.ContextUtils;
import com.purui.prims.headless.api.pojo.DataInfo;
import com.purui.prims.headless.api.pojo.DataSetInfo;
import com.purui.prims.headless.api.pojo.DataSetSchema;
import com.purui.prims.headless.api.pojo.Dim;
import com.purui.prims.headless.api.pojo.EntityInfo;
import com.purui.prims.headless.api.pojo.QueryParam;
import com.purui.prims.headless.api.pojo.SchemaElement;
import com.purui.prims.headless.api.pojo.SemanticParseInfo;
import com.purui.prims.headless.api.pojo.TimeDefaultConfig;
import com.purui.prims.headless.api.pojo.enums.MetricType;
import com.purui.prims.headless.api.pojo.enums.SemanticType;
import com.purui.prims.headless.api.pojo.request.DimensionValueReq;
import com.purui.prims.headless.api.pojo.request.ExplainSqlReq;
import com.purui.prims.headless.api.pojo.request.QueryFilter;
import com.purui.prims.headless.api.pojo.request.QueryMetricReq;
import com.purui.prims.headless.api.pojo.request.QueryMultiSqlReq;
import com.purui.prims.headless.api.pojo.request.QueryMultiStructReq;
import com.purui.prims.headless.api.pojo.request.QuerySqlReq;
import com.purui.prims.headless.api.pojo.request.QueryStructReq;
import com.purui.prims.headless.api.pojo.request.SchemaFilterReq;
import com.purui.prims.headless.api.pojo.request.SemanticQueryReq;
import com.purui.prims.headless.api.pojo.response.DimensionResp;
import com.purui.prims.headless.api.pojo.response.ExplainResp;
import com.purui.prims.headless.api.pojo.response.ItemResp;
import com.purui.prims.headless.api.pojo.response.MetricResp;
import com.purui.prims.headless.api.pojo.response.ModelResp;
import com.purui.prims.headless.api.pojo.response.SemanticQueryResp;
import com.purui.prims.headless.api.pojo.response.SemanticSchemaResp;
import com.purui.prims.headless.chat.knowledge.HanlpMapResult;
import com.purui.prims.headless.chat.knowledge.KnowledgeBaseService;
import com.purui.prims.headless.chat.knowledge.MapResult;
import com.purui.prims.headless.chat.knowledge.SearchService;
import com.purui.prims.headless.chat.knowledge.helper.HanlpHelper;
import com.purui.prims.headless.chat.knowledge.helper.NatureHelper;
import com.purui.prims.headless.chat.utils.QueryReqBuilder;
import com.purui.prims.headless.core.cache.QueryCache;
import com.purui.prims.headless.core.executor.QueryExecutor;
import com.purui.prims.headless.core.pojo.QueryStatement;
import com.purui.prims.headless.core.translator.SemanticTranslator;
import com.purui.prims.headless.core.translator.calcite.s2sql.SemanticModel;
import com.purui.prims.headless.core.utils.ComponentFactory;
import com.purui.prims.headless.server.annotation.S2DataPermission;
import com.purui.prims.headless.server.aspect.MetricDrillDownChecker;
import com.purui.prims.headless.server.manager.SemanticSchemaManager;
import com.purui.prims.headless.server.service.DataSetService;
import com.purui.prims.headless.server.service.MetricService;
import com.purui.prims.headless.server.service.SchemaService;
import com.purui.prims.headless.server.service.SemanticLayerService;
import com.purui.prims.headless.server.utils.QueryReqConverter;
import com.purui.prims.headless.server.utils.QueryUtils;
import com.purui.prims.headless.server.utils.StatUtils;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.Alias;
import net.sf.jsqlparser.expression.Function;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.statement.select.SelectItem;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;


@Service
@Slf4j
public class S2SemanticLayerService implements SemanticLayerService {

    private StatUtils statUtils;
    private final QueryUtils queryUtils;
    private final QueryReqConverter queryReqConverter;
    private final SemanticSchemaManager semanticSchemaManager;
    private final DataSetService dataSetService;
    private final MetricService metricService;
    private final SchemaService schemaService;
    private final SemanticTranslator semanticTranslator;
    private final MetricDrillDownChecker metricDrillDownChecker;
    private final KnowledgeBaseService knowledgeBaseService;

    public S2SemanticLayerService(StatUtils statUtils, QueryUtils queryUtils, QueryReqConverter queryReqConverter,
                                  SemanticSchemaManager semanticSchemaManager, DataSetService dataSetService,
                                  SchemaService schemaService, MetricService metricService,
                                  SemanticTranslator semanticTranslator, MetricDrillDownChecker metricDrillDownChecker,
                                  KnowledgeBaseService knowledgeBaseService) {
        this.statUtils = statUtils;
        this.queryUtils = queryUtils;
        this.queryReqConverter = queryReqConverter;
        this.semanticSchemaManager = semanticSchemaManager;
        this.dataSetService = dataSetService;
        this.metricService = metricService;
        this.schemaService = schemaService;
        this.semanticTranslator = semanticTranslator;
        this.metricDrillDownChecker = metricDrillDownChecker;
        this.knowledgeBaseService = knowledgeBaseService;
    }

    public DataSetSchema getDataSetSchema(Long id) {
        return schemaService.getDataSetSchema(id);
    }

    @Override
    @S2DataPermission
    @SneakyThrows
    public SemanticQueryResp queryByReq(SemanticQueryReq queryReq, User user) {
        TaskStatusEnum state = TaskStatusEnum.SUCCESS;
        log.info("[queryReq:{}]", queryReq);
        try {
            //1.initStatInfo
            statUtils.initStatInfo(queryReq, user);
            //2.query from cache
            QueryCache queryCache = ComponentFactory.getQueryCache();
            String cacheKey = queryCache.getCacheKey(queryReq);
            log.info("cacheKey:{}", cacheKey);
            Object query = queryCache.query(queryReq, cacheKey);
            if (Objects.nonNull(query)) {
                SemanticQueryResp queryResp = (SemanticQueryResp) query;
                queryResp.setUseCache(true);
                return queryResp;
            }
            StatUtils.get().setUseResultCache(false);
            //3 query
            QueryStatement queryStatement = buildQueryStatement(queryReq, user);
            SemanticQueryResp result = query(queryStatement);
            //4 reset cache and set stateInfo
            Boolean setCacheSuccess = queryCache.put(cacheKey, result);
            if (setCacheSuccess) {
                // if result is not null, update cache data
                statUtils.updateResultCacheKey(cacheKey);
            }
            if (Objects.isNull(result)) {
                state = TaskStatusEnum.ERROR;
            }
            log.info(result.getSql());
            // clear sql
            result.setSql(null);
            return result;
        } catch (Exception e) {
            log.error("exception in queryByStruct, e: ", e);
            state = TaskStatusEnum.ERROR;
            throw e;
        } finally {
            statUtils.statInfo2DbAsync(state);
        }
    }

    @Override
    public SemanticQueryResp queryByMetric(QueryMetricReq queryMetricReq, User user) throws Exception {
        QueryStructReq queryStructReq = metricService.convert(queryMetricReq);

        SemanticQueryReq queryReq = buildQueryReq(queryStructReq);

        SemanticQueryResp semanticQueryResp = this.queryByReq(queryReq, user);
        return semanticQueryResp;
    }

    public SemanticQueryReq buildQueryReq(QueryStructReq queryStructReq) throws Exception {
        boolean isMultiSql = false;
        String compositeExpr = null;
        String metricName = null;
        // 原始sql
        QuerySqlReq querySqlReq = queryStructReq.convert(true);
        String outputSql = querySqlReq.getSql();
        // 先判断指标是否是复合指标，如果是复合指标则采用multi sql方式
        if (queryStructReq.getMetricIds().size() > 0) {
            MetricResp metricResp = metricService.getMetric(queryStructReq.getMetricIds().get(0));
            if (metricResp != null && metricResp.getType().equalsIgnoreCase(MetricType.COMPOSITE.name())) {
                List<Aggregator> aggregators = new ArrayList<>();
                metricResp.getMetricDefineByMetricParams().getMetrics().forEach(m -> {
                    MetricResp metric = metricService.getMetric(m.getId());
                    Aggregator aggregator = new Aggregator();
                    aggregator.setFunc(AggOperatorEnum.valueOf(metric.getDefaultAgg()));
                    aggregator.setColumn(m.getBizName());
                    aggregators.add(aggregator);
                });
                metricName = metricResp.getBizName();
                compositeExpr = "(" + metricResp.getMetricDefineByMetricParams().getExpr() + ")";
                queryStructReq.setAggregators(aggregators);
                isMultiSql = true;
            }
        }

        // 复合指标查询
        if (isMultiSql && StringUtils.isNotEmpty(compositeExpr)) {
            // 修正后的sql
            querySqlReq = queryStructReq.convert(true);
            QueryReqConverter queryReqConverter = ContextUtils.getBean(QueryReqConverter.class);
            QueryMultiSqlReq queryMultiSqlReq = queryReqConverter.buildMultiStructReq(querySqlReq.getSql(), queryStructReq.getModelIdSet());

            // 自动增加时间戳
            if (queryStructReq.isAutoAddTimestamp()) {
                Function timeFunction = new Function();
                timeFunction.setName("NOW");
                timeFunction.setParameters(new ExpressionList());
                SelectItem nowItem = new SelectItem(timeFunction);
                nowItem.setAlias(new Alias("update_time"));
                outputSql = SqlAddHelper.addFieldsToSelect(outputSql, nowItem);
            }
            String tableName = SqlSelectHelper.getTableName(outputSql);
            outputSql = outputSql.replace("(" + metricName + ")", compositeExpr).replace(tableName, "(%s) t");
            queryMultiSqlReq.setOutputSql(outputSql);
            return queryMultiSqlReq;
        }

        return querySqlReq;
    }

    private QueryStatement buildSqlQueryStatement(QuerySqlReq querySqlReq, User user) throws Exception {
        // 换datasetId
        if (querySqlReq.needGetDataSetId()) {
            Long dataSetId = dataSetService.getDataSetIdFromSql(querySqlReq.getSql(), user);
            querySqlReq.setDataSetId(dataSetId);
        }
        SchemaFilterReq filter = buildSchemaFilterReq(querySqlReq);
        SemanticSchemaResp semanticSchemaResp = schemaService.fetchSemanticSchema(filter);
        QueryStatement queryStatement = queryReqConverter.convert(querySqlReq, semanticSchemaResp);
        queryStatement.setModelIds(querySqlReq.getModelIds());
        queryStatement.setEnableOptimize(queryUtils.enableOptimize());
        queryStatement.setSemanticSchemaResp(semanticSchemaResp);
        queryStatement.setSemanticModel(semanticSchemaManager.getSemanticModel(semanticSchemaResp));
        return queryStatement;
    }

    public QueryStatement buildQueryStatement(SemanticQueryReq semanticQueryReq, User user) throws Exception {
        QueryStatement queryStatement = null;
        if (semanticQueryReq instanceof QuerySqlReq) {
            queryStatement = buildSqlQueryStatement((QuerySqlReq) semanticQueryReq, user);
        }
        if (semanticQueryReq instanceof QueryStructReq) {
            queryStatement = buildStructQueryStatement((QueryStructReq) semanticQueryReq);
        }
        if (semanticQueryReq instanceof QueryMultiStructReq) {
            queryStatement = buildMultiStructQueryStatement((QueryMultiStructReq) semanticQueryReq, user);
        }
        if (semanticQueryReq instanceof QueryMultiSqlReq) {
            queryStatement = buildMultiSqlQueryStatement((QueryMultiSqlReq) semanticQueryReq, user);
        }
        if (Objects.nonNull(queryStatement) && Objects.nonNull(semanticQueryReq.getSqlInfo()) && StringUtils.isNotBlank(
            semanticQueryReq.getSqlInfo().getQuerySQL())) {
            queryStatement.setSql(semanticQueryReq.getSqlInfo().getQuerySQL());
            queryStatement.setSourceId(semanticQueryReq.getSqlInfo().getSourceId());
            queryStatement.setDataSetId(semanticQueryReq.getDataSetId());
            queryStatement.setIsTranslated(true);
        }
        return queryStatement;
    }

    @Override
    public String buildSql(SemanticQueryReq semanticQueryReq, User user) {
        try {
            QueryStatement queryStatement = buildQueryStatement(semanticQueryReq, user);
            if (!queryStatement.isTranslated()) {
                semanticTranslator.translate(queryStatement);
            }
            return queryStatement.getSql();
        } catch (Exception e) {
            log.info("exception in buildSql, e: ", e);
        }
        return null;
    }

    private QueryStatement buildStructQueryStatement(QueryStructReq queryStructReq) {
        SchemaFilterReq filter = buildSchemaFilterReq(queryStructReq);
        SemanticSchemaResp semanticSchemaResp = schemaService.fetchSemanticSchema(filter);
        QueryStatement queryStatement = new QueryStatement();
        QueryParam queryParam = new QueryParam();
        queryReqConverter.convert(queryStructReq, queryParam);
        queryStatement.setQueryParam(queryParam);
        queryStatement.setIsS2SQL(false);
        queryStatement.setEnableOptimize(queryUtils.enableOptimize());
        queryStatement.setDataSetId(queryStructReq.getDataSetId());
        queryStatement.setSemanticSchemaResp(semanticSchemaResp);
        queryStatement.setSemanticModel(semanticSchemaManager.getSemanticModel(semanticSchemaResp));
        return queryStatement;
    }

    private QueryStatement buildMultiStructQueryStatement(QueryMultiStructReq queryMultiStructReq, User user) throws Exception {
        List<QueryStatement> sqlParsers = new ArrayList<>();
        for (QueryStructReq queryStructReq : queryMultiStructReq.getQueryStructReqs()) {
            QueryStatement queryStatement = buildQueryStatement(queryStructReq, user);
            SemanticModel semanticModel = queryStatement.getSemanticModel();
            queryStatement.setModelIds(queryStructReq.getModelIds());
            queryStatement.setSemanticModel(semanticModel);
            queryStatement.setEnableOptimize(queryUtils.enableOptimize());
            semanticTranslator.translate(queryStatement);
            sqlParsers.add(queryStatement);
        }
        log.info("multi sqlParser:{}", sqlParsers);
        return queryUtils.sqlParserUnion(queryMultiStructReq, sqlParsers);
    }

    private QueryStatement buildMultiSqlQueryStatement(QueryMultiSqlReq queryMultiSqlReq, User user) throws Exception {
        List<QueryStatement> sqlParsers = new ArrayList<>();
        for (QuerySqlReq sqlReq : queryMultiSqlReq.getQuerySqlReqs()) {
            QueryStatement queryStatement = buildQueryStatement(sqlReq, user);
            queryStatement.setModelIds(sqlReq.getModelIds());
            semanticTranslator.translate(queryStatement);
            sqlParsers.add(queryStatement);
        }
        return queryUtils.sqlParserLeftJoin(sqlParsers, queryMultiSqlReq.getOutputSql());
    }

    private SchemaFilterReq buildSchemaFilterReq(SemanticQueryReq semanticQueryReq) {
        SchemaFilterReq schemaFilterReq = new SchemaFilterReq();
        schemaFilterReq.setDataSetId(semanticQueryReq.getDataSetId());
        schemaFilterReq.setModelIds(semanticQueryReq.getModelIds());
        return schemaFilterReq;
    }

    @Override
    @SneakyThrows
    public SemanticQueryResp queryDimensionValue(DimensionValueReq dimensionValueReq, User user) {
        SemanticQueryResp semanticQueryResp = new SemanticQueryResp();
        DimensionResp dimensionResp = getDimension(dimensionValueReq);
        Set<Long> dataSetIds = dimensionValueReq.getDataSetIds();
        dimensionValueReq.setModelId(dimensionResp.getModelId());
        // 从缓存中加载
        List<String> dimensionValues = getDimensionValuesFromDict(dimensionValueReq, dataSetIds);
        // 如果没有，则从数据库中加载
        if (CollectionUtils.isEmpty(dimensionValues)) {
            semanticQueryResp = getDimensionValuesFromDb(dimensionValueReq, user);
            return semanticQueryResp;
        }
        List<QueryColumn> columns = new ArrayList<>();
        QueryColumn queryColumn = new QueryColumn();
        queryColumn.setNameEn(dimensionValueReq.getBizName());
        queryColumn.setShowType(SemanticType.CATEGORY.name());
        queryColumn.setAuthorized(true);
        queryColumn.setType("CHAR");
        columns.add(queryColumn);
        List<Map<String, Object>> resultList = new ArrayList<>();
        dimensionValues.stream().forEach(o -> {
            Map<String, Object> map = new HashMap<>();
            map.put(dimensionValueReq.getBizName(), o);
            resultList.add(map);
        });
        semanticQueryResp.setColumns(columns);
        semanticQueryResp.setResultList(resultList);
        return semanticQueryResp;
    }

    private List<String> getDimensionValuesFromDict(DimensionValueReq dimensionValueReq, Set<Long> dataSetIds) {
        //if value is null ,then search from NATURE_TO_VALUES
        if (StringUtils.isBlank(dimensionValueReq.getValue())) {
            return SearchService.getDimensionValue(dimensionValueReq);
        }
        Map<Long, List<Long>> modelIdToDataSetIds = new HashMap<>();
        modelIdToDataSetIds.put(dimensionValueReq.getModelId(), new ArrayList<>(dataSetIds));
        //search from prefixSearch
        List<HanlpMapResult> hanlpMapResultList =
            knowledgeBaseService.prefixSearch(dimensionValueReq.getValue(), 2000, modelIdToDataSetIds, dataSetIds);
        HanlpHelper.transLetterOriginal(hanlpMapResultList);
        return hanlpMapResultList.stream().filter(o -> {
            for (String nature : o.getNatures()) {
                Long elementID = NatureHelper.getElementID(nature);
                if (dimensionValueReq.getElementID().equals(elementID)) {
                    return true;
                }
            }
            return false;
        }).map(MapResult::getName).collect(Collectors.toList());
    }

    private DimensionResp getDimension(DimensionValueReq dimensionValueReq) {
        DimensionResp dimensionResp = schemaService.getDimension(dimensionValueReq.getElementID());
        if (dimensionResp == null) {
            return schemaService.getDimension(dimensionValueReq.getBizName(), dimensionValueReq.getModelId());
        }
        return dimensionResp;
    }

    private SemanticQueryResp getDimensionValuesFromDb(DimensionValueReq dimensionValueReq, User user) {
        QuerySqlReq querySqlReq = buildQuerySqlReq(dimensionValueReq);
        return queryByReq(querySqlReq, user);
    }

    @Override
    public <T> ExplainResp explain(ExplainSqlReq<T> explainSqlReq, User user) throws Exception {
        T queryReq = explainSqlReq.getQueryReq();
        QueryStatement queryStatement = buildQueryStatement((SemanticQueryReq) queryReq, user);
        semanticTranslator.translate(queryStatement);

        String sql = "";
        String sorceId = "";
        if (Objects.nonNull(queryStatement)) {
            sql = queryStatement.getSql();
            sorceId = queryStatement.getSourceId();
        }
        return ExplainResp.builder().sql(sql).sourceId(sorceId).build();
    }

    public List<ItemResp> getDomainDataSetTree() {
        return schemaService.getDomainDataSetTree();
    }

    @Override
    public JSONArray convertToResult(SemanticQueryResp semanticQueryResp) {
        JSONArray resultList = new JSONArray();
        if (CollectionUtils.isNotEmpty(semanticQueryResp.getResultList())) {
            String json = JSON.toJSONString(semanticQueryResp.getResultList());
            resultList = JSONArray.parseArray(json);
        }
        return resultList;
    }

    private QuerySqlReq buildQuerySqlReq(DimensionValueReq queryDimValueReq) {
        QuerySqlReq querySqlReq = new QuerySqlReq();
        List<ModelResp> modelResps = schemaService.getModelList(Lists.newArrayList(queryDimValueReq.getModelId()));
        DimensionResp dimensionResp = schemaService.getDimension(queryDimValueReq.getBizName(), queryDimValueReq.getModelId());
        ModelResp modelResp = modelResps.get(0);
        String sql = String.format("select distinct %s from %s where 1=1", dimensionResp.getName(), modelResp.getName());
        List<Dim> timeDims = modelResp.getTimeDimension();
        if (CollectionUtils.isNotEmpty(timeDims)) {
            sql = String.format("%s and %s >= '%s' and %s <= '%s'", sql, TimeDimensionEnum.DAY.getName(),
                queryDimValueReq.getDateInfo().getStartDate(), TimeDimensionEnum.DAY.getName(),
                queryDimValueReq.getDateInfo().getEndDate());
        }
        if (StringUtils.isNotBlank(queryDimValueReq.getValue())) {
            sql += " AND " + queryDimValueReq.getBizName() + " LIKE '%" + queryDimValueReq.getValue() + "%'";
        }
        querySqlReq.setModelIds(Sets.newHashSet(queryDimValueReq.getModelId()));
        querySqlReq.setSql(sql);
        return querySqlReq;
    }

    private SemanticQueryResp query(QueryStatement queryStatement) {
        SemanticQueryResp semanticQueryResp = null;
        try {
            //1 translate
            if (!queryStatement.isTranslated()) {
                semanticTranslator.translate(queryStatement);
            }

            //2. query pre-check
            queryPreCheck(queryStatement);

            //3 execute
            for (QueryExecutor queryExecutor : ComponentFactory.getQueryExecutors()) {
                if (queryExecutor.accept(queryStatement)) {
                    semanticQueryResp = queryExecutor.execute(queryStatement);
                    queryUtils.populateQueryColumns(semanticQueryResp, queryStatement.getSemanticSchemaResp());
                }
            }
        } catch (Exception e) {
            log.error("exception in query, e: ", e);
        }
        return semanticQueryResp;
    }

    private void queryPreCheck(QueryStatement queryStatement) {
        //Check whether the dimensions of the metric drill-down are correct temporarily,
        //add the abstraction of a validator later.
        metricDrillDownChecker.checkQuery(queryStatement);
    }

    public EntityInfo getEntityInfo(SemanticParseInfo parseInfo, DataSetSchema dataSetSchema, User user) {
        if (parseInfo != null && parseInfo.getDataSetId() != null && parseInfo.getDataSetId() > 0) {
            EntityInfo entityInfo = getEntityBasicInfo(dataSetSchema);
            if (parseInfo.getDimensionFilters().size() <= 0 || entityInfo.getDataSetInfo() == null) {
                entityInfo.setMetrics(null);
                entityInfo.setDimensions(null);
                return entityInfo;
            }
            String primaryKey = entityInfo.getDataSetInfo().getPrimaryKey();
            if (StringUtils.isNotBlank(primaryKey)) {
                String entityId = "";
                for (QueryFilter chatFilter : parseInfo.getDimensionFilters()) {
                    if (chatFilter != null && chatFilter.getBizName() != null && chatFilter.getBizName().equals(primaryKey)) {
                        if (chatFilter.getOperator().equals(FilterOperatorEnum.EQUALS)) {
                            entityId = chatFilter.getValue().toString();
                        }
                    }
                }
                entityInfo.setEntityId(entityId);
                try {
                    fillEntityInfoValue(entityInfo, dataSetSchema, user);
                    return entityInfo;
                } catch (Exception e) {
                    log.error("setMainModel error", e);
                }
            }
        }
        return null;
    }

    private EntityInfo getEntityBasicInfo(DataSetSchema dataSetSchema) {

        EntityInfo entityInfo = new EntityInfo();
        if (dataSetSchema == null) {
            return entityInfo;
        }
        Long dataSetId = dataSetSchema.getDataSet().getDataSet();
        DataSetInfo dataSetInfo = new DataSetInfo();
        dataSetInfo.setItemId(dataSetId.intValue());
        dataSetInfo.setName(dataSetSchema.getDataSet().getName());
        dataSetInfo.setWords(dataSetSchema.getDataSet().getAlias());
        dataSetInfo.setBizName(dataSetSchema.getDataSet().getBizName());
        if (Objects.nonNull(dataSetSchema.getEntity())) {
            dataSetInfo.setPrimaryKey(dataSetSchema.getEntity().getBizName());
        }
        entityInfo.setDataSetInfo(dataSetInfo);
        return entityInfo;
    }

    private void fillEntityInfoValue(EntityInfo entityInfo, DataSetSchema dataSetSchema, User user) {
        SemanticQueryResp queryResultWithColumns = getQueryResultWithSchemaResp(entityInfo, dataSetSchema, user);
        if (queryResultWithColumns != null) {
            if (!org.springframework.util.CollectionUtils.isEmpty(queryResultWithColumns.getResultList())
                && queryResultWithColumns.getResultList().size() > 0) {
                Map<String, Object> result = queryResultWithColumns.getResultList().get(0);
                for (Map.Entry<String, Object> entry : result.entrySet()) {
                    String entryKey = getEntryKey(entry);
                    if (entry.getValue() == null || entryKey == null) {
                        continue;
                    }
                    entityInfo.getDimensions().stream().filter(i -> entryKey.equals(i.getBizName()))
                              .forEach(i -> i.setValue(entry.getValue().toString()));
                    entityInfo.getMetrics().stream().filter(i -> entryKey.equals(i.getBizName()))
                              .forEach(i -> i.setValue(entry.getValue().toString()));
                }
            }
        }
    }

    private SemanticQueryResp getQueryResultWithSchemaResp(EntityInfo entityInfo, DataSetSchema dataSetSchema, User user) {
        SemanticParseInfo semanticParseInfo = new SemanticParseInfo();
        semanticParseInfo.setDataSet(dataSetSchema.getDataSet());
        semanticParseInfo.setQueryType(QueryType.DETAIL);
        semanticParseInfo.setMetrics(getMetrics(entityInfo));
        semanticParseInfo.setDimensions(getDimensions(entityInfo));
        DateConf dateInfo = new DateConf();
        int unit = 1;
        TimeDefaultConfig timeDefaultConfig = dataSetSchema.getMetricTypeTimeDefaultConfig();
        if (Objects.nonNull(timeDefaultConfig)) {
            unit = timeDefaultConfig.getUnit();
            String date = LocalDate.now().plusDays(-unit).toString();
            dateInfo.setDateMode(DateConf.DateMode.BETWEEN);
            dateInfo.setStartDate(date);
            dateInfo.setEndDate(date);
        } else {
            dateInfo.setUnit(unit);
            dateInfo.setDateMode(DateConf.DateMode.RECENT);
        }
        semanticParseInfo.setDateInfo(dateInfo);

        //add filter
        QueryFilter chatFilter = getQueryFilter(entityInfo);
        Set<QueryFilter> chatFilters = new LinkedHashSet();
        chatFilters.add(chatFilter);
        semanticParseInfo.setDimensionFilters(chatFilters);

        SemanticQueryResp queryResultWithColumns = null;
        try {
            QueryStructReq queryStructReq = QueryReqBuilder.buildStructReq(semanticParseInfo);
            queryResultWithColumns = queryByReq(queryStructReq, user);
        } catch (Exception e) {
            log.warn("setMainModel queryByStruct error, e:", e);
        }
        return queryResultWithColumns;
    }

    private QueryFilter getQueryFilter(EntityInfo entityInfo) {
        QueryFilter chatFilter = new QueryFilter();
        chatFilter.setValue(entityInfo.getEntityId());
        chatFilter.setOperator(FilterOperatorEnum.EQUALS);
        chatFilter.setBizName(getEntityPrimaryName(entityInfo));
        return chatFilter;
    }

    private Set<SchemaElement> getDimensions(EntityInfo modelInfo) {
        Set<SchemaElement> dimensions = new LinkedHashSet();
        for (DataInfo mainEntityDimension : modelInfo.getDimensions()) {
            SchemaElement dimension = new SchemaElement();
            dimension.setBizName(mainEntityDimension.getBizName());
            dimensions.add(dimension);
        }
        return dimensions;
    }

    private String getEntryKey(Map.Entry<String, Object> entry) {
        // metric parser special handle
        String entryKey = entry.getKey();
        if (entryKey.contains("__")) {
            entryKey = entryKey.split("__")[1];
        }
        return entryKey;
    }

    private Set<SchemaElement> getMetrics(EntityInfo modelInfo) {
        Set<SchemaElement> metrics = new LinkedHashSet();
        for (DataInfo metricValue : modelInfo.getMetrics()) {
            SchemaElement metric = new SchemaElement();
            BeanUtils.copyProperties(metricValue, metric);
            metrics.add(metric);
        }
        return metrics;
    }

    private String getEntityPrimaryName(EntityInfo entityInfo) {
        return entityInfo.getDataSetInfo().getPrimaryKey();
    }

}
