package rules

import (
	"context"
	"fmt"
	"os"
	"os/signal"
	"syscall"

	"github.com/jmoiron/sqlx"
	_ "github.com/mattn/go-sqlite3"
	"go.signoz.io/query-service/app/clickhouseReader"
	am "go.signoz.io/query-service/integrations/alertManager"
	"go.signoz.io/query-service/model"
	pqle "go.signoz.io/query-service/pqlEngine"
	"go.signoz.io/query-service/utils/value"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"net/url"
	"testing"
	"time"
)

func initZapLog() *zap.Logger {
	config := zap.NewDevelopmentConfig()
	config.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder
	config.EncoderConfig.TimeKey = "timestamp"
	config.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
	logger, _ := config.Build()
	return logger
}

func TestRules(t *testing.T) {
	fmt.Println("starting test TestRules..")
	loggerMgr := initZapLog()
	zap.ReplaceGlobals(loggerMgr)
	defer loggerMgr.Sync() // flushes buffer, if any

	logger := loggerMgr.Sugar()

	configFile := "../config/prometheus.yml"
	// create engine
	pqle, err := pqle.FromConfigPath(configFile)
	if err != nil {
		fmt.Println("failed to create pql:", err)
		t.Errorf("failed to create pql engine : %v", err)
	}

	// create db conn
	db, err := sqlx.Open("sqlite3", "../signoz.db")
	if err != nil {
		fmt.Println("failed to create db conn:", err)
		t.Errorf("failed to create db conn: %v", err)
	}

	// create ch reader
	ch := clickhouseReader.NewReader(db, configFile)

	// notifier opts
	notifierOpts := am.NotifierOptions{
		QueueCapacity:    10000,
		Timeout:          1 * time.Second,
		AlertManagerURLs: []string{"http://localhost:9093/api/"},
	}

	externalURL, _ := url.Parse("http://signoz.io")

	// create manager opts
	managerOpts := &ManagerOptions{
		NotifierOpts: notifierOpts,
		Queriers: &Queriers{
			PqlEngine: pqle,
			Ch:        ch,
		},
		ExternalURL: externalURL,
		Conn:        db,
		Context:     context.Background(),
		Logger:      nil,
	}

	// create Manager
	manager, err := NewManager(managerOpts)
	if err != nil {
		fmt.Println("manager error:", err)
		t.Errorf("manager error: %v", err)
	}
	fmt.Println("manager is ready:", manager)

	manager.run()

	// test rules
	// create promql rule
	/* promql rule
	postableRule := PostableRule{
		Alert:      "test alert 1 - promql",
		RuleType:   RuleTypeProm,
		EvalWindow: 5 * time.Minute,
		Frequency:  30 * time.Second,
		RuleCondition: RuleCondition{
			CompositeMetricQuery: &model.CompositeMetricQuery{
				QueryType: model.PROM,
				PromQueries: map[string]*model.PromQuery{
					"A": &model.PromQuery{Query: `sum(signoz_latency_count{span_kind="SPAN_KIND_SERVER"}) by (service_name) > 100`},
				},
			},
		},
		Labels:      map[string]string{},
		Annotations: map[string]string{},
	}*/
	// create builder rule
	metricQuery := &model.MetricQuery{
		QueryName:  "A",
		MetricName: "signoz_latency_count",
		TagFilters: &model.FilterSet{Operation: "AND", Items: []model.FilterItem{
			{Key: "span_kind", Value: "SPAN_KIND_SERVER", Operation: "neq"},
		}},
		GroupingTags:      []string{"service_name"},
		AggregateOperator: model.RATE_SUM,
		Expression:        "A",
	}

	postableRule := PostableRule{
		Alert:      "test alert 2 - builder",
		RuleType:   RuleTypeThreshold,
		EvalWindow: 5 * time.Minute,
		Frequency:  30 * time.Second,
		RuleCondition: RuleCondition{
			Target:    value.Float64(500),
			CompareOp: TargetIsMore,
			CompositeMetricQuery: &model.CompositeMetricQuery{
				QueryType: model.QUERY_BUILDER,
				BuilderQueries: map[string]*model.MetricQuery{
					"A": metricQuery,
				},
			},
		},
		Labels:      map[string]string{"host": "server1"},
		Annotations: map[string]string{},
	}
	err = manager.addTask(&postableRule, postableRule.Alert)
	if err != nil {
		fmt.Println("failed to add rule: ", err)
		t.Errorf("failed to add rule")
	}

	signalsChannel := make(chan os.Signal, 1)
	signal.Notify(signalsChannel, os.Interrupt, syscall.SIGTERM)

	for {
		select {
		case <-signalsChannel:
			logger.Fatal("Received OS Interrupt Signal ... ")
		}
	}
}