package eval

import (
	"fmt"

	"github.com/grafana/grafana/pkg/expr"
	"github.com/grafana/grafana/pkg/plugins"
	"github.com/grafana/grafana/pkg/services/datasources"
	"github.com/grafana/grafana/pkg/services/ngalert/models"
	"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore"
)

type ConditionValidator struct {
	dataSourceCache   datasources.CacheService
	pluginsStore      pluginstore.Store
	expressionService expressionBuilder
}

func NewConditionValidator(datasourceCache datasources.CacheService, expressionService *expr.Service, pluginsStore pluginstore.Store) *ConditionValidator {
	return &ConditionValidator{
		dataSourceCache:   datasourceCache,
		expressionService: expressionService,
		pluginsStore:      pluginsStore,
	}
}

func (e *ConditionValidator) Validate(ctx EvaluationContext, condition models.Condition) error {
	req, err := getExprRequest(ctx, condition, e.dataSourceCache, ctx.AlertingResultsReader)
	if err != nil {
		return err
	}
	for _, query := range req.Queries {
		if query.DataSource == nil {
			continue
		}
		switch expr.NodeTypeFromDatasourceUID(query.DataSource.UID) {
		case expr.TypeDatasourceNode:
			p, found := e.pluginsStore.Plugin(ctx.Ctx, query.DataSource.Type)
			if !found { // technically this should fail earlier during datasource resolution phase.
				return fmt.Errorf("datasource refID %s could not be found: %w", query.RefID, plugins.ErrPluginUnavailable)
			}
			if !p.Backend {
				return fmt.Errorf("datasource refID %s is not a backend datasource", query.RefID)
			}
		case expr.TypeMLNode:
			_, found := e.pluginsStore.Plugin(ctx.Ctx, query.DataSource.Type)
			if !found {
				return fmt.Errorf("datasource refID %s could not be found: %w", query.RefID, plugins.ErrPluginUnavailable)
			}
		case expr.TypeCMDNode:
		}
	}
	pipeline, err := e.expressionService.BuildPipeline(req)
	if err != nil {
		return err
	}
	refIDs := make([]string, 0, len(pipeline))
	for _, node := range pipeline {
		if node.RefID() == condition.Condition {
			return nil
		}
		refIDs = append(refIDs, node.RefID())
	}
	return models.ErrConditionNotExist(condition.Condition, refIDs)
}
