package app

import (
	"math"
	"reflect"
	"testing"

	"github.com/SigNoz/govaluate"
	v3 "go.signoz.io/signoz/pkg/query-service/model/v3"
)

func TestFindUniqueLabelSets(t *testing.T) {
	tests := []struct {
		name   string
		result []*v3.Result
		want   []map[string]string
	}{
		{
			name: "test1",
			result: []*v3.Result{
				{
					QueryName: "A",
					Series: []*v3.Series{
						{
							Labels: map[string]string{
								"service_name": "frontend",
								"operation":    "GET /api",
							},
						},
					},
				},
				{
					QueryName: "B",
					Series: []*v3.Series{
						{
							Labels: map[string]string{
								"service_name": "redis",
							},
						},
					},
				},
			},
			want: []map[string]string{
				{
					"service_name": "frontend",
					"operation":    "GET /api",
				},
				{
					"service_name": "redis",
				},
			},
		},
		{
			name: "test2",
			result: []*v3.Result{
				{
					QueryName: "A",
					Series: []*v3.Series{
						{
							Labels: map[string]string{
								"service_name": "frontend",
								"operation":    "GET /api",
							},
						},
					},
				},
				{
					QueryName: "B",
					Series: []*v3.Series{
						{
							Labels: map[string]string{
								"service_name": "frontend",
							},
						},
					},
				},
				{
					QueryName: "C",
					Series: []*v3.Series{
						{
							Labels: map[string]string{
								"operation": "PUT /api",
							},
						},
					},
				},
				{
					QueryName: "D",
					Series: []*v3.Series{
						{
							Labels: map[string]string{
								"service_name": "frontend",
								"http_status":  "200",
							},
						},
					},
				},
			},
			want: []map[string]string{
				{
					"service_name": "frontend",
					"operation":    "GET /api",
				},
				{
					"service_name": "frontend",
					"http_status":  "200",
				},
				{
					"operation": "PUT /api",
				},
			},
		},
		{
			name: "empty result",
			result: []*v3.Result{
				{
					QueryName: "A",
					Series:    []*v3.Series{},
				},
				{
					QueryName: "B",
					Series:    []*v3.Series{},
				},
			},
			want: []map[string]string{},
		},
		{
			name: "results with overlapping labels",
			result: []*v3.Result{
				{
					QueryName: "A",
					Series: []*v3.Series{
						{
							Labels: map[string]string{
								"service_name": "frontend",
								"operation":    "GET /api",
							},
						},
						{
							Labels: map[string]string{
								"service_name": "redis",
								"operation":    "GET /api",
							},
						},
					},
				},
				{
					QueryName: "B",
					Series: []*v3.Series{
						{
							Labels: map[string]string{
								"service_name": "redis",
							},
						},
						{
							Labels: map[string]string{
								"service_name": "frontend",
							},
						},
					},
				},
			},
			want: []map[string]string{
				{
					"service_name": "frontend",
					"operation":    "GET /api",
				},
				{
					"service_name": "redis",
					"operation":    "GET /api",
				},
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got := findUniqueLabelSets(tt.result)
			if !reflect.DeepEqual(got, tt.want) {
				t.Errorf("findUniqueLabelSets() = %v, want %v\n", got, tt.want)
			}
		})
	}
}

func TestProcessResults(t *testing.T) {
	tests := []struct {
		name    string
		results []*v3.Result
		want    *v3.Result
	}{
		{
			name: "test1",
			results: []*v3.Result{
				{
					QueryName: "A",
					Series: []*v3.Series{
						{
							Labels: map[string]string{
								"service_name": "frontend",
								"operation":    "GET /api",
							},
							Points: []v3.Point{
								{
									Timestamp: 1,
									Value:     10,
								},
								{
									Timestamp: 2,
									Value:     20,
								},
							},
						},
					},
				},
				{
					QueryName: "B",
					Series: []*v3.Series{
						{
							Labels: map[string]string{
								"service_name": "redis",
							},
							Points: []v3.Point{
								{
									Timestamp: 1,
									Value:     30,
								},
								{
									Timestamp: 3,
									Value:     40,
								},
							},
						},
					},
				},
			},
			want: &v3.Result{
				Series: []*v3.Series{
					{
						Labels: map[string]string{
							"service_name": "frontend",
							"operation":    "GET /api",
						},
						Points: []v3.Point{
							{
								Timestamp: 1,
								Value:     10,
							},
							{
								Timestamp: 2,
								Value:     20,
							},
						},
					},
					{
						Labels: map[string]string{
							"service_name": "redis",
						},
						Points: []v3.Point{
							{
								Timestamp: 1,
								Value:     30,
							},
							{
								Timestamp: 3,
								Value:     40,
							},
						},
					},
				},
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			expression, err := govaluate.NewEvaluableExpression("A + B")
			if err != nil {
				t.Errorf("Error parsing expression: %v", err)
			}
			got, err := processResults(tt.results, expression)
			if err != nil {
				t.Errorf("Error processing results: %v", err)
			}
			if len(got.Series) != len(tt.want.Series) {
				t.Errorf("processResults(): number of sereis - got = %v, want %v", len(got.Series), len(tt.want.Series))
			}

			for i := range got.Series {
				if len(got.Series[i].Points) != len(tt.want.Series[i].Points) {
					t.Errorf("processResults(): number of points - got = %v, want %v", got, tt.want)
				}
				for j := range got.Series[i].Points {
					if got.Series[i].Points[j].Value != tt.want.Series[i].Points[j].Value {
						t.Errorf("processResults(): got = %v, want %v", got.Series[i].Points[j].Value, tt.want.Series[i].Points[j].Value)
					}
				}
			}
		})
	}
}

func TestProcessResultsErrorRate(t *testing.T) {
	tests := []struct {
		name    string
		results []*v3.Result
		want    *v3.Result
	}{
		{
			name: "test1",
			results: []*v3.Result{
				{
					QueryName: "A",
					Series: []*v3.Series{
						{
							Labels: map[string]string{
								"service_name": "frontend",
							},
							Points: []v3.Point{
								{
									Timestamp: 1,
									Value:     10,
								},
								{
									Timestamp: 2,
									Value:     20,
								},
							},
						},
						{
							Labels: map[string]string{
								"service_name": "redis",
							},
							Points: []v3.Point{
								{
									Timestamp: 1,
									Value:     12,
								},
								{
									Timestamp: 2,
									Value:     45,
								},
							},
						},
						{
							Labels: map[string]string{
								"service_name": "route",
							},
							Points: []v3.Point{
								{
									Timestamp: 1,
									Value:     2,
								},
								{
									Timestamp: 2,
									Value:     45,
								},
							},
						},
					},
				},
				{
					QueryName: "B",
					Series: []*v3.Series{
						{
							Labels: map[string]string{
								"service_name": "redis",
							},
							Points: []v3.Point{
								{
									Timestamp: 1,
									Value:     6,
								},
								{
									Timestamp: 2,
									Value:     9,
								},
							},
						},
					},
				},
			},
			want: &v3.Result{
				Series: []*v3.Series{
					{
						Labels: map[string]string{
							"service_name": "frontend",
						},
						Points: []v3.Point{
							{
								Timestamp: 1,
								Value:     0,
							},
							{
								Timestamp: 2,
								Value:     0,
							},
						},
					},
					{
						Labels: map[string]string{
							"service_name": "redis",
						},
						Points: []v3.Point{
							{
								Timestamp: 1,
								Value:     0.5,
							},
							{
								Timestamp: 2,
								Value:     0.2,
							},
						},
					},
					{
						Labels: map[string]string{
							"service_name": "route",
						},
						Points: []v3.Point{
							{
								Timestamp: 1,
								Value:     0,
							},
							{
								Timestamp: 2,
								Value:     0,
							},
						},
					},
				},
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			expression, err := govaluate.NewEvaluableExpression("B/A")
			if err != nil {
				t.Errorf("Error parsing expression: %v", err)
			}
			got, err := processResults(tt.results, expression)
			if err != nil {
				t.Errorf("Error processing results: %v", err)
			}
			if len(got.Series) != len(tt.want.Series) {
				t.Errorf("processResults(): number of sereis - got = %v, want %v", len(got.Series), len(tt.want.Series))
			}

			for i := range got.Series {
				if len(got.Series[i].Points) != len(tt.want.Series[i].Points) {
					t.Errorf("processResults(): number of points - got = %v, want %v", got, tt.want)
				}
				for j := range got.Series[i].Points {
					if got.Series[i].Points[j].Value != tt.want.Series[i].Points[j].Value {
						t.Errorf("processResults(): got = %v, want %v", got.Series[i].Points[j].Value, tt.want.Series[i].Points[j].Value)
					}
				}
			}
		})
	}
}

func TestFormula(t *testing.T) {
	tests := []struct {
		name       string
		expression string
		results    []*v3.Result
		want       *v3.Result
	}{
		{
			name:       "No group keys on the left side",
			expression: "B/A",
			results: []*v3.Result{
				{
					QueryName: "A",
					Series: []*v3.Series{
						{
							Labels: map[string]string{
								"service_name": "frontend",
							},
							Points: []v3.Point{
								{
									Timestamp: 1,
									Value:     10,
								},
								{
									Timestamp: 2,
									Value:     20,
								},
							},
						},
						{
							Labels: map[string]string{
								"service_name": "redis",
							},
							Points: []v3.Point{
								{
									Timestamp: 1,
									Value:     12,
								},
								{
									Timestamp: 2,
									Value:     45,
								},
							},
						},
					},
				},
				{
					QueryName: "B",
					Series: []*v3.Series{
						{
							Labels: map[string]string{},
							Points: []v3.Point{
								{
									Timestamp: 1,
									Value:     22,
								},
								{
									Timestamp: 2,
									Value:     65,
								},
							},
						},
					},
				},
			},
			want: &v3.Result{
				Series: []*v3.Series{
					{
						Labels: map[string]string{
							"service_name": "frontend",
						},
						Points: []v3.Point{
							{
								Timestamp: 1,
								Value:     2.2,
							},
							{
								Timestamp: 2,
								Value:     3.25,
							},
						},
					},
					{
						Labels: map[string]string{
							"service_name": "redis",
						},
						Points: []v3.Point{
							{
								Timestamp: 1,
								Value:     1.8333333333333333,
							},
							{
								Timestamp: 2,
								Value:     1.4444444444444444,
							},
						},
					},
				},
			},
		},
		{
			name:       "No group keys on the right side",
			expression: "A/B",
			results: []*v3.Result{
				{
					QueryName: "A",
					Series: []*v3.Series{
						{
							Labels: map[string]string{
								"host_name": "ip-10-420-69-1",
								"state":     "running",
							},
							Points: []v3.Point{
								{
									Timestamp: 1,
									Value:     10,
								},
								{
									Timestamp: 2,
									Value:     20,
								},
							},
						},
						{
							Labels: map[string]string{
								"host_name": "ip-10-420-69-2",
								"state":     "idle",
							},
							Points: []v3.Point{
								{
									Timestamp: 1,
									Value:     12,
								},
								{
									Timestamp: 2,
									Value:     45,
								},
							},
						},
					},
				},
				{
					QueryName: "B",
					Series: []*v3.Series{
						{
							Labels: map[string]string{},
							Points: []v3.Point{
								{
									Timestamp: 1,
									Value:     22,
								},
								{
									Timestamp: 2,
									Value:     65,
								},
							},
						},
					},
				},
			},
			want: &v3.Result{
				Series: []*v3.Series{
					{
						Labels: map[string]string{
							"host_name": "ip-10-420-69-1",
							"state":     "running",
						},
						Points: []v3.Point{
							{
								Timestamp: 1,
								Value:     0.45454545454545453,
							},
							{
								Timestamp: 2,
								Value:     0.3076923076923077,
							},
						},
					},
					{
						Labels: map[string]string{
							"host_name": "ip-10-420-69-2",
							"state":     "idle",
						},
						Points: []v3.Point{
							{
								Timestamp: 1,
								Value:     0.5454545454545454,
							},
							{
								Timestamp: 2,
								Value:     0.6923076923076923,
							},
						},
					},
				},
			},
		},
		{
			name:       "Group keys on both sides are the same",
			expression: "A/B",
			results: []*v3.Result{
				{
					QueryName: "A",
					Series: []*v3.Series{
						{
							Labels: map[string]string{
								"host_name": "ip-10-420-69-1",
								"state":     "running",
							},
							Points: []v3.Point{
								{
									Timestamp: 1,
									Value:     10,
								},
								{
									Timestamp: 2,
									Value:     20,
								},
								{
									Timestamp: 4,
									Value:     40,
								},
								{
									Timestamp: 5,
									Value:     50,
								},
								{
									Timestamp: 7,
									Value:     70,
								},
							},
						},
						{
							Labels: map[string]string{
								"host_name": "ip-10-420-69-2",
								"state":     "idle",
							},
							Points: []v3.Point{
								{
									Timestamp: 1,
									Value:     12,
								},
								{
									Timestamp: 2,
									Value:     45,
								},
								{
									Timestamp: 3,
									Value:     30,
								},
								{
									Timestamp: 4,
									Value:     40,
								},
								{
									Timestamp: 5,
									Value:     50,
								},
							},
						},
					},
				},
				{
					QueryName: "B",
					Series: []*v3.Series{
						{
							Labels: map[string]string{
								"host_name": "ip-10-420-69-1",
								"state":     "running",
							},
							Points: []v3.Point{
								{
									Timestamp: 1,
									Value:     22,
								},
								{
									Timestamp: 2,
									Value:     65,
								},
								{
									Timestamp: 3,
									Value:     30,
								},
								{
									Timestamp: 4,
									Value:     40,
								},
								{
									Timestamp: 5,
									Value:     50,
								},
							},
						},
						{
							Labels: map[string]string{
								"host_name": "ip-10-420-69-2",
								"state":     "idle",
							},
							Points: []v3.Point{
								{
									Timestamp: 1,
									Value:     22,
								},
								{
									Timestamp: 2,
									Value:     65,
								},
								{
									Timestamp: 4,
									Value:     40,
								},
								{
									Timestamp: 5,
									Value:     50,
								},
							},
						},
					},
				},
			},
			want: &v3.Result{
				Series: []*v3.Series{
					{
						Labels: map[string]string{
							"host_name": "ip-10-420-69-1",
							"state":     "running",
						},
						Points: []v3.Point{
							{
								Timestamp: 1,
								Value:     float64(10) / float64(22),
							},
							{
								Timestamp: 2,
								Value:     0.3076923076923077,
							},
							{
								Timestamp: 3,
								Value:     0,
							},
							{
								Timestamp: 4,
								Value:     1,
							},
							{
								Timestamp: 5,
								Value:     1,
							},
							{
								Timestamp: 7,
								Value:     math.Inf(1),
							},
						},
					},
					{
						Labels: map[string]string{
							"host_name": "ip-10-420-69-2",
							"state":     "idle",
						},
						Points: []v3.Point{
							{
								Timestamp: 1,
								Value:     0.5454545454545454,
							},
							{
								Timestamp: 2,
								Value:     0.6923076923076923,
							},
							{
								Timestamp: 3,
								Value:     math.Inf(1),
							},
							{
								Timestamp: 4,
								Value:     1,
							},
							{
								Timestamp: 5,
								Value:     1,
							},
						},
					},
				},
			},
		},
		{
			name:       "Group keys on both sides are same but different values",
			expression: "A/B",
			results: []*v3.Result{
				{
					QueryName: "A",
					Series: []*v3.Series{
						{
							Labels: map[string]string{
								"host_name": "ip-10-420-69-1",
								"state":     "running",
							},
							Points: []v3.Point{
								{
									Timestamp: 1,
									Value:     10,
								},
								{
									Timestamp: 2,
									Value:     20,
								},
								{
									Timestamp: 4,
									Value:     40,
								},
								{
									Timestamp: 5,
									Value:     50,
								},
								{
									Timestamp: 7,
									Value:     70,
								},
							},
						},
						{
							Labels: map[string]string{
								"host_name": "ip-10-420-69-2",
								"state":     "idle",
							},
							Points: []v3.Point{
								{
									Timestamp: 1,
									Value:     12,
								},
								{
									Timestamp: 2,
									Value:     45,
								},
								{
									Timestamp: 3,
									Value:     30,
								},
								{
									Timestamp: 4,
									Value:     40,
								},
								{
									Timestamp: 5,
									Value:     50,
								},
							},
						},
					},
				},
				{
					QueryName: "B",
					Series: []*v3.Series{
						{
							Labels: map[string]string{
								"host_name": "ip-10-420-69-1",
								"state":     "not_running_chalamet",
							},
							Points: []v3.Point{
								{
									Timestamp: 1,
									Value:     22,
								},
								{
									Timestamp: 2,
									Value:     65,
								},
								{
									Timestamp: 3,
									Value:     30,
								},
								{
									Timestamp: 4,
									Value:     40,
								},
								{
									Timestamp: 5,
									Value:     50,
								},
							},
						},
						{
							Labels: map[string]string{
								"host_name": "ip-10-420-69-2",
								"state":     "busy",
							},
							Points: []v3.Point{
								{
									Timestamp: 1,
									Value:     22,
								},
								{
									Timestamp: 2,
									Value:     65,
								},
								{
									Timestamp: 4,
									Value:     40,
								},
								{
									Timestamp: 5,
									Value:     50,
								},
							},
						},
					},
				},
			},
			want: &v3.Result{
				Series: []*v3.Series{
					{
						Labels: map[string]string{
							"host_name": "ip-10-420-69-1",
							"state":     "running",
						},
						Points: []v3.Point{
							{
								Timestamp: 1,
								Value:     math.Inf(0),
							},
							{
								Timestamp: 2,
								Value:     math.Inf(0),
							},
							{
								Timestamp: 4,
								Value:     math.Inf(0),
							},
							{
								Timestamp: 5,
								Value:     math.Inf(0),
							},
							{
								Timestamp: 7,
								Value:     math.Inf(0),
							},
						},
					},
					{
						Labels: map[string]string{
							"host_name": "ip-10-420-69-2",
							"state":     "idle",
						},
						Points: []v3.Point{
							{
								Timestamp: 1,
								Value:     math.Inf(0),
							},
							{
								Timestamp: 2,
								Value:     math.Inf(0),
							},
							{
								Timestamp: 3,
								Value:     math.Inf(0),
							},
							{
								Timestamp: 4,
								Value:     math.Inf(0),
							},
							{
								Timestamp: 5,
								Value:     math.Inf(0),
							},
						},
					},
					{
						Labels: map[string]string{
							"host_name": "ip-10-420-69-1",
							"state":     "not_running_chalamet",
						},
						Points: []v3.Point{
							{
								Timestamp: 1,
								Value:     0,
							},
							{
								Timestamp: 2,
								Value:     0,
							},
							{
								Timestamp: 3,
								Value:     0,
							},
							{
								Timestamp: 4,
								Value:     0,
							},
							{
								Timestamp: 5,
								Value:     0,
							},
						},
					},
					{
						Labels: map[string]string{
							"host_name": "ip-10-420-69-2",
							"state":     "busy",
						},
						Points: []v3.Point{
							{
								Timestamp: 1,
								Value:     0,
							},
							{
								Timestamp: 2,
								Value:     0,
							},
							{
								Timestamp: 4,
								Value:     0,
							},
							{
								Timestamp: 5,
								Value:     0,
							},
						},
					},
				},
			},
		},
		{
			name:       "Group keys on the left side are a superset of the right side",
			expression: "A/B",
			results: []*v3.Result{
				{
					QueryName: "A",
					Series: []*v3.Series{
						{
							Labels: map[string]string{
								"host_name": "ip-10-420-69-1",
								"state":     "running",
								"os.type":   "linux",
							},
							Points: []v3.Point{
								{
									Timestamp: 1,
									Value:     10,
								},
								{
									Timestamp: 2,
									Value:     20,
								},
								{
									Timestamp: 4,
									Value:     40,
								},
								{
									Timestamp: 5,
									Value:     50,
								},
								{
									Timestamp: 7,
									Value:     70,
								},
							},
						},
						{
							Labels: map[string]string{
								"host_name": "ip-10-420-69-2",
								"state":     "idle",
								"os.type":   "linux",
							},
							Points: []v3.Point{
								{
									Timestamp: 1,
									Value:     12,
								},
								{
									Timestamp: 2,
									Value:     45,
								},
								{
									Timestamp: 3,
									Value:     30,
								},
								{
									Timestamp: 4,
									Value:     40,
								},
								{
									Timestamp: 5,
									Value:     50,
								},
							},
						},
					},
				},
				{
					QueryName: "B",
					Series: []*v3.Series{
						{
							Labels: map[string]string{
								"state":   "running",
								"os.type": "linux",
							},
							Points: []v3.Point{
								{
									Timestamp: 1,
									Value:     22,
								},
								{
									Timestamp: 2,
									Value:     65,
								},
								{
									Timestamp: 3,
									Value:     30,
								},
								{
									Timestamp: 4,
									Value:     40,
								},
								{
									Timestamp: 5,
									Value:     50,
								},
							},
						},
						{
							Labels: map[string]string{
								"state":   "busy",
								"os.type": "linux",
							},
							Points: []v3.Point{
								{
									Timestamp: 1,
									Value:     22,
								},
								{
									Timestamp: 2,
									Value:     65,
								},
								{
									Timestamp: 4,
									Value:     40,
								},
								{
									Timestamp: 5,
									Value:     50,
								},
							},
						},
					},
				},
			},
			want: &v3.Result{
				Series: []*v3.Series{
					{
						Labels: map[string]string{
							"host_name": "ip-10-420-69-1",
							"state":     "running",
							"os.type":   "linux",
						},
						Points: []v3.Point{
							{
								Timestamp: 1,
								Value:     float64(10) / float64(22),
							},
							{
								Timestamp: 2,
								Value:     0.3076923076923077,
							},
							{
								Timestamp: 3,
								Value:     0,
							},
							{
								Timestamp: 4,
								Value:     1,
							},
							{
								Timestamp: 5,
								Value:     1,
							},
							{
								Timestamp: 7,
								Value:     math.Inf(1),
							},
						},
					},
					{
						Labels: map[string]string{
							"host_name": "ip-10-420-69-2",
							"state":     "idle",
							"os.type":   "linux",
						},
						Points: []v3.Point{
							{
								Timestamp: 1,
								Value:     math.Inf(0),
							},
							{
								Timestamp: 2,
								Value:     math.Inf(0),
							},
							{
								Timestamp: 3,
								Value:     math.Inf(0),
							},
							{
								Timestamp: 4,
								Value:     math.Inf(0),
							},
							{
								Timestamp: 5,
								Value:     math.Inf(0),
							},
						},
					},
					{
						Labels: map[string]string{
							"state":   "busy",
							"os.type": "linux",
						},
						Points: []v3.Point{
							{
								Timestamp: 1,
								Value:     0,
							},
							{
								Timestamp: 2,
								Value:     0,
							},
							{
								Timestamp: 4,
								Value:     0,
							},
							{
								Timestamp: 5,
								Value:     0,
							},
						},
					},
				},
			},
		},
		{
			name:       "Group keys are subsets, A is a subset of B and their result is a subset of C",
			expression: "A/B + C",
			results: []*v3.Result{
				{
					QueryName: "A",
					Series: []*v3.Series{
						{
							Labels: map[string]string{
								"state": "running",
							},
							Points: []v3.Point{
								{
									Timestamp: 1,
									Value:     10,
								},
								{
									Timestamp: 2,
									Value:     20,
								},
								{
									Timestamp: 4,
									Value:     40,
								},
								{
									Timestamp: 5,
									Value:     50,
								},
								{
									Timestamp: 7,
									Value:     70,
								},
							},
						},
						{
							Labels: map[string]string{
								"state": "idle",
							},
							Points: []v3.Point{
								{
									Timestamp: 1,
									Value:     12,
								},
								{
									Timestamp: 2,
									Value:     45,
								},
								{
									Timestamp: 3,
									Value:     30,
								},
								{
									Timestamp: 4,
									Value:     40,
								},
								{
									Timestamp: 5,
									Value:     50,
								},
							},
						},
					},
				},
				{
					QueryName: "B",
					Series: []*v3.Series{
						{
							Labels: map[string]string{
								"host_name": "ip-10-420-69-1",
								"state":     "running",
							},
							Points: []v3.Point{
								{
									Timestamp: 1,
									Value:     22,
								},
								{
									Timestamp: 2,
									Value:     65,
								},
								{
									Timestamp: 3,
									Value:     30,
								},
								{
									Timestamp: 4,
									Value:     40,
								},
								{
									Timestamp: 5,
									Value:     50,
								},
							},
						},
						{
							Labels: map[string]string{
								"host_name": "ip-10-420-69-2",
								"state":     "idle",
							},
							Points: []v3.Point{
								{
									Timestamp: 1,
									Value:     22,
								},
								{
									Timestamp: 2,
									Value:     65,
								},
								{
									Timestamp: 4,
									Value:     40,
								},
								{
									Timestamp: 5,
									Value:     50,
								},
							},
						},
					},
				},
				{
					QueryName: "C",
					Series: []*v3.Series{
						{
							Labels: map[string]string{
								"host_name": "ip-10-420-69-1",
								"state":     "running",
								"os.type":   "linux",
							},
							Points: []v3.Point{
								{
									Timestamp: 1,
									Value:     10,
								},
								{
									Timestamp: 2,
									Value:     20,
								},
								{
									Timestamp: 4,
									Value:     40,
								},
								{
									Timestamp: 5,
									Value:     50,
								},
								{
									Timestamp: 7,
									Value:     70,
								},
							},
						},
						{
							Labels: map[string]string{
								"host_name": "ip-10-420-69-2",
								"state":     "idle",
								"os.type":   "linux",
							},
							Points: []v3.Point{
								{
									Timestamp: 1,
									Value:     12,
								},
								{
									Timestamp: 2,
									Value:     45,
								},
								{
									Timestamp: 3,
									Value:     30,
								},
								{
									Timestamp: 4,
									Value:     40,
								},
								{
									Timestamp: 5,
									Value:     50,
								},
							},
						},
					},
				},
			},
			want: &v3.Result{
				Series: []*v3.Series{
					{
						Labels: map[string]string{
							"host_name": "ip-10-420-69-1",
							"state":     "running",
							"os.type":   "linux",
						},
						Points: []v3.Point{
							{
								Timestamp: 1,
								Value:     10.45454545454545453,
							},
							{
								Timestamp: 2,
								Value:     20.3076923076923077,
							},
							{
								Timestamp: 3,
								Value:     0,
							},
							{
								Timestamp: 4,
								Value:     41,
							},
							{
								Timestamp: 5,
								Value:     51,
							},
							{
								Timestamp: 7,
								Value:     math.Inf(1),
							},
						},
					},
					{
						Labels: map[string]string{
							"host_name": "ip-10-420-69-2",
							"state":     "idle",
							"os.type":   "linux",
						},
						Points: []v3.Point{
							{
								Timestamp: 1,
								Value:     12.5454545454545454,
							},
							{
								Timestamp: 2,
								Value:     45.6923076923076923,
							},
							{
								Timestamp: 3,
								Value:     math.Inf(1),
							},
							{
								Timestamp: 4,
								Value:     41,
							},
							{
								Timestamp: 5,
								Value:     51,
							},
						},
					},
				},
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			expression, err := govaluate.NewEvaluableExpression(tt.expression)
			if err != nil {
				t.Errorf("Error parsing expression: %v", err)
				return
			}
			got, err := processResults(tt.results, expression)
			if err != nil {
				t.Errorf("Error processing results: %v", err)
				return
			}
			if len(got.Series) != len(tt.want.Series) {
				t.Errorf("processResults(): number of series - got = %v, want %v", len(got.Series), len(tt.want.Series))
				return
			}

			for i := range got.Series {
				if len(got.Series[i].Points) != len(tt.want.Series[i].Points) {
					t.Errorf("processResults(): number of points - got = %v, want %v", len(got.Series[i].Points), len(tt.want.Series[i].Points))
					return
				}
				for j := range got.Series[i].Points {
					if got.Series[i].Points[j].Value != tt.want.Series[i].Points[j].Value {
						t.Errorf("processResults(): got = %v, want %v", got.Series[i].Points[j].Value, tt.want.Series[i].Points[j].Value)
					}
				}
			}
		})
	}
}
