package com.purui.prims.headless.server.schedule;

import com.purui.prims.common.pojo.PersistenceConf;
import com.purui.prims.dscheduler.model.DSTaskRequest;
import com.purui.prims.dscheduler.service.DatasourceService;
import com.purui.prims.dscheduler.service.ProjectService;
import com.purui.prims.headless.api.pojo.response.DimensionResp;
import com.purui.prims.headless.api.pojo.response.MetricPersistenceConfigResp;
import com.purui.prims.headless.api.pojo.response.MetricResp;
import com.purui.prims.headless.api.pojo.response.ModelResp;
import com.purui.prims.headless.server.pojo.MetaFilter;
import com.purui.prims.headless.server.service.DatabaseService;
import com.purui.prims.headless.server.service.DimensionService;
import com.purui.prims.headless.server.service.MetricPersistenceConfigService;
import com.purui.prims.headless.server.service.MetricService;
import com.purui.prims.headless.server.service.ModelService;
import com.purui.prims.headless.server.service.SchedulerService;
import com.purui.prims.headless.server.utils.DorisUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 指标持久化任务
 *
 * @author charlie
 * @date 2024/07/29
 */
@Component
@Slf4j
public class MetricPersistenceTask {

    MetricPersistenceConfigService metricPersistenceConfigService;
    MetricService metricService;
    ModelService modelService;
    ProjectService projectService;
    SchedulerService schedulerService;
    DatabaseService databaseService;
    DimensionService dimensionService;
    DatasourceService datasourceService;

    int sourceId;
    int targetId;
    String sourceType;
    String targetType;

    public MetricPersistenceTask(MetricPersistenceConfigService metricPersistenceConfigService, MetricService metricService,
                                 ModelService modelService, ProjectService projectService, SchedulerService schedulerService,
                                 DatabaseService databaseService, DimensionService dimensionService, DatasourceService datasourceService) {
        this.metricPersistenceConfigService = metricPersistenceConfigService;
        this.metricService = metricService;
        this.modelService = modelService;
        this.projectService = projectService;
        this.schedulerService = schedulerService;
        this.databaseService = databaseService;
        this.dimensionService = dimensionService;
        this.datasourceService = datasourceService;
    }

    /**
     * 指标持久化任务
     */
    // @Scheduled(cron = "${persistence.metric.cron:0 10 * * * ?}")
    public void executeTask() {
        sourceId = datasourceService.getSourceId();
        targetId = datasourceService.getTargetId();
        sourceType = datasourceService.getSourceType();
        targetType = datasourceService.getTargetType();
        // 读取指标持久化配置
        Map<Long, Map<String, List<MetricPersistenceConfigResp>>> modelAndRunCycle = readPersistenceConfig();
        // 将模型中相同周期、相同维度【相同维度指标自动合并】的指标持久化到同一个表
        modelAndRunCycle.forEach((modelId, runCycleMap) -> runCycleMap.forEach((runCycleCode, configList) -> {
            persistenceShareMetric(modelId, runCycleCode, configList);
        }));
    }

    /**
     * 持久化指标（已发布的版本）
     *
     * @param modelId
     * @param runCycleCode
     * @param configList
     */
    private void persistenceShareMetric(Long modelId, String runCycleCode, List<MetricPersistenceConfigResp> configList) {
        List<MetricResp> metricRespList = configList.stream().map(config -> metricService.getLastVersionMetric(config.getMetricId()))
                                                    .filter(metricResp -> (metricResp.getRelateDimension() != null
                                                                           && metricResp.getRelateDimension().getDrillDownDimensions()
                                                                              != null)).collect(Collectors.toList());

        // 根据血缘关系，放入依赖DAG
        metricService.buildDependencyDAG(metricRespList);

        // 相同维度的指标
        Map<Object, List<MetricResp>> metricsGroup =
            metricRespList.stream().filter(metricResp -> metricResp.getRelateDimension() != null).collect(Collectors.groupingBy(s -> {
                return s.getRelateDimension().getDrillDownDimensions();
            }));

        String processName = "prims_metrics_" + runCycleCode;
        ModelResp modelResp = modelService.getModel(modelId);
        List<DSTaskRequest> taskRequests = new ArrayList<>();
        metricsGroup.values().forEach(metrics -> {
            String sql = metricService.buildMetricSql(modelResp, metrics);
            taskRequests.addAll(buildTaskList(configList, modelResp, runCycleCode, sql));
        });

        MetricPersistenceConfigResp resp = configList.stream().filter(x -> x.getConf() != null).findFirst().orElse(null);
        // 默认一组相同周期的持久化保持相同
        PersistenceConf conf = new PersistenceConf();
        if (resp != null) {
            conf = resp.getConf();
        } else {
            conf.setCron("0 10 * * * ?");
        }
        // 提交任务
        schedulerService.pushWorkflow(processName, taskRequests, conf);
    }

    private List<DSTaskRequest> buildTaskList(List<MetricPersistenceConfigResp> configList, ModelResp modelResp, String runCycleCode,
                                              String sql) {
        String taskName = getTableName(runCycleCode, modelResp.getId());
        // 此时合并后的metrics表，表名和任务名一致
        String preSql = checkTableExist(configList, modelResp, taskName);
        DSTaskRequest task = new DSTaskRequest().create(taskName, sql, preSql, null, sourceId, targetId, sourceType, targetType);
        task.setTaskId(modelResp.getId().toString());
        task.setScheduleCycle(runCycleCode);
        return Arrays.asList(task);
    }

    private String getTableName(String cycleCode, Long modelId) {
        return String.format("index_metrics_%s_%s", cycleCode, modelId);
    }

    // 根据指标进行合并，构建合并后的指标表SQL
    private String checkTableExist(List<MetricPersistenceConfigResp> configList, ModelResp modelResp, String tableName) {
        // 所有指标
        List<Long> metricsIds = configList.stream().map(config -> config.getMetricId()).collect(Collectors.toList());
        MetaFilter metaFilter = new MetaFilter();
        metaFilter.setIds(metricsIds);
        List<MetricResp> metricList = metricService.getMetrics(metaFilter);

        if (metricList.isEmpty()) {
            throw new RuntimeException("指标不存在!");
        }
        // 仅合并指标
        MetricResp metricResp = metricList.stream().filter(x -> x.getRelateDimension() != null).findFirst().get();
        List<Long> dimensionIds =
            metricResp.getRelateDimension().getDrillDownDimensions().stream().map(dd -> dd.getDimensionId()).collect(Collectors.toList());
        metricList.stream()
                  .filter(metric -> (metric.getRelateDimension() != null && metric.getRelateDimension().getDrillDownDimensions() != null))
                  .forEach(metric -> {
                      List<Long> currentDimensionIds =
                          metric.getRelateDimension().getDrillDownDimensions().stream().map(dd -> dd.getDimensionId())
                                .collect(Collectors.toList());
                      dimensionIds.retainAll(currentDimensionIds);
                  });
        // 增加日期维度
        if (metricResp.getDateDimId() != null) {
            dimensionIds.add(metricResp.getDateDimId());
        }
        MetaFilter filter = new MetaFilter();
        filter.setIds(dimensionIds);
        if (dimensionIds.size() > 0) {
            // 合并后的维度
            List<DimensionResp> dimensionRespList = dimensionService.getDimensions(filter).stream().collect(Collectors.toList());
            return DorisUtils.buildCreateTableSql(tableName, dimensionRespList, metricList);
        }
        throw new RuntimeException("创建指标持久化表异常：无维度信息！");
    }

    private Map<Long, Map<String, List<MetricPersistenceConfigResp>>> readPersistenceConfig() {
        List<MetricPersistenceConfigResp> configList = metricPersistenceConfigService.getConfigList();
        // 对configList按模型和相同计算周期进行分组
        Map<Long, Map<String, List<MetricPersistenceConfigResp>>> collect =
            configList.stream().filter(config -> config.getModelId() != null).collect(
                Collectors.groupingBy(MetricPersistenceConfigResp::getModelId,
                    Collectors.groupingBy(MetricPersistenceConfigResp::getRunCycleCode)));
        return collect;
    }

}
