package envoy

import (
	"encoding/json"
	"reflect"
	"regexp"
	"strings"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

const (
	expectedSelfAdminCluster = `{
		"name": "self_admin",
		"connect_timeout": "5s",
		"type": "STATIC",
		"http_protocol_options": {},
		"hosts": [
			{
				"socket_address": {
					"address": "127.0.0.1",
					"port_value": 19000
				}
			}
		]
	}`
	expectedSelfAdminClusterNonLoopbackIP = `{
		"name": "self_admin",
		"connect_timeout": "5s",
		"type": "STATIC",
		"http_protocol_options": {},
		"hosts": [
			{
				"socket_address": {
					"address": "192.0.2.10",
					"port_value": 19002
				}
			}
		]
	}`
	expectedPromListener = `{
		"name": "envoy_prometheus_metrics_listener",
		"address": {
			"socket_address": {
				"address": "0.0.0.0",
				"port_value": 9000
			}
		},
		"filter_chains": [
			{
				"filters": [
					{
						"name": "envoy.http_connection_manager",
						"config": {
							"stat_prefix": "envoy_prometheus_metrics",
							"codec_type": "HTTP1",
							"route_config": {
								"name": "self_admin_route",
								"virtual_hosts": [
									{
										"name": "self_admin",
										"domains": [
											"*"
										],
										"routes": [
											{
												"match": {
													"path": "/metrics"
												},
												"route": {
													"cluster": "self_admin",
													"prefix_rewrite": "/stats/prometheus"
												}
											},
											{
												"match": {
													"prefix": "/"
												},
												"direct_response": {
													"status": 404
												}
											}
										]
									}
								]
							},
							"http_filters": [
								{
									"name": "envoy.router"
								}
							]
						}
					}
				]
			}
		]
	}`
	expectedStatsListener = `{
		"name": "envoy_metrics_listener",
		"address": {
			"socket_address": {
				"address": "0.0.0.0",
				"port_value": 9000
			}
		},
		"filter_chains": [
			{
				"filters": [
					{
						"name": "envoy.http_connection_manager",
						"config": {
							"stat_prefix": "envoy_metrics",
							"codec_type": "HTTP1",
							"route_config": {
								"name": "self_admin_route",
								"virtual_hosts": [
									{
										"name": "self_admin",
										"domains": [
											"*"
										],
										"routes": [
											{
												"match": {
													"prefix": "/stats"
												},
												"route": {
													"cluster": "self_admin",
													"prefix_rewrite": "/stats"
												}
											},
											{
												"match": {
													"prefix": "/"
												},
												"direct_response": {
													"status": 404
												}
											}
										]
									}
								]
							},
							"http_filters": [
								{
									"name": "envoy.router"
								}
							]
						}
					}
				]
			}
		]
	}`
	expectedReadyListener = `{
		"name": "envoy_ready_listener",
		"address": {
			"socket_address": {
				"address": "0.0.0.0",
				"port_value": 4444
			}
		},
		"filter_chains": [
			{
				"filters": [
					{
						"name": "envoy.http_connection_manager",
						"config": {
							"stat_prefix": "envoy_ready",
							"codec_type": "HTTP1",
							"route_config": {
								"name": "self_admin_route",
								"virtual_hosts": [
									{
										"name": "self_admin",
										"domains": [
											"*"
										],
										"routes": [
											{
												"match": {
													"path": "/ready"
												},
												"route": {
													"cluster": "self_admin",
													"prefix_rewrite": "/ready"
												}
											},
											{
												"match": {
													"prefix": "/"
												},
												"direct_response": {
													"status": 404
												}
											}
										]
									}
								]
							},
							"http_filters": [
								{
									"name": "envoy.router"
								}
							]
						}
					}
				]
			}
		]
	}`
)

func TestBootstrapConfig_ConfigureArgs(t *testing.T) {
	defaultTags, err := generateStatsTags(&BootstrapTplArgs{}, nil, false)
	require.NoError(t, err)

	defaultTagsJSON := strings.Join(defaultTags, ",\n")
	defaultStatsConfigJSON := formatStatsTags(defaultTags)

	// The updated tags exclude the ones deprecated in Consul 1.9
	updatedTags, err := generateStatsTags(&BootstrapTplArgs{}, nil, true)
	require.NoError(t, err)

	updatedStatsConfigJSON := formatStatsTags(updatedTags)

	tests := []struct {
		name               string
		input              BootstrapConfig
		env                []string
		baseArgs           BootstrapTplArgs
		wantArgs           BootstrapTplArgs
		omitDeprecatedTags bool
		wantErr            bool
	}{
		{
			name:  "defaults",
			input: BootstrapConfig{},
			wantArgs: BootstrapTplArgs{
				StatsConfigJSON: defaultStatsConfigJSON,
			},
			wantErr: false,
		},
		{
			name: "extra-stats-sinks",
			input: BootstrapConfig{
				StatsSinksJSON: `{
					"name": "envoy.custom_exciting_sink",
					"config": {
						"foo": "bar"
					}
				}`,
			},
			wantArgs: BootstrapTplArgs{
				StatsConfigJSON: defaultStatsConfigJSON,
				StatsSinksJSON: `[{
					"name": "envoy.custom_exciting_sink",
					"config": {
						"foo": "bar"
					}
				}]`,
			},
		},
		{
			name: "simple-statsd-sink",
			input: BootstrapConfig{
				StatsdURL: "udp://127.0.0.1:9125",
			},
			wantArgs: BootstrapTplArgs{
				StatsConfigJSON: defaultStatsConfigJSON,
				StatsSinksJSON: `[{
					"name": "envoy.statsd",
					"config": {
						"address": {
							"socket_address": {
								"address": "127.0.0.1",
								"port_value": 9125
							}
						}
					}
				}]`,
			},
			wantErr: false,
		},
		{
			name: "simple-statsd-sink-plus-extra",
			input: BootstrapConfig{
				StatsdURL: "udp://127.0.0.1:9125",
				StatsSinksJSON: `{
					"name": "envoy.custom_exciting_sink",
					"config": {
						"foo": "bar"
					}
				}`,
			},
			wantArgs: BootstrapTplArgs{
				StatsConfigJSON: defaultStatsConfigJSON,
				StatsSinksJSON: `[{
					"name": "envoy.statsd",
					"config": {
						"address": {
							"socket_address": {
								"address": "127.0.0.1",
								"port_value": 9125
							}
						}
					}
				},
				{
					"name": "envoy.custom_exciting_sink",
					"config": {
						"foo": "bar"
					}
				}]`,
			},
			wantErr: false,
		},
		{
			name: "simple-statsd-sink-env",
			input: BootstrapConfig{
				StatsdURL: "$MY_STATSD_URL",
			},
			env: []string{"MY_STATSD_URL=udp://127.0.0.1:9125"},
			wantArgs: BootstrapTplArgs{
				StatsConfigJSON: defaultStatsConfigJSON,
				StatsSinksJSON: `[{
					"name": "envoy.statsd",
					"config": {
						"address": {
							"socket_address": {
								"address": "127.0.0.1",
								"port_value": 9125
							}
						}
					}
				}]`,
			},
			wantErr: false,
		},
		{
			name: "simple-dogstatsd-sink",
			input: BootstrapConfig{
				DogstatsdURL: "udp://127.0.0.1:9125",
			},
			wantArgs: BootstrapTplArgs{
				StatsConfigJSON: defaultStatsConfigJSON,
				StatsSinksJSON: `[{
					"name": "envoy.dog_statsd",
					"config": {
						"address": {
							"socket_address": {
								"address": "127.0.0.1",
								"port_value": 9125
							}
						}
					}
				}]`,
			},
			wantErr: false,
		},
		{
			name: "simple-dogstatsd-unix-sink",
			input: BootstrapConfig{
				DogstatsdURL: "unix:///var/run/dogstatsd.sock",
			},
			wantArgs: BootstrapTplArgs{
				StatsConfigJSON: defaultStatsConfigJSON,
				StatsSinksJSON: `[{
					"name": "envoy.dog_statsd",
					"config": {
						"address": {
							"pipe": {
								"path": "/var/run/dogstatsd.sock"
							}
						}
					}
				}]`,
			},
			wantErr: false,
		},

		{
			name: "simple-dogstatsd-sink-env",
			input: BootstrapConfig{
				DogstatsdURL: "$MY_STATSD_URL",
			},
			env: []string{"MY_STATSD_URL=udp://127.0.0.1:9125"},
			wantArgs: BootstrapTplArgs{
				StatsConfigJSON: defaultStatsConfigJSON,
				StatsSinksJSON: `[{
					"name": "envoy.dog_statsd",
					"config": {
						"address": {
							"socket_address": {
								"address": "127.0.0.1",
								"port_value": 9125
							}
						}
					}
				}]`,
			},
			wantErr: false,
		},
		{
			name: "stats-config-override",
			input: BootstrapConfig{
				StatsConfigJSON: `{
					"use_all_default_tags": true
				}`,
			},
			wantArgs: BootstrapTplArgs{
				StatsConfigJSON: `{
					"use_all_default_tags": true
				}`,
			},
			wantErr: false,
		},
		{
			name: "simple-tags",
			input: BootstrapConfig{
				StatsTags: []string{"canary", "foo=bar", "baz=2"},
			},
			wantArgs: BootstrapTplArgs{
				StatsConfigJSON: `{
					"stats_tags": [
						{
							"tag_name": "canary",
							"fixed_value": "1"
						},
						{
							"tag_name": "foo",
							"fixed_value": "bar"
						},
						{
							"tag_name": "baz",
							"fixed_value": "2"
						},
						` + defaultTagsJSON + `
					],
					"use_all_default_tags": true
				}`,
			},
			wantErr: false,
		},
		{
			name: "prometheus-bind-addr",
			input: BootstrapConfig{
				PrometheusBindAddr: "0.0.0.0:9000",
			},
			baseArgs: BootstrapTplArgs{
				AdminBindAddress: "127.0.0.1",
				AdminBindPort:    "19000",
			},
			wantArgs: BootstrapTplArgs{
				AdminBindAddress: "127.0.0.1",
				AdminBindPort:    "19000",
				// Should add a static cluster for the self-proxy to admin
				StaticClustersJSON: expectedSelfAdminCluster,
				// Should add a static http listener too
				StaticListenersJSON: expectedPromListener,
				StatsConfigJSON:     defaultStatsConfigJSON,
			},
			wantErr: false,
		},
		{
			name: "prometheus-bind-addr-non-loopback-ip",
			input: BootstrapConfig{
				PrometheusBindAddr: "0.0.0.0:9000",
			},
			baseArgs: BootstrapTplArgs{
				AdminBindAddress: "192.0.2.10",
				AdminBindPort:    "19002",
			},
			wantArgs: BootstrapTplArgs{
				AdminBindAddress: "192.0.2.10",
				AdminBindPort:    "19002",
				// Should add a static cluster for the self-proxy to admin
				StaticClustersJSON: expectedSelfAdminClusterNonLoopbackIP,
				// Should add a static http listener too
				StaticListenersJSON: expectedPromListener,
				StatsConfigJSON:     defaultStatsConfigJSON,
			},
			wantErr: false,
		},
		{
			name: "prometheus-bind-addr-with-overrides",
			input: BootstrapConfig{
				PrometheusBindAddr:  "0.0.0.0:9000",
				StaticClustersJSON:  `{"foo":"bar"}`,
				StaticListenersJSON: `{"baz":"qux"}`,
			},
			baseArgs: BootstrapTplArgs{
				AdminBindAddress: "127.0.0.1",
				AdminBindPort:    "19000",
			},
			wantArgs: BootstrapTplArgs{
				AdminBindAddress: "127.0.0.1",
				AdminBindPort:    "19000",
				// Should add a static cluster for the self-proxy to admin
				StaticClustersJSON: `{"foo":"bar"},` + expectedSelfAdminCluster,
				// Should add a static http listener too
				StaticListenersJSON: `{"baz":"qux"},` + expectedPromListener,
				StatsConfigJSON:     defaultStatsConfigJSON,
			},
			wantErr: false,
		},
		{
			name: "stats-bind-addr",
			input: BootstrapConfig{
				StatsBindAddr: "0.0.0.0:9000",
			},
			baseArgs: BootstrapTplArgs{
				AdminBindAddress: "127.0.0.1",
				AdminBindPort:    "19000",
			},
			wantArgs: BootstrapTplArgs{
				AdminBindAddress: "127.0.0.1",
				AdminBindPort:    "19000",
				// Should add a static cluster for the self-proxy to admin
				StaticClustersJSON: expectedSelfAdminCluster,
				// Should add a static http listener too
				StaticListenersJSON: expectedStatsListener,
				StatsConfigJSON:     defaultStatsConfigJSON,
			},
			wantErr: false,
		},
		{
			name: "stats-bind-addr-with-overrides",
			input: BootstrapConfig{
				StatsBindAddr:       "0.0.0.0:9000",
				StaticClustersJSON:  `{"foo":"bar"}`,
				StaticListenersJSON: `{"baz":"qux"}`,
			},
			baseArgs: BootstrapTplArgs{
				AdminBindAddress: "127.0.0.1",
				AdminBindPort:    "19000",
			},
			wantArgs: BootstrapTplArgs{
				AdminBindAddress: "127.0.0.1",
				AdminBindPort:    "19000",
				// Should add a static cluster for the self-proxy to admin
				StaticClustersJSON: `{"foo":"bar"},` + expectedSelfAdminCluster,
				// Should add a static http listener too
				StaticListenersJSON: `{"baz":"qux"},` + expectedStatsListener,
				StatsConfigJSON:     defaultStatsConfigJSON,
			},
			wantErr: false,
		},
		{
			name: "stats-flush-interval",
			input: BootstrapConfig{
				StatsFlushInterval: `10s`,
			},
			wantArgs: BootstrapTplArgs{
				StatsConfigJSON:    defaultStatsConfigJSON,
				StatsFlushInterval: `10s`,
			},
			wantErr: false,
		},
		{
			name: "override-tracing",
			input: BootstrapConfig{
				TracingConfigJSON: `{"foo": "bar"}`,
			},
			wantArgs: BootstrapTplArgs{
				StatsConfigJSON:   defaultStatsConfigJSON,
				TracingConfigJSON: `{"foo": "bar"}`,
			},
			wantErr: false,
		},
		{
			name: "err-bad-prometheus-addr",
			input: BootstrapConfig{
				PrometheusBindAddr: "asdasdsad",
			},
			wantErr: true,
		},
		{
			name: "err-bad-stats-addr",
			input: BootstrapConfig{
				StatsBindAddr: "asdasdsad",
			},
			wantErr: true,
		},
		{
			name: "err-bad-statsd-addr",
			input: BootstrapConfig{
				StatsdURL: "asdasdsad",
			},
			wantErr: true,
		},
		{
			name: "err-bad-dogstatsd-addr",
			input: BootstrapConfig{
				DogstatsdURL: "asdasdsad",
			},
			wantErr: true,
		},
		{
			name: "ready-bind-addr",
			input: BootstrapConfig{
				ReadyBindAddr: "0.0.0.0:4444",
			},
			baseArgs: BootstrapTplArgs{
				AdminBindAddress: "127.0.0.1",
				AdminBindPort:    "19000",
			},
			wantArgs: BootstrapTplArgs{
				AdminBindAddress: "127.0.0.1",
				AdminBindPort:    "19000",
				// Should add a static cluster for the self-proxy to admin
				StaticClustersJSON: expectedSelfAdminCluster,
				// Should add a static http listener too
				StaticListenersJSON: expectedReadyListener,
				StatsConfigJSON:     defaultStatsConfigJSON,
			},
			wantErr: false,
		},
		{
			name: "ready-bind-addr-with-overrides",
			input: BootstrapConfig{
				ReadyBindAddr:       "0.0.0.0:4444",
				StaticClustersJSON:  `{"foo":"bar"}`,
				StaticListenersJSON: `{"baz":"qux"}`,
			},
			baseArgs: BootstrapTplArgs{
				AdminBindAddress: "127.0.0.1",
				AdminBindPort:    "19000",
			},
			wantArgs: BootstrapTplArgs{
				AdminBindAddress: "127.0.0.1",
				AdminBindPort:    "19000",
				// Should add a static cluster for the self-proxy to admin
				StaticClustersJSON: `{"foo":"bar"},` + expectedSelfAdminCluster,
				// Should add a static http listener too
				StaticListenersJSON: `{"baz":"qux"},` + expectedReadyListener,
				StatsConfigJSON:     defaultStatsConfigJSON,
			},
			wantErr: false,
		},
		{
			name: "ready-bind-addr-and-prometheus-and-stats",
			input: BootstrapConfig{
				ReadyBindAddr:      "0.0.0.0:4444",
				PrometheusBindAddr: "0.0.0.0:9000",
				StatsBindAddr:      "0.0.0.0:9000",
			},
			baseArgs: BootstrapTplArgs{
				AdminBindAddress: "127.0.0.1",
				AdminBindPort:    "19000",
			},
			wantArgs: BootstrapTplArgs{
				AdminBindAddress: "127.0.0.1",
				AdminBindPort:    "19000",
				// Should add a static cluster for the self-proxy to admin
				StaticClustersJSON: expectedSelfAdminCluster,
				// Should add a static http listener too
				StaticListenersJSON: strings.Join(
					[]string{expectedPromListener, expectedStatsListener, expectedReadyListener},
					", ",
				),
				StatsConfigJSON: defaultStatsConfigJSON,
			},
			wantErr: false,
		},
		{
			name: "omit-deprecated-tags",
			input: BootstrapConfig{
				ReadyBindAddr:      "0.0.0.0:4444",
				PrometheusBindAddr: "0.0.0.0:9000",
				StatsBindAddr:      "0.0.0.0:9000",
			},
			baseArgs: BootstrapTplArgs{
				AdminBindAddress: "127.0.0.1",
				AdminBindPort:    "19000",
			},
			omitDeprecatedTags: true,
			wantArgs: BootstrapTplArgs{
				AdminBindAddress:   "127.0.0.1",
				AdminBindPort:      "19000",
				StaticClustersJSON: expectedSelfAdminCluster,
				StaticListenersJSON: strings.Join(
					[]string{expectedPromListener, expectedStatsListener, expectedReadyListener},
					", ",
				),
				// Should not have default stats config JSON when deprecated tags are omitted
				StatsConfigJSON: updatedStatsConfigJSON,
			},
			wantErr: false,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			args := tt.baseArgs

			defer testSetAndResetEnv(t, tt.env)()

			err := tt.input.ConfigureArgs(&args, tt.omitDeprecatedTags)
			if tt.wantErr {
				require.Error(t, err)
			} else {
				require.NoError(t, err)

				// Want to compare JSON fields with JSONEq
				argV := reflect.ValueOf(args)
				wantV := reflect.ValueOf(tt.wantArgs)
				argT := reflect.TypeOf(args)
				for i := 0; i < argT.NumField(); i++ {
					f := argT.Field(i)
					if strings.HasSuffix(f.Name, "JSON") && wantV.Field(i).String() != "" {
						// Some of our JSON strings are comma separated objects to be
						// insertedinto an array which is not valid JSON on it's own so wrap
						// them all in an array. For simple values this is still valid JSON
						// too.
						want := "[" + wantV.Field(i).String() + "]"
						got := "[" + argV.Field(i).String() + "]"
						require.JSONEq(t, want, got, "field %s should be equivalent JSON", f.Name)
					} else {
						require.Equalf(t, wantV.Field(i).Interface(),
							argV.Field(i).Interface(), "field %s should be equal", f.Name)
					}
				}
			}
		})
	}
}

func TestConsulTagSpecifiers(t *testing.T) {
	// Conveniently both envoy and Go use the re2 dialect of regular
	// expressions, so we can actually test the stats tag extraction regular
	// expressions right here!

	specs, err := resourceTagSpecifiers(false)
	require.NoError(t, err)

	specsNoDeprecated, err := resourceTagSpecifiers(true)
	require.NoError(t, err)

	type testPattern struct {
		name string
		r    *regexp.Regexp
	}

	parseSpecs := func(specs []string) []testPattern {
		var patterns []testPattern
		for _, spec := range specs {
			var m struct {
				TagName string `json:"tag_name"`
				Regex   string `json:"regex"`
			}
			require.NoError(t, json.Unmarshal([]byte(spec), &m))

			patterns = append(patterns, testPattern{
				name: m.TagName,
				r:    regexp.MustCompile(m.Regex),
			})
		}
		return patterns
	}

	var (
		patterns             = parseSpecs(specs)
		patternsNoDeprecated = parseSpecs(specsNoDeprecated)
	)

	type testcase struct {
		name               string
		stat               string
		expect             map[string][]string // this is the m[1:] of the match
		expectNoDeprecated map[string][]string // this is the m[1:] of the match
	}

	cases := []testcase{
		{
			name: "cluster service",
			stat: "cluster.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.bind_errors",
			expect: map[string][]string{
				"consul.custom_hash":                {"pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", ""},
				"consul.datacenter":                 {"pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "dc2"},
				"consul.destination.custom_hash":    {"pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", ""},
				"consul.destination.datacenter":     {"pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "dc2"},
				"consul.destination.full_target":    {"pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648"},
				"consul.destination.namespace":      {"pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "default"},
				"consul.destination.routing_type":   {"pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "internal"},
				"consul.destination.service":        {"pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "pong"},
				"consul.destination.service_subset": {"pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", ""},
				"consul.destination.target":         {"pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "pong.default.dc2"},
				"consul.destination.trust_domain":   {"pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "e5b08d03-bfc3-c870-1833-baddb116e648"},
				"consul.full_target":                {"pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648"},
				"consul.namespace":                  {"pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "default"},
				"consul.routing_type":               {"pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "internal"},
				"consul.service":                    {"pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "pong"},
				"consul.service_subset":             {"pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", ""},
				"consul.target":                     {"pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "pong.default.dc2"},
				"consul.trust_domain":               {"pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "e5b08d03-bfc3-c870-1833-baddb116e648"},
			},
			expectNoDeprecated: map[string][]string{
				"consul.destination.custom_hash":    {"pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", ""},
				"consul.destination.datacenter":     {"pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "dc2"},
				"consul.destination.full_target":    {"pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648"},
				"consul.destination.namespace":      {"pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "default"},
				"consul.destination.routing_type":   {"pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "internal"},
				"consul.destination.service":        {"pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "pong"},
				"consul.destination.service_subset": {"pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", ""},
				"consul.destination.target":         {"pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "pong.default.dc2"},
				"consul.destination.trust_domain":   {"pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "e5b08d03-bfc3-c870-1833-baddb116e648"},
			},
		},
		{
			name: "cluster custom service",
			stat: "cluster.f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.bind_errors",
			expect: map[string][]string{
				"consul.custom_hash":                {"f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "f8f8f8f8"},
				"consul.datacenter":                 {"f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "dc2"},
				"consul.destination.custom_hash":    {"f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "f8f8f8f8"},
				"consul.destination.datacenter":     {"f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "dc2"},
				"consul.destination.full_target":    {"f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648"},
				"consul.destination.namespace":      {"f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "default"},
				"consul.destination.routing_type":   {"f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "internal"},
				"consul.destination.service":        {"f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "pong"},
				"consul.destination.service_subset": {"f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", ""},
				"consul.destination.target":         {"f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "f8f8f8f8~pong.default.dc2"},
				"consul.destination.trust_domain":   {"f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "e5b08d03-bfc3-c870-1833-baddb116e648"},
				"consul.full_target":                {"f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648"},
				"consul.namespace":                  {"f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "default"},
				"consul.routing_type":               {"f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "internal"},
				"consul.service":                    {"f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "pong"},
				"consul.service_subset":             {"f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", ""},
				"consul.target":                     {"f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "f8f8f8f8~pong.default.dc2"},
				"consul.trust_domain":               {"f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "e5b08d03-bfc3-c870-1833-baddb116e648"},
			},
			expectNoDeprecated: map[string][]string{
				"consul.destination.custom_hash":    {"f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "f8f8f8f8"},
				"consul.destination.datacenter":     {"f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "dc2"},
				"consul.destination.full_target":    {"f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648"},
				"consul.destination.namespace":      {"f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "default"},
				"consul.destination.routing_type":   {"f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "internal"},
				"consul.destination.service":        {"f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "pong"},
				"consul.destination.service_subset": {"f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", ""},
				"consul.destination.target":         {"f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "f8f8f8f8~pong.default.dc2"},
				"consul.destination.trust_domain":   {"f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "e5b08d03-bfc3-c870-1833-baddb116e648"},
			},
		},
		{
			name: "cluster service subset",
			stat: "cluster.v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.bind_errors",
			expect: map[string][]string{
				"consul.custom_hash":                {"v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", ""},
				"consul.datacenter":                 {"v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "dc2"},
				"consul.destination.custom_hash":    {"v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", ""},
				"consul.destination.datacenter":     {"v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "dc2"},
				"consul.destination.full_target":    {"v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648"},
				"consul.destination.namespace":      {"v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "default"},
				"consul.destination.routing_type":   {"v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "internal"},
				"consul.destination.service":        {"v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "pong"},
				"consul.destination.service_subset": {"v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "v2"},
				"consul.destination.target":         {"v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "v2.pong.default.dc2"},
				"consul.destination.trust_domain":   {"v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "e5b08d03-bfc3-c870-1833-baddb116e648"},
				"consul.full_target":                {"v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648"},
				"consul.namespace":                  {"v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "default"},
				"consul.routing_type":               {"v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "internal"},
				"consul.service":                    {"v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "pong"},
				"consul.service_subset":             {"v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "v2"},
				"consul.target":                     {"v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "v2.pong.default.dc2"},
				"consul.trust_domain":               {"v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "e5b08d03-bfc3-c870-1833-baddb116e648"},
			},
			expectNoDeprecated: map[string][]string{
				"consul.destination.custom_hash":    {"v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", ""},
				"consul.destination.datacenter":     {"v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "dc2"},
				"consul.destination.full_target":    {"v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648"},
				"consul.destination.namespace":      {"v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "default"},
				"consul.destination.routing_type":   {"v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "internal"},
				"consul.destination.service":        {"v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "pong"},
				"consul.destination.service_subset": {"v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "v2"},
				"consul.destination.target":         {"v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "v2.pong.default.dc2"},
				"consul.destination.trust_domain":   {"v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "e5b08d03-bfc3-c870-1833-baddb116e648"},
			},
		},
		{
			name: "cluster custom service subset",
			stat: "cluster.f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.bind_errors",
			expect: map[string][]string{
				"consul.custom_hash":                {"f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "f8f8f8f8"},
				"consul.datacenter":                 {"f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "dc2"},
				"consul.destination.custom_hash":    {"f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "f8f8f8f8"},
				"consul.destination.datacenter":     {"f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "dc2"},
				"consul.destination.full_target":    {"f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648"},
				"consul.destination.namespace":      {"f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "default"},
				"consul.destination.routing_type":   {"f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "internal"},
				"consul.destination.service":        {"f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "pong"},
				"consul.destination.service_subset": {"f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "v2"},
				"consul.destination.target":         {"f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "f8f8f8f8~v2.pong.default.dc2"},
				"consul.destination.trust_domain":   {"f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "e5b08d03-bfc3-c870-1833-baddb116e648"},
				"consul.full_target":                {"f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648"},
				"consul.namespace":                  {"f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "default"},
				"consul.routing_type":               {"f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "internal"},
				"consul.service":                    {"f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "pong"},
				"consul.service_subset":             {"f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "v2"},
				"consul.target":                     {"f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "f8f8f8f8~v2.pong.default.dc2"},
				"consul.trust_domain":               {"f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "e5b08d03-bfc3-c870-1833-baddb116e648"},
			},
			expectNoDeprecated: map[string][]string{
				"consul.destination.custom_hash":    {"f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "f8f8f8f8"},
				"consul.destination.datacenter":     {"f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "dc2"},
				"consul.destination.full_target":    {"f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648"},
				"consul.destination.namespace":      {"f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "default"},
				"consul.destination.routing_type":   {"f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "internal"},
				"consul.destination.service":        {"f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "pong"},
				"consul.destination.service_subset": {"f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "v2"},
				"consul.destination.target":         {"f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "f8f8f8f8~v2.pong.default.dc2"},
				"consul.destination.trust_domain":   {"f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "e5b08d03-bfc3-c870-1833-baddb116e648"},
			},
		},
		{
			name: "tcp listener no namespace",
			stat: "tcp.upstream.db.dc1.downstream_cx_total",
			expect: map[string][]string{
				"consul.upstream.datacenter": {"db.dc1.", "dc1"},
				"consul.upstream.namespace":  {"db.dc1.", ""},
				"consul.upstream.service":    {"db.dc1.", "db"},
			},
		},
		{
			name: "tcp listener with namespace",
			stat: "tcp.upstream.db.default.dc1.downstream_cx_total",
			expect: map[string][]string{
				"consul.upstream.datacenter": {"db.default.dc1.", "dc1"},
				"consul.upstream.namespace":  {"db.default.dc1.", "default"},
				"consul.upstream.service":    {"db.default.dc1.", "db"},
			},
		},
		{
			name: "http listener no namespace",
			stat: "http.upstream.web.dc1.downstream_cx_total",
			expect: map[string][]string{
				"consul.upstream.datacenter": {"web.dc1.", "dc1"},
				"consul.upstream.namespace":  {"web.dc1.", ""},
				"consul.upstream.service":    {"web.dc1.", "web"},
			},
		},
		{
			name: "http listener with namespace",
			stat: "http.upstream.web.default.dc1.downstream_cx_total",
			expect: map[string][]string{
				"consul.upstream.datacenter": {"web.default.dc1.", "dc1"},
				"consul.upstream.namespace":  {"web.default.dc1.", "default"},
				"consul.upstream.service":    {"web.default.dc1.", "web"},
			},
		},
	}

	for _, tc := range cases {
		tc := tc
		t.Run(tc.name, func(t *testing.T) {
			var (
				got             = make(map[string][]string)
				gotNoDeprecated = make(map[string][]string)
			)
			for _, p := range patterns {
				m := p.r.FindStringSubmatch(tc.stat)
				if len(m) > 1 {
					m = m[1:]
					got[p.name] = m
				}
			}
			for _, p := range patternsNoDeprecated {
				m := p.r.FindStringSubmatch(tc.stat)
				if len(m) > 1 {
					m = m[1:]
					gotNoDeprecated[p.name] = m
				}
			}

			if tc.expectNoDeprecated == nil {
				tc.expectNoDeprecated = tc.expect
			}

			assert.Equal(t, tc.expect, got)
			assert.Equal(t, tc.expectNoDeprecated, gotNoDeprecated)
		})
	}
}
