package metrics

import (
	"reflect"
	"testing"

	"github.com/hikhvar/mqtt2prometheus/pkg/config"
	"github.com/prometheus/client_golang/prometheus"
)

func TestNewJSONObjectExtractor_parseMetric(t *testing.T) {
	now = testNow
	type fields struct {
		metricConfigs map[string][]*config.MetricConfig
	}
	type args struct {
		metricPath string
		deviceID   string
		value      string
	}
	tests := []struct {
		name      string
		separator string
		fields    fields
		args      args
		want      Metric
		wantErr   bool
		noValue   bool
	}{
		{
			name:      "string value",
			separator: "->",
			fields: fields{
				map[string][]*config.MetricConfig{
					"SDS0X1->PM2->5": {
						{
							PrometheusName: "temperature",
							MQTTName:       "SDS0X1.PM2.5",
							ValueType:      "gauge",
						},
					},
				},
			},
			args: args{
				metricPath: "topic",
				deviceID:   "dht22",
				value:      "{\"SDS0X1\":{\"PM2\":{\"5\":4.9}}}",
			},
			want: Metric{
				Description: prometheus.NewDesc("temperature", "", []string{"sensor", "topic"}, nil),
				ValueType:   prometheus.GaugeValue,
				Value:       4.9,
				IngestTime:  testNow(),
				Topic:       "topic",
			},
		}, {
			name:      "string value with dots in path",
			separator: "->",
			fields: fields{
				map[string][]*config.MetricConfig{
					"SDS0X1->PM2.5": {
						{
							PrometheusName: "temperature",
							MQTTName:       "SDS0X1->PM2.5",
							ValueType:      "gauge",
						},
					},
				},
			},
			args: args{
				metricPath: "topic",
				deviceID:   "dht22",
				value:      "{\"SDS0X1\":{\"PM2.5\":4.9,\"PM10\":8.5}}",
			},
			want: Metric{
				Description: prometheus.NewDesc("temperature", "", []string{"sensor", "topic"}, nil),
				ValueType:   prometheus.GaugeValue,
				Value:       4.9,
				IngestTime:  testNow(),
				Topic:       "topic",
			},
		}, {
			name:      "metric matching SensorNameFilter",
			separator: ".",
			fields: fields{
				map[string][]*config.MetricConfig{
					"temperature": {
						{
							PrometheusName:   "temperature",
							MQTTName:         "temperature",
							ValueType:        "gauge",
							SensorNameFilter: *config.MustNewRegexp(".*22$"),
						},
					},
				},
			},
			args: args{
				metricPath: "topic",
				deviceID:   "dht22",
				value:      "{\"temperature\": 8.5}",
			},
			want: Metric{
				Description: prometheus.NewDesc("temperature", "", []string{"sensor", "topic"}, nil),
				ValueType:   prometheus.GaugeValue,
				Value:       8.5,
				IngestTime:  testNow(),
				Topic:       "topic",
			},
		}, {
			name:      "metric not matching SensorNameFilter",
			separator: ".",
			fields: fields{
				map[string][]*config.MetricConfig{
					"temperature": {
						{
							PrometheusName:   "temperature",
							MQTTName:         "temperature",
							ValueType:        "gauge",
							SensorNameFilter: *config.MustNewRegexp(".*fail$"),
						},
					},
				},
			},
			args: args{
				metricPath: "topic",
				deviceID:   "dht22",
				value:      "{\"temperature\": 8.5}",
			},
			want:    Metric{},
			noValue: true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			p := Parser{
				separator:     tt.separator,
				metricConfigs: tt.fields.metricConfigs,
			}
			extractor := NewJSONObjectExtractor(p)

			got, err := extractor(tt.args.metricPath, []byte(tt.args.value), tt.args.deviceID)
			if (err != nil) != tt.wantErr {
				t.Errorf("parseMetric() error = %v, wantErr %v", err, tt.wantErr)
				return
			}

			if len(got) == 0 {
				if !tt.noValue {
					t.Errorf("parseMetric() got = %v, want %v", nil, tt.want)
				}
			} else if !reflect.DeepEqual(got[0], tt.want) {
				t.Errorf("parseMetric() got = %v, want %v", got[0], tt.want)
			} else if len(got) > 1 {
				t.Errorf("unexpected result got = %v, want %v", got, tt.want)
			}
		})
	}
}
