package utils

import (
	"reflect"
	"testing"

	"go.signoz.io/signoz/pkg/query-service/constants"
	v3 "go.signoz.io/signoz/pkg/query-service/model/v3"
)

type args struct {
	v        interface{}
	dataType v3.AttributeKeyDataType
}

var testValidateAndCastValueData = []struct {
	name    string
	args    args
	want    interface{}
	wantErr bool
}{
	// Test cases for v3.AttributeKeyDataTypeString
	{
		name: "v3.AttributeKeyDataTypeString: Valid string",
		args: args{
			v:        "test",
			dataType: v3.AttributeKeyDataTypeString,
		},
		want:    "test",
		wantErr: false,
	},
	{
		name: "v3.AttributeKeyDataTypeString: Valid int",
		args: args{
			v:        1,
			dataType: v3.AttributeKeyDataTypeString,
		},
		want:    "1",
		wantErr: false,
	},
	{
		name: "v3.AttributeKeyDataTypeString: Valid float32",
		args: args{
			v:        float32(1.1),
			dataType: v3.AttributeKeyDataTypeString,
		},
		want:    "1.1",
		wantErr: false,
	},
	{
		name: "v3.AttributeKeyDataTypeString: Valid float64",
		args: args{
			v:        float64(1.1),
			dataType: v3.AttributeKeyDataTypeString,
		},
		want:    "1.1",
		wantErr: false,
	},
	{
		name: "v3.AttributeKeyDataTypeString: Valid bool",
		args: args{
			v:        true,
			dataType: v3.AttributeKeyDataTypeString,
		},
		want:    "true",
		wantErr: false,
	},
	{
		name: "v3.AttributeKeyDataTypeString: Valid []interface{}",
		args: args{
			v:        []interface{}{"test", "test2"},
			dataType: v3.AttributeKeyDataTypeString,
		},
		want:    []interface{}{"test", "test2"},
		wantErr: false,
	},
	{
		name: "v3.AttributeKeyDataTypeString: Valid []interface{}",
		args: args{
			v:        []interface{}{"test", 1},
			dataType: v3.AttributeKeyDataTypeString,
		},
		want:    []interface{}{"test", "1"},
		wantErr: false,
	},
	{
		name: "v3.AttributeKeyDataTypeString: Invalid []interface{}",
		args: args{
			v:        []interface{}{"test", [1]string{"string Array"}},
			dataType: v3.AttributeKeyDataTypeString,
		},
		want:    nil,
		wantErr: true,
	},
	{
		name: "v3.AttributeKeyDataTypeString: Invalid type",
		args: args{
			v:        map[string]interface{}{"test": "test"},
			dataType: v3.AttributeKeyDataTypeString,
		},
		want:    nil,
		wantErr: true,
	},
	// Test cases for v3.AttributeKeyDataTypeBool
	{
		name: "v3.AttributeKeyDataTypeBool: Valid bool",
		args: args{
			v:        true,
			dataType: v3.AttributeKeyDataTypeBool,
		},
		want:    true,
		wantErr: false,
	},
	{
		name: "v3.AttributeKeyDataTypeBool: Valid string",
		args: args{
			v:        "true",
			dataType: v3.AttributeKeyDataTypeBool,
		},
		want:    true,
		wantErr: false,
	},
	{
		name: "v3.AttributeKeyDataTypeBool: Valid []interface{}",
		args: args{
			v:        []interface{}{"true", false},
			dataType: v3.AttributeKeyDataTypeBool,
		},
		want:    []interface{}{true, false},
		wantErr: false,
	},
	{
		name: "v3.AttributeKeyDataTypeBool: Invalid type",
		args: args{
			v:        1,
			dataType: v3.AttributeKeyDataTypeBool,
		},
		want:    nil,
		wantErr: true,
	},
	{
		name: "v3.AttributeKeyDataTypeBool: Invalid []interface{}",
		args: args{
			v:        []interface{}{1, false},
			dataType: v3.AttributeKeyDataTypeBool,
		},
		want:    nil,
		wantErr: true,
	},
	// Test cases for v3.AttributeKeyDataTypeInt64
	{
		name: "v3.AttributeKeyDataTypeInt64: Valid int",
		args: args{
			v:        1,
			dataType: v3.AttributeKeyDataTypeInt64,
		},
		want:    1,
		wantErr: false,
	},
	{
		name: "v3.AttributeKeyDataTypeInt64: Valid int64",
		args: args{
			v:        int64(1),
			dataType: v3.AttributeKeyDataTypeInt64,
		},
		want:    int64(1),
		wantErr: false,
	},
	{
		name: "v3.AttributeKeyDataTypeInt64: Valid string",
		args: args{
			v:        "1",
			dataType: v3.AttributeKeyDataTypeInt64,
		},
		want:    int64(1),
		wantErr: false,
	},
	{
		name: "v3.AttributeKeyDataTypeInt64: Valid []interface{}",
		args: args{
			v:        []interface{}{"1", 2},
			dataType: v3.AttributeKeyDataTypeInt64,
		},
		want:    []interface{}{int64(1), int64(2)},
		wantErr: false,
	},
	{
		name: "v3.AttributeKeyDataTypeInt64: Invalid []interface{}",
		args: args{
			v:        []interface{}{"1", false},
			dataType: v3.AttributeKeyDataTypeInt64,
		},
		want:    nil,
		wantErr: true,
	},
	{
		name: "v3.AttributeKeyDataTypeInt64: Invalid type",
		args: args{
			v:        true,
			dataType: v3.AttributeKeyDataTypeInt64,
		},
		want:    nil,
		wantErr: true,
	},
	// Test cases for v3.AttributeKeyDataTypeFloat64
	{
		name: "v3.AttributeKeyDataTypeFloat64: Valid float32",
		args: args{
			v:        float32(1.1),
			dataType: v3.AttributeKeyDataTypeFloat64,
		},
		want:    float32(1.1),
		wantErr: false,
	},
	{
		name: "v3.AttributeKeyDataTypeFloat64: Valid float64",
		args: args{
			v:        float64(1.1),
			dataType: v3.AttributeKeyDataTypeFloat64,
		},
		want:    float64(1.1),
		wantErr: false,
	},
	{
		name: "v3.AttributeKeyDataTypeFloat64: Valid int",
		args: args{
			v:        1,
			dataType: v3.AttributeKeyDataTypeFloat64,
		},
		want:    float64(1),
		wantErr: false,
	},
	{
		name: "v3.AttributeKeyDataTypeFloat64: Valid string",
		args: args{
			v:        "1.1",
			dataType: v3.AttributeKeyDataTypeFloat64,
		},
		want:    float64(1.1),
		wantErr: false,
	},
	{
		name: "v3.AttributeKeyDataTypeFloat: Valid []interface{}",
		args: args{
			v:        []interface{}{4, 3},
			dataType: v3.AttributeKeyDataTypeFloat64,
		},
		want:    []interface{}{float64(4), float64(3)},
		wantErr: false,
	},
	{
		name: "v3.AttributeKeyDataTypeFloat: Valid []interface{}",
		args: args{
			v:        []interface{}{4, "3"},
			dataType: v3.AttributeKeyDataTypeFloat64,
		},
		want:    []interface{}{float64(4), float64(3)},
		wantErr: false,
	},
	{
		name: "v3.AttributeKeyDataTypeFloat: Invalid []interface{}",
		args: args{
			v:        []interface{}{4, "true"},
			dataType: v3.AttributeKeyDataTypeFloat64,
		},
		want:    nil,
		wantErr: true,
	},
	{
		name: "v3.AttributeKeyDataTypeFloat64: Invalid type",
		args: args{
			v:        true,
			dataType: v3.AttributeKeyDataTypeFloat64,
		},
		want:    nil,
		wantErr: true,
	},
	{
		name: "v3.AttributeKeyDataTypeInt64: valid float32",
		args: args{
			v:        float32(1000),
			dataType: v3.AttributeKeyDataTypeInt64,
		},
		want:    int64(1000),
		wantErr: false,
	},
	{
		name: "v3.AttributeKeyDataTypeInt64: valid float64",
		args: args{
			v:        float64(1000),
			dataType: v3.AttributeKeyDataTypeInt64,
		},
		want:    int64(1000),
		wantErr: false,
	},
}

// Test cases for ValidateAndCastValue function in pkg/query-service/utils/format.go
func TestValidateAndCastValue(t *testing.T) {
	for _, tt := range testValidateAndCastValueData {
		t.Run(tt.name, func(t *testing.T) {
			got, err := ValidateAndCastValue(tt.args.v, tt.args.dataType)
			if (err != nil) != tt.wantErr {
				t.Errorf("ValidateAndCastValue() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			if !reflect.DeepEqual(got, tt.want) && !reflect.DeepEqual(err, tt.wantErr) {
				t.Errorf("ValidateAndCastValue() = %v, want %v", got, tt.want)
			}
		})
	}
}

var one = 1
var onePointOne = 1.1
var oneString = "1"
var trueBool = true

var testClickHouseFormattedValueData = []struct {
	name  string
	value interface{}
	want  interface{}
}{
	{
		name:  "int",
		value: 1,
		want:  "1",
	},
	{
		name:  "int64",
		value: int64(1),
		want:  "1",
	},
	{
		name:  "float32",
		value: float32(1.1),
		want:  "1.100000",
	},
	{
		name:  "string",
		value: "1",
		want:  "'1'",
	},
	{
		name:  "bool",
		value: true,
		want:  "true",
	},
	{
		name:  "[]interface{}",
		value: []interface{}{1, 2},
		want:  "[1,2]",
	},
	{
		name:  "[]interface{}",
		value: []interface{}{"1", "2"},
		want:  "['1','2']",
	},
	{
		name:  "pointer int",
		value: &one,
		want:  "1",
	},
	{
		name:  "pointer float32",
		value: onePointOne,
		want:  "1.100000",
	},
	{
		name:  "pointer string",
		value: &oneString,
		want:  "'1'",
	},
	{
		name:  "pointer bool",
		value: &trueBool,
		want:  "true",
	},
	{
		name:  "pointer []interface{}",
		value: []interface{}{&one, &one},
		want:  "[1,1]",
	},
	{
		name:  "string with single quote",
		value: "test'1",
		want:  "'test\\'1'",
	},
	{
		name: "[]interface{} with string with single quote",
		value: []interface{}{
			"test'1",
			"test'2",
		},
		want: "['test\\'1','test\\'2']",
	},
}

func TestClickHouseFormattedValue(t *testing.T) {
	for _, tt := range testClickHouseFormattedValueData {
		t.Run(tt.name, func(t *testing.T) {
			got := ClickHouseFormattedValue(tt.value)
			if !reflect.DeepEqual(got, tt.want) {
				t.Errorf("ClickHouseFormattedValue() = %v, want %v", got, tt.want)
			}
		})
	}
}

var testGetClickhouseColumnName = []struct {
	name     string
	typeName string
	dataType string
	field    string
	want     string
}{
	{
		name:     "tag",
		typeName: string(v3.AttributeKeyTypeTag),
		dataType: string(v3.AttributeKeyDataTypeInt64),
		field:    "tag1",
		want:     "attribute_int64_tag1",
	},
	{
		name:     "resource",
		typeName: string(v3.AttributeKeyTypeResource),
		dataType: string(v3.AttributeKeyDataTypeInt64),
		field:    "tag1",
		want:     "resource_int64_tag1",
	},
	{
		name:     "attribute old parser",
		typeName: constants.Attributes,
		dataType: string(v3.AttributeKeyDataTypeInt64),
		field:    "tag1",
		want:     "attribute_int64_tag1",
	},
	{
		name:     "resource old parser",
		typeName: constants.Resources,
		dataType: string(v3.AttributeKeyDataTypeInt64),
		field:    "tag1",
		want:     "resource_int64_tag1",
	},
}

func TestGetClickhouseColumnName(t *testing.T) {
	for _, tt := range testGetClickhouseColumnName {
		t.Run(tt.name, func(t *testing.T) {
			got := GetClickhouseColumnName(tt.typeName, tt.dataType, tt.field)
			if !reflect.DeepEqual(got, tt.want) {
				t.Errorf("ClickHouseFormattedValue() = %v, want %v", got, tt.want)
			}
		})
	}
}

var testGetEpochNanoSecsData = []struct {
	Name       string
	Epoch      int64
	Multiplier int64
	Result     int64
}{
	{
		Name:   "Test 1",
		Epoch:  1680712080000,
		Result: 1680712080000000000,
	},
	{
		Name:   "Test 1",
		Epoch:  1680712080000000000,
		Result: 1680712080000000000,
	},
}

func TestGetEpochNanoSecs(t *testing.T) {
	for _, tt := range testGetEpochNanoSecsData {
		t.Run(tt.Name, func(t *testing.T) {
			got := GetEpochNanoSecs(tt.Epoch)
			if !reflect.DeepEqual(got, tt.Result) {
				t.Errorf("ClickHouseFormattedValue() = %v, want %v", got, tt.Result)
			}
		})
	}
}
