package com.purui.prims.headless.core.translator.calcite.sql;


import com.purui.prims.headless.api.pojo.MeasureParam;
import com.purui.prims.headless.api.pojo.enums.EngineType;
import com.purui.prims.headless.core.pojo.MetricQueryParam;
import com.purui.prims.headless.core.translator.calcite.s2sql.DataSource;
import com.purui.prims.headless.core.translator.calcite.s2sql.Dimension;
import com.purui.prims.headless.core.translator.calcite.s2sql.Identify;
import com.purui.prims.headless.core.translator.calcite.s2sql.Measure;
import com.purui.prims.headless.core.translator.calcite.s2sql.Metric;
import com.purui.prims.headless.core.translator.calcite.schema.SemanticSchema;
import com.purui.prims.headless.core.translator.calcite.sql.node.MeasureNode;
import com.purui.prims.headless.core.translator.calcite.sql.node.MetricNode;
import com.purui.prims.headless.core.translator.calcite.sql.node.SemanticNode;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.calcite.sql.SqlNode;
import org.apache.calcite.sql.validate.SqlValidatorScope;
import org.apache.commons.lang3.StringUtils;

import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;

/**
 * process TableView
 */
@Data
@Slf4j
public abstract class Renderer {

    protected TableView tableView = new TableView();

    public static Optional<Dimension> getDimensionByName(String name, DataSource datasource) {
        return datasource.getDimensions().stream().filter(d -> d.getName().equalsIgnoreCase(name)).findFirst();
    }

    public static Optional<Measure> getMeasureByName(String name, DataSource datasource) {
        return datasource.getMeasures().stream().filter(mm -> mm.getName().equalsIgnoreCase(name)).findFirst();
    }

    public static Optional<Metric> getMetricByName(String name, SemanticSchema schema) {
        Optional<Metric> metric = schema.getMetrics().stream().filter(m -> m.getName().equalsIgnoreCase(name))
                                        .findFirst();
        return metric;
    }

    public static Optional<Identify> getIdentifyByName(String name, DataSource datasource) {
        return datasource.getIdentifiers().stream().filter(i -> i.getName().equalsIgnoreCase(name)).findFirst();
    }

    public static MetricNode buildMetricNode(String metric, DataSource datasource, SqlValidatorScope scope,
                                             SemanticSchema schema, boolean nonAgg, String alias,
                                             Map<String, MeasureParam> derivedMetrics) throws Exception {
        Optional<Metric> metricOpt = getMetricByName(metric, schema);
        MetricNode metricNode = new MetricNode();
        EngineType engineType = EngineType.fromString(datasource.getType());
        // 如果是指标
        if (metricOpt.isPresent()) {
            metricNode.setMetric(metricOpt.get());
            for (Measure m : metricOpt.get().getMetricTypeParams().getMeasures()) {
                // 获取一次最新的数据
                Optional<Measure> measure = getMeasureByName(m.getName(), datasource);
                if (measure.isPresent()) {
                    metricNode.getNonAggNode()
                              .put(measure.get().getName(),
                                      MeasureNode.buildNonAgg(alias, measure.get(), scope, engineType));
                    metricNode.getAggNode()
                              .put(measure.get().getName(),
                                      MeasureNode.buildAgg(measure.get(), nonAgg, scope, engineType));
                    metricNode.getAggFunction().put(measure.get().getName(), measure.get().getAgg());

                } else {
                    metricNode.getNonAggNode().put(m.getName(), MeasureNode.buildNonAgg(alias, m, scope, engineType));
                    metricNode.getAggNode().put(m.getName(), MeasureNode.buildAgg(m, nonAgg, scope, engineType));
                    metricNode.getAggFunction().put(m.getName(), m.getAgg());
                }
                if (m.getConstraint() != null && !m.getConstraint().isEmpty()) {
                    metricNode.getMeasureFilter()
                              .put(m.getName(), SemanticNode.parse(m.getConstraint(), scope, engineType));
                }
            }

            // 业务限定的条件处理
            if (StringUtils.isNotEmpty(metricOpt.get().getBizCondition())) {
                metricNode.getMeasureFilter()
                    .put(metricOpt.get().getName(),
                                  SemanticNode.parse(metricOpt.get().getBizCondition(), scope, engineType));
            }
            return metricNode;
        }
        // 如果是度量
        Optional<Measure> measure = getMeasureByName(metric, datasource);
        if (measure.isPresent()) {
            String measureName = measure.get().getName();
            metricNode.getNonAggNode()
                      .put(measureName, MeasureNode.buildNonAgg(alias, measure.get(), scope, engineType));
            metricNode.getAggNode()
                      .put(measureName, MeasureNode.buildAgg(measure.get(), nonAgg, scope, engineType));
            metricNode.getAggFunction()
                      .put(measureName, measure.get().getAgg());

            // 转换限定where条件
            if (measure.get().getConstraint() != null && !measure.get().getConstraint().isEmpty()) {
                metricNode.getMeasureFilter().put(measureName,
                        SemanticNode.parse(measure.get().getConstraint(), scope, engineType));
            }
            // 复合指标中使用的衍生指标转换为where条件
            if (derivedMetrics != null) {
                for (String k : derivedMetrics.keySet()) {
                    MeasureParam v = derivedMetrics.get(k);
                    if (v.getBizName().equalsIgnoreCase(measureName)) {
                        metricNode.getMeasureFilter().put(measureName,
                            SemanticNode.parse(v.getConstraint(), scope, engineType));
                    }
                }
            }
        }
        return metricNode;
    }

    public static List<String> uniqList(List<String> list) {
        // 去重,不区分大小写 2024-08-09
        Set<String> tmp = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
        list.stream().forEach(s -> tmp.add(s));
        return tmp.stream().collect(Collectors.toList());
    }

    public void setTable(SqlNode table) {
        tableView.setTable(table);
    }

    public SqlNode builder() {
        return tableView.build();
    }

    public SqlNode builderAs(String alias) throws Exception {
        return SemanticNode.buildAs(alias, tableView.build());
    }

    public abstract void render(MetricQueryParam metricCommand, List<DataSource> dataSources, SqlValidatorScope scope,
                                SemanticSchema schema, boolean nonAgg) throws Exception;
}
