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

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.purui.prims.common.pojo.enums.AggregateTypeEnum;
import com.purui.prims.common.util.BeanMapper;
import com.purui.prims.headless.api.pojo.Field;
import com.purui.prims.headless.api.pojo.Measure;
import com.purui.prims.headless.api.pojo.MeasureParam;
import com.purui.prims.headless.api.pojo.ModelDetail;
import com.purui.prims.headless.api.pojo.enums.MetricDefineType;
import com.purui.prims.headless.api.pojo.enums.MetricType;
import com.purui.prims.headless.api.pojo.response.MetricResp;
import com.purui.prims.headless.api.pojo.response.ModelResp;
import com.purui.prims.headless.server.manager.MetricYamlManager;
import com.purui.prims.headless.server.persistence.dataobject.MetricLineageDO;
import com.purui.prims.headless.server.persistence.mapper.MetricLineageCustomDOMapper;
import com.purui.prims.headless.server.persistence.mapper.MetricLineageDOMapper;
import com.purui.prims.headless.server.pojo.MetricLineage;
import com.purui.prims.headless.server.pojo.yaml.FieldParamYamlTpl;
import com.purui.prims.headless.server.pojo.yaml.MetricTypeParamsYamlTpl;
import com.purui.prims.headless.server.pojo.yaml.MetricYamlTpl;
import com.purui.prims.headless.server.service.MetricLineageService;
import com.purui.prims.headless.server.service.MetricService;
import com.purui.prims.headless.server.service.ModelService;
import io.jsonwebtoken.lang.Collections;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 指标血缘服务实现类
 *
 * @author charlie
 * @since 2024-07-26
 */
@Service
public class MetricLineageServiceImpl extends ServiceImpl<MetricLineageDOMapper, MetricLineageDO> implements MetricLineageService {

    @Resource
    private MetricLineageCustomDOMapper customDOMapper;

    @Resource
    private ModelService modelService;

    @Resource
    private MetricService metricService;

    @Override
    public MetricLineage getLineages(Long metricId) {
        MetricResp metricResp = metricService.getMetric(metricId);
        MetricLineage metricLineage = new MetricLineage();
        metricLineage.setMetricId(metricResp.getId());
        metricLineage.setMetricName(metricResp.getName());
        metricLineage.setMetricType(metricResp.getType());
        getMetricLineage(metricLineage);
        return metricLineage;
    }

    // 根据指标ID获取上游依赖的所有指标
    private List<MetricLineage> getMetricLineage(MetricLineage metricLineage) {
        List<MetricLineage> children = new ArrayList<>();
        List<MetricLineageDO> lineageDOList = customDOMapper.getMetricLineage(metricLineage);
        if (!Collections.isEmpty(lineageDOList)) {
            children = lineageDOList.stream().map(this::convert).collect(Collectors.toList());
            if (children.size() > 0) {
                children.stream().forEach(lineage -> {
                    getMetricLineage(lineage);
                });
                metricLineage.setChildren(children);
            }
        }
        return children;
    }

    // 根据指标id获取指标血缘
    private MetricLineageDO getByMetricId(Long metricId, Long sourceMetricId) {
        QueryWrapper<MetricLineageDO> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(MetricLineageDO::getMetricId, metricId);
        if (sourceMetricId != null) {
            queryWrapper.lambda().eq(MetricLineageDO::getSourceMetricId, sourceMetricId);
        }
        return getOne(queryWrapper);
    }

    private MetricLineageDO convert(MetricResp metricResp, Long sourceMetricId, String aggregateType) {
        MetricLineageDO lineage = new MetricLineageDO();
        MetricLineageDO oldLineage = getByMetricId(metricResp.getId(), sourceMetricId);
        if (oldLineage != null) {
            lineage.setId(oldLineage.getId());
        }
        lineage.setMetricId(metricResp.getId());
        lineage.setMetricName(metricResp.getName());
        lineage.setMetricType(metricResp.getType());
        lineage.setSourceMetricId(sourceMetricId);
        lineage.setAggregateType(aggregateType);
        lineage.setDescription(metricResp.getExpr());
        return lineage;
    }

    private MetricLineage convert(MetricLineageDO metricLineageDO) {
        MetricLineage metricLineage = new MetricLineage();
        BeanMapper.mapper(metricLineageDO, metricLineage);
        return metricLineage;
    }

    // 保存指标血缘
    @Override
    public boolean saveMetricLineage(MetricResp metricResp) {
        MetricYamlTpl metricYamlTpl = MetricYamlManager.convert2MetricYamlTpl(metricResp);
        MetricTypeParamsYamlTpl typeParams = metricYamlTpl.getTypeParams();
        if (typeParams != null) {
            if (MetricType.COMPOSITE.equals(MetricType.of(metricResp.getType()))) {
                // 来自于指标
                List<MetricLineageDO> metricLineages = typeParams.getMetrics().stream().map(paramYamlTpl -> {
                    return convert(metricResp, paramYamlTpl.getId(), getAggregateType(typeParams).name());
                }).collect(Collectors.toList());
                metricLineages.stream().forEach(lineage -> {
                    // 保存当前指标血缘
                    saveOrUpdate(lineage);
                    MetricResp nextMetricResp = metricService.getMetric(lineage.getSourceMetricId());
                    if (nextMetricResp != null) {
                        saveMetricLineage(nextMetricResp);
                    }
                });
            } else {
                ModelResp modelResp = modelService.getModel(metricResp.getModelId());
                if (modelResp == null || modelResp.getModelDetail() == null) {
                    return false;
                }
                ModelDetail modelDetail = modelResp.getModelDetail();

                // 来自于度量
                List<MetricLineageDO> measureLineages = new ArrayList<>();
                typeParams.getMeasures().stream().forEach(paramYamlTpl -> {
                    String aggregateType = getAggregateType(paramYamlTpl.getAgg()).name();
                    MetricLineageDO lineage = convert(metricResp, 0L, aggregateType);
                    if (MetricType.DERIVED.equals(MetricType.of(metricResp.getType()))) {
                        // 保存衍生指标（衍生指标来自于原子指标）
                        String bizName = parseBizName(paramYamlTpl.getConstraint());
                        if (StringUtils.isNotEmpty(bizName)) {
                            MetricResp atomicMetric = getAtomicMetric(metricResp.getModelId(), bizName);
                            if (atomicMetric != null) {
                                MetricLineageDO derivedLineage = convert(atomicMetric, 0L, aggregateType);
                                lineage.setDescription(paramYamlTpl.getConstraint());
                                measureLineages.add(derivedLineage);
                                // 派生指标来源于原子指标
                                lineage.setSourceMetricId(atomicMetric.getId());

                            }
                        }
                    } else {
                        MeasureParam measureParam =
                            metricResp.getMetricDefineByMeasureParams().getMeasures().stream().findFirst().orElse(null);
                        if (measureParam != null) {
                            Measure measure =
                                modelDetail.getMeasures().stream().filter(m -> m.getBizName().equals(measureParam.getBizName()))
                                           .findFirst().orElse(null);
                            if (measure != null) {
                                // 度量不是真实的指标，保存为虚拟指标
                                Long measureId = metricResp.getId() * -1L;
                                MetricLineageDO lineageDO =
                                    createVisualizeMetricLineage(measureId, measure.getExpr(),
                                        measure.getName());
                                measureLineages.add(lineageDO);
                                // 保存当前指标血缘
                                lineage.setSourceMetricId(measureId);
                            }
                        }
                    }
                    // 原子指标(可能存在）
                    MetricLineageDO existLineage = getByMetricId(metricResp.getId(), lineage.getSourceMetricId());
                    if (existLineage != null) {
                        lineage.setId(existLineage.getId());
                    }
                    measureLineages.add(lineage);
                });

                // 来源字段
                for (FieldParamYamlTpl paramYamlTpl : typeParams.getFields()) {
                    String aggregateType = getAggregateType(paramYamlTpl.getAgg()).name();
                    MetricLineageDO lineage = convert(metricResp, 0L, aggregateType);
                    if (metricResp.getMetricDefineType().equals(MetricDefineType.CUSTOM)) {
                        Field field =
                            modelDetail.getFields().stream().filter(f -> f.getFieldName().equals(paramYamlTpl.getFieldName())).findFirst()
                                       .orElse(null);
                        if (field != null) {
                            // 保存字段指标
                            Long fieldId = metricResp.getId() * -2L;
                            MetricLineageDO lineageDO =
                                createVisualizeMetricLineage(fieldId, metricResp.getExpr(),
                                    metricResp.getName());
                            measureLineages.add(lineageDO);
                            lineage.setSourceMetricId(fieldId);
                        }
                        // 原子指标(可能存在）
                        MetricLineageDO existLineage = getByMetricId(metricResp.getId(), lineage.getSourceMetricId());
                        if (existLineage != null) {
                            lineage.setId(existLineage.getId());
                        }
                        measureLineages.add(lineage);
                        // 只保存一个字段指标
                        break;
                    }
                }
                // 保存血缘
                if (measureLineages.size() > 0) {
                    saveOrUpdateBatch(measureLineages);
                }
            }
        }
        return false;
    }

    private MetricLineageDO createVisualizeMetricLineage(Long metricId, String alias, String name) {
        MetricLineageDO measureLineage = getByMetricId(metricId, 0L);
        if (measureLineage == null) {
            measureLineage = new MetricLineageDO();
            measureLineage.setMetricId(metricId);
        }
        measureLineage.setSourceMetricId(0L);
        measureLineage.setAggregateType(AggregateTypeEnum.NONE.name());
        measureLineage.setMetricName(alias);
        measureLineage.setMetricType(MetricType.ATOMIC.name());
        measureLineage.setDescription(name);
        return measureLineage;
    }

    private String parseBizName(String expr) {
        // 正则表达式匹配字段名
        Pattern pattern = Pattern.compile("([a-zA-Z_][a-zA-Z0-9_]*)");
        Matcher matcher = pattern.matcher(expr);

        if (matcher.find()) {
            return matcher.group(1);
        }
        return null;
    }

    private MetricResp getAtomicMetric(Long modelId, String bizName) {
        MetricResp atomicMetric = metricService.getMetric(modelId, bizName);
        return atomicMetric;
    }

    private AggregateTypeEnum getAggregateType(MetricTypeParamsYamlTpl typeParamsYamlTpl) {
        AggregateTypeEnum aggregateType = getAggregateType(typeParamsYamlTpl.getExpr());
        if (aggregateType == AggregateTypeEnum.NONE && StringUtils.isNotEmpty(typeParamsYamlTpl.getOperations())) {
            aggregateType = AggregateTypeEnum.of(typeParamsYamlTpl.getOperations());
        }
        return aggregateType;
    }

    private AggregateTypeEnum getAggregateType(String expr) {
        if (expr == null) {
            return AggregateTypeEnum.SUM;
        }
        if (expr.contains("(")) {
            expr = expr.substring(0, expr.indexOf("("));
        }
        switch (expr.toLowerCase(Locale.ROOT)) {
            case "avg":
                return AggregateTypeEnum.AVG;
            case "count":
                return AggregateTypeEnum.COUNT;
            case "distinct":
                return AggregateTypeEnum.DISTINCT;
            case "max":
                return AggregateTypeEnum.MAX;
            case "min":
                return AggregateTypeEnum.MIN;
            case "sum":
                return AggregateTypeEnum.SUM;
            default:
                return AggregateTypeEnum.NONE;
        }
    }

}
