package promrelabel

import (
	"bytes"
	"encoding/json"
	"testing"

	"github.com/VictoriaMetrics/VictoriaMetrics/lib/promutil"
	"gopkg.in/yaml.v2"
)

func TestIfExpressionParseFailure(t *testing.T) {
	f := func(s string) {
		t.Helper()

		var ie IfExpression
		if err := ie.Parse(s); err == nil {
			t.Fatalf("expecting non-nil error when parsing %q", s)
		}
	}

	f(`{`)
	f(`{foo`)
	f(`foo{`)
	f(`foo{bar="a" or}`)
}

func TestIfExpressionParseSuccess(t *testing.T) {
	f := func(s string) {
		t.Helper()

		var ie IfExpression
		if err := ie.Parse(s); err != nil {
			t.Fatalf("unexpected error: %s", err)
		}
	}

	f(`foo`)
	f(`{foo="bar"}`)
	f(`foo{bar=~"baz", x!="y"}`)
	f(`{a="b" or c="d",e="x"}`)
	f(`foo{
  bar="a",x="y" or
  x="a",a="b" or
  a="x"
}`)
}

func TestIfExpressionMarshalUnmarshalJSON(t *testing.T) {
	f := func(s, jsonExpected string) {
		t.Helper()

		var ie IfExpression
		if err := ie.Parse(s); err != nil {
			t.Fatalf("cannot parse ifExpression %q: %s", s, err)
		}
		data, err := json.Marshal(&ie)
		if err != nil {
			t.Fatalf("cannot marshal ifExpression %q: %s", s, err)
		}
		if string(data) != jsonExpected {
			t.Fatalf("unexpected value after json marshaling;\ngot\n%s\nwant\n%s", data, jsonExpected)
		}
		var ie2 IfExpression
		if err := json.Unmarshal(data, &ie2); err != nil {
			t.Fatalf("cannot unmarshal ifExpression from json %q: %s", data, err)
		}
		data2, err := json.Marshal(&ie2)
		if err != nil {
			t.Fatalf("cannot marshal ifExpression2: %s", err)
		}
		if string(data2) != jsonExpected {
			t.Fatalf("unexpected data after unmarshal/marshal cycle;\ngot\n%s\nwant\n%s", data2, jsonExpected)
		}
	}

	f("foo", `"foo"`)
	f(`{foo="bar",baz=~"x.*"}`, `"{foo=\"bar\",baz=~\"x.*\"}"`)
	f(`{a="b" or c="d",x="z"}`, `"{a=\"b\" or c=\"d\",x=\"z\"}"`)
}

func TestIfExpressionUnmarshalFailure(t *testing.T) {
	f := func(s string) {
		t.Helper()

		var ie IfExpression
		err := yaml.UnmarshalStrict([]byte(s), &ie)
		if err == nil {
			t.Fatalf("expecting non-nil error")
		}
	}

	f(`{`)
	f(`{x:y}`)
	f(`[1]`)
	f(`"{"`)
	f(`'{'`)
	f(`foo{bar`)
	f(`foo{bar}`)
	f(`foo{bar=`)
	f(`foo{bar="`)
	f(`foo{bar='`)
	f(`foo{bar=~"("}`)
	f(`foo{bar!~"("}`)
	f(`foo{bar==aaa}`)
	f(`foo{bar=="b"}`)
	f(`'foo+bar'`)
	f(`'foo{bar=~"a[b"}'`)
}

func TestIfExpressionUnmarshalSuccess(t *testing.T) {
	f := func(s string) {
		t.Helper()

		var ie IfExpression
		if err := yaml.UnmarshalStrict([]byte(s), &ie); err != nil {
			t.Fatalf("unexpected error during unmarshal: %s", err)
		}
		b, err := yaml.Marshal(&ie)
		if err != nil {
			t.Fatalf("unexpected error during marshal: %s", err)
		}
		b = bytes.TrimSpace(b)
		if string(b) != s {
			t.Fatalf("unexpected marshaled data;\ngot\n%s\nwant\n%s", b, s)
		}
	}

	f(`'{}'`)
	f(`foo`)
	f(`foo{bar="baz"}`)
	f(`'{a="b", c!="d", e=~"g", h!~"d"}'`)
	f(`foo{bar="zs",a=~"b|c"}`)
	f(`foo{z="y" or bar="zs",a=~"b|c"}`)
	f(`- foo
- bar{baz="abc"}`)
}

func TestIfExpressionString(t *testing.T) {
	f := func(s, resultExpected string) {
		t.Helper()

		var ie IfExpression
		if err := yaml.UnmarshalStrict([]byte(s), &ie); err != nil {
			t.Fatalf("cannot unmarshal if expression: %s", err)
		}
		result := ie.String()
		if result != resultExpected {
			t.Fatalf("unexpected result\ngot\n%s\nwant\n%s", result, resultExpected)
		}
	}

	// empty filters
	f(`'{}'`, `{}`)

	// multiple filters
	f(`foo{bar="baz",a=~"bc.+",d!="e",g!~".*qwe"}`, `foo{bar="baz",a=~"bc.+",d!="e",g!~".*qwe"}`)

	// multiple if expressions
	f(`- foo
- bar{baz="abc"}`, `foo,bar{baz="abc"}`)
}

func TestIfExpressionMatch(t *testing.T) {
	f := func(ifExpr, metricWithLabels string) {
		t.Helper()

		var ie IfExpression
		if err := yaml.UnmarshalStrict([]byte(ifExpr), &ie); err != nil {
			t.Fatalf("unexpected error during unmarshal: %s", err)
		}
		labels := promutil.MustNewLabelsFromString(metricWithLabels)
		if !ie.Match(labels.GetLabels()) {
			t.Fatalf("unexpected mismatch of ifExpr=%s for %s", ifExpr, metricWithLabels)
		}
	}

	f(`foo`, `foo`)
	f(`foo`, `foo{bar="baz",a="b"}`)
	f(`foo{bar="a"}`, `foo{bar="a"}`)
	f(`foo{bar="a" or baz="x"}`, `foo{bar="a"}`)
	f(`foo{baz="x" or bar="a"}`, `foo{bar="a"}`)
	f(`foo{bar="a"}`, `foo{x="y",bar="a",baz="b"}`)
	f(`'{a=~"x|abc",y!="z"}'`, `m{x="aa",a="abc"}`)
	f(`'{a=~"x|abc",y!="z"}'`, `m{x="aa",a="abc",y="qwe"}`)
	f(`'{__name__="foo"}'`, `foo{bar="baz"}`)
	f(`'{__name__=~"foo|bar"}'`, `bar`)
	f(`'{__name__!=""}'`, `foo`)
	f(`'{__name__!=""}'`, `bar{baz="aa",b="c"}`)
	f(`'{__name__!~"a.+"}'`, `bar{baz="aa",b="c"}`)
	f(`foo{a!~"a.+"}`, `foo{a="baa"}`)
	f(`'{foo=""}'`, `bar`)
	f(`'{foo!=""}'`, `aa{foo="b"}`)
	f(`'{foo=~".*"}'`, `abc`)
	f(`'{foo=~".*"}'`, `abc{foo="bar"}`)
	f(`'{foo!~".+"}'`, `abc`)
	f(`'{foo=~"bar|"}'`, `abc`)
	f(`'{foo=~"bar|"}'`, `abc{foo="bar"}`)
	f(`'{foo!~"bar|"}'`, `abc{foo="baz"}`)
}

func TestIfExpressionMismatch(t *testing.T) {
	f := func(ifExpr, metricWithLabels string) {
		t.Helper()

		var ie IfExpression
		if err := yaml.UnmarshalStrict([]byte(ifExpr), &ie); err != nil {
			t.Fatalf("unexpected error during unmarshal: %s", err)
		}
		labels := promutil.MustNewLabelsFromString(metricWithLabels)
		if ie.Match(labels.GetLabels()) {
			t.Fatalf("unexpected match of ifExpr=%s for %s", ifExpr, metricWithLabels)
		}
	}

	f(`foo`, `bar`)
	f(`foo`, `a{foo="bar"}`)
	f(`foo{bar="a"}`, `foo`)
	f(`foo{bar="a" or baz="a"}`, `foo`)
	f(`foo{bar="a"}`, `foo{bar="b"}`)
	f(`foo{bar="a"}`, `foo{baz="b",a="b"}`)
	f(`'{a=~"x|abc",y!="z"}'`, `m{x="aa",a="xabc"}`)
	f(`'{a=~"x|abc",y!="z"}'`, `m{x="aa",a="abc",y="z"}`)
	f(`'{__name__!~".+"}'`, `foo`)
	f(`'{a!~"a.+"}'`, `foo{a="abc"}`)
	f(`'{foo=""}'`, `bar{foo="aa"}`)
	f(`'{foo!=""}'`, `aa`)
	f(`'{foo=~".+"}'`, `abc`)
	f(`'{foo!~".+"}'`, `abc{foo="x"}`)
	f(`'{foo=~"bar|"}'`, `abc{foo="baz"}`)
	f(`'{foo!~"bar|"}'`, `abc`)
	f(`'{foo!~"bar|"}'`, `abc{foo="bar"}`)
}
