package firehose

import (
	"testing"
	"time"

	"encoding/json"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/prom2json"
	"github.com/stretchr/testify/assert"
)

// var metricData = "# HELP tidb_sli_small_txn_write_duration_seconds Bucketed histogram of small transaction write time (s).\n" +
// 	"# TYPE tidb_sli_small_txn_write_duration_seconds_bucket gauge\ntidb_sli_small_txn_write_duration_seconds_bucket{le=\"0.001\"} 0\n" +
// 	"tidb_sli_small_txn_write_duration_seconds_bucket{le=\"0.002\"} 0\ntidb_sli_small_txn_write_duration_seconds_bucket{le=\"0.004\"} 0\n" +
// 	"tidb_sli_small_txn_write_duration_seconds_bucket{le=\"0.008\"} 0\ntidb_sli_small_txn_write_duration_seconds_bucket{le=\"0.016\"} 0\n" +
// 	"tidb_sli_small_txn_write_duration_seconds_bucket{le=\"0.032\"} 0\ntidb_sli_small_txn_write_duration_seconds_bucket{le=\"0.064\"} 0\n" +
// 	"tidb_sli_small_txn_write_duration_seconds_bucket{le=\"0.128\"} 0\ntidb_sli_small_txn_write_duration_seconds_bucket{le=\"0.256\"} 0\n" +
// 	"tidb_sli_small_txn_write_duration_seconds_bucket{le=\"0.512\"} 0\ntidb_sli_small_txn_write_duration_seconds_bucket{le=\"1.024\"} 0\n" +
// 	"tidb_sli_small_txn_write_duration_seconds_bucket{le=\"2.048\"} 0\ntidb_sli_small_txn_write_duration_seconds_bucket{le=\"4.096\"} 0\n" +
// 	"tidb_sli_small_txn_write_duration_seconds_bucket{le=\"8.192\"} 0\ntidb_sli_small_txn_write_duration_seconds_bucket{le=\"16.384\"} 0\n" +
// 	"tidb_sli_small_txn_write_duration_seconds_bucket{le=\"32.768\"} 0\ntidb_sli_small_txn_write_duration_seconds_bucket{le=\"65.536\"} 0\n" +
// 	"tidb_sli_small_txn_write_duration_seconds_bucket{le=\"131.072\"} 0\ntidb_sli_small_txn_write_duration_seconds_bucket{le=\"262.144\"} 0\n" +
// 	"tidb_sli_small_txn_write_duration_seconds_bucket{le=\"524.288\"} 0\ntidb_sli_small_txn_write_duration_seconds_bucket{le=\"1048.576\"} 0\n" +
// 	"tidb_sli_small_txn_write_duration_seconds_bucket{le=\"2097.152\"} 0\ntidb_sli_small_txn_write_duration_seconds_bucket{le=\"4194.304\"} 0\n" +
// 	"tidb_sli_small_txn_write_duration_seconds_bucket{le=\"8388.608\"} 0\ntidb_sli_small_txn_write_duration_seconds_bucket{le=\"16777.216\"} 0\n" +
// 	"tidb_sli_small_txn_write_duration_seconds_bucket{le=\"33554.432\"} 0\ntidb_sli_small_txn_write_duration_seconds_bucket{le=\"67108.864\"} 0\n" +
// 	"tidb_sli_small_txn_write_duration_seconds_bucket{le=\"134217.728\"} 0\ntidb_sli_small_txn_write_duration_seconds_bucket{le=\"+Inf\"} 0\n" +
// 	"tidb_sli_small_txn_write_duration_seconds_bucket{le=\"0.002\"} 0\ntidb_sli_small_txn_write_duration_seconds_bucket{le=\"0.004\"} 0\n" +
// 	"tidb_sli_small_txn_write_duration_seconds_bucket{le=\"0.008\"} 0\ntidb_sli_small_txn_write_duration_seconds_bucket{le=\"0.016\"} 0\n" +
// 	"tidb_sli_small_txn_write_duration_seconds_bucket{le=\"0.032\"} 0\ntidb_sli_small_txn_write_duration_seconds_bucket{le=\"0.064\"} 0\n" +
// 	"tidb_sli_small_txn_write_duration_seconds_bucket{le=\"0.128\"} 0\ntidb_sli_small_txn_write_duration_seconds_bucket{le=\"0.256\"} 0\n" +
// 	"tidb_sli_small_txn_write_duration_seconds_bucket{le=\"0.512\"} 0\ntidb_sli_small_txn_write_duration_seconds_bucket{le=\"1.024\"} 0\n" +
// 	"tidb_sli_small_txn_write_duration_seconds_bucket{le=\"2.048\"} 0\ntidb_sli_small_txn_write_duration_seconds_bucket{le=\"4.096\"} 0\n" +
// 	"tidb_sli_small_txn_write_duration_seconds_bucket{le=\"8.192\"} 0\ntidb_sli_small_txn_write_duration_seconds_bucket{le=\"16.384\"} 0\n" +
// 	"tidb_sli_small_txn_write_duration_seconds_bucket{le=\"32.768\"} 0\ntidb_sli_small_txn_write_duration_seconds_bucket{le=\"65.536\"} 0\n" +
// 	"tidb_sli_small_txn_write_duration_seconds_bucket{le=\"131.072\"} 0\ntidb_sli_small_txn_write_duration_seconds_bucket{le=\"262.144\"} 0\n" +
// 	"tidb_sli_small_txn_write_duration_seconds_bucket{le=\"524.288\"} 0\ntidb_sli_small_txn_write_duration_seconds_bucket{le=\"1048.576\"} 0\n" +
// 	"tidb_sli_small_txn_write_duration_seconds_bucket{le=\"2097.152\"} 0\ntidb_sli_small_txn_write_duration_seconds_bucket{le=\"4194.304\"} 0\n" +
// 	"tidb_sli_small_txn_write_duration_seconds_bucket{le=\"8388.608\"} 0\ntidb_sli_small_txn_write_duration_seconds_bucket{le=\"16777.216\"} 0\n" +
// 	"tidb_sli_small_txn_write_duration_seconds_bucket{le=\"33554.432\"} 0\ntidb_sli_small_txn_write_duration_seconds_bucket{le=\"67108.864\"} 0\n" +
// 	"tidb_sli_small_txn_write_duration_seconds_bucket{le=\"134217.728\"} 0\ntidb_sli_small_txn_write_duration_seconds_bucket{le=\"+Inf\"} 0\n" +
// 	"tidb_sli_small_txn_write_duration_seconds_bucket{le=\"0.002\"} 0\ntidb_sli_small_txn_write_duration_seconds_bucket{le=\"0.004\"} 0\n" +
// 	"tidb_sli_small_txn_write_duration_seconds_bucket{le=\"0.008\"} 0\ntidb_sli_small_txn_write_duration_seconds_bucket{le=\"0.016\"} 0\n" +
// 	"tidb_sli_small_txn_write_duration_seconds_bucket{le=\"0.032\"} 0\ntidb_sli_small_txn_write_duration_seconds_bucket{le=\"0.064\"} 0\n" +
// 	"tidb_sli_small_txn_write_duration_seconds_bucket{le=\"0.128\"} 0\ntidb_sli_small_txn_write_duration_seconds_bucket{le=\"0.256\"} 0\n" +
// 	"tidb_sli_small_txn_write_duration_seconds_bucket{le=\"0.512\"} 0\ntidb_sli_small_txn_write_duration_seconds_bucket{le=\"1.024\"} 0\n" +
// 	"tidb_sli_small_txn_write_duration_seconds_bucket{le=\"2.048\"} 0\ntidb_sli_small_txn_write_duration_seconds_bucket{le=\"4.096\"} 0\n" +
// 	"tidb_sli_small_txn_write_duration_seconds_bucket{le=\"8.192\"} 0\ntidb_sli_small_txn_write_duration_seconds_bucket{le=\"16.384\"} 0\n" +
// 	"tidb_sli_small_txn_write_duration_seconds_bucket{le=\"32.768\"} 0\ntidb_sli_small_txn_write_duration_seconds_bucket{le=\"65.536\"} 0\n" +
// 	"tidb_sli_small_txn_write_duration_seconds_bucket{le=\"131.072\"} 0\ntidb_sli_small_txn_write_duration_seconds_bucket{le=\"262.144\"} 0\n" +
// 	"tidb_sli_small_txn_write_duration_seconds_bucket{le=\"524.288\"} 0\ntidb_sli_small_txn_write_duration_seconds_bucket{le=\"1048.576\"} 0\n" +
// 	"tidb_sli_small_txn_write_duration_seconds_bucket{le=\"2097.152\"} 0\ntidb_sli_small_txn_write_duration_seconds_bucket{le=\"4194.304\"} 0\n" +
// 	"tidb_sli_small_txn_write_duration_seconds_bucket{le=\"8388.608\"} 0\ntidb_sli_small_txn_write_duration_seconds_bucket{le=\"16777.216\"} 0\n" +
// 	"tidb_sli_small_txn_write_duration_seconds_bucket{le=\"33554.432\"} 0\ntidb_sli_small_txn_write_duration_seconds_bucket{le=\"67108.864\"} 0\n" +
// 	"tidb_sli_small_txn_write_duration_seconds_bucket{le=\"134217.728\"} 0\ntidb_sli_small_txn_write_duration_seconds_bucket{le=\"+Inf\"} 0\n"

// func TestMetricParser(t *testing.T) {
// 	SLOList = []string{
// 		"tidb_sli_small_txn_write_duration_seconds",
// 	}
// 	SLOMetricNameMap = make(map[string]struct{}, len(SLOList))
// 	for _, name := range SLOList {
// 		SLOMetricNameMap[name] = struct{}{}
// 	}
// 	data := "# HELP tidb_sli_small_txn_write_duration_seconds Bucketed histogram of small transaction write time (s).\n" +
// 		"# TYPE tidb_sli_small_txn_write_duration_seconds histogram\ntidb_sli_small_txn_write_duration_seconds_bucket{le=\"0.001\"} 0\n" +
// 		"tidb_sli_small_txn_write_duration_seconds_bucket{le=\"0.002\"} 0\ntidb_sli_small_txn_write_duration_seconds_bucket{le=\"0.004\"} 0\n" +
// 		"tidb_sli_small_txn_write_duration_seconds_bucket{le=\"0.008\"} 0\ntidb_sli_small_txn_write_duration_seconds_bucket{le=\"0.016\"} 0\n" +
// 		"tidb_sli_small_txn_write_duration_seconds_bucket{le=\"0.032\"} 0\ntidb_sli_small_txn_write_duration_seconds_bucket{le=\"0.064\"} 0\n" +
// 		"tidb_sli_small_txn_write_duration_seconds_bucket{le=\"0.128\"} 0\ntidb_sli_small_txn_write_duration_seconds_bucket{le=\"0.256\"} 0\n" +
// 		"tidb_sli_small_txn_write_duration_seconds_bucket{le=\"0.512\"} 0\ntidb_sli_small_txn_write_duration_seconds_bucket{le=\"1.024\"} 0\n" +
// 		"tidb_sli_small_txn_write_duration_seconds_bucket{le=\"2.048\"} 0\ntidb_sli_small_txn_write_duration_seconds_bucket{le=\"4.096\"} 0\n" +
// 		"tidb_sli_small_txn_write_duration_seconds_bucket{le=\"8.192\"} 0\ntidb_sli_small_txn_write_duration_seconds_bucket{le=\"16.384\"} 0\n" +
// 		"tidb_sli_small_txn_write_duration_seconds_bucket{le=\"32.768\"} 0\ntidb_sli_small_txn_write_duration_seconds_bucket{le=\"65.536\"} 0\n" +
// 		"tidb_sli_small_txn_write_duration_seconds_bucket{le=\"131.072\"} 0\ntidb_sli_small_txn_write_duration_seconds_bucket{le=\"262.144\"} 0\n" +
// 		"tidb_sli_small_txn_write_duration_seconds_bucket{le=\"524.288\"} 0\ntidb_sli_small_txn_write_duration_seconds_bucket{le=\"1048.576\"} 0\n" +
// 		"tidb_sli_small_txn_write_duration_seconds_bucket{le=\"2097.152\"} 0\ntidb_sli_small_txn_write_duration_seconds_bucket{le=\"4194.304\"} 0\n" +
// 		"tidb_sli_small_txn_write_duration_seconds_bucket{le=\"8388.608\"} 0\ntidb_sli_small_txn_write_duration_seconds_bucket{le=\"16777.216\"} 0\n" +
// 		"tidb_sli_small_txn_write_duration_seconds_bucket{le=\"33554.432\"} 0\ntidb_sli_small_txn_write_duration_seconds_bucket{le=\"67108.864\"} 0\n" +
// 		"tidb_sli_small_txn_write_duration_seconds_bucket{le=\"134217.728\"} 0\ntidb_sli_small_txn_write_duration_seconds_bucket{le=\"+Inf\"} 0\n"
// 	reader := strings.NewReader(data)
// 	metrics := Parser(reader, "")
// 	require.NotNil(t, metrics["tidb_sli_small_txn_write_duration_seconds"])
// 	metric := metrics["tidb_sli_small_txn_write_duration_seconds"]
// 	require.Equal(t, "tidb_sli_small_txn_write_duration_seconds", metric.Name)
// 	require.Equal(t, "HISTOGRAM", metric.Type)
// }

// func TestSplitMessage(t *testing.T) {
// 	SLOList = []string{
// 		"tidb_sli_small_txn_write_duration_seconds",
// 	}
// 	SLOMetricNameMap = make(map[string]struct{}, len(SLOList))
// 	for _, name := range SLOList {
// 		SLOMetricNameMap[name] = struct{}{}
// 	}
// 	MessageSize = 500
// 	SLOMetricNameMap["tidb_sli_small_txn_write_duration_seconds_bucket"] = struct{}{}
// 	defer func() {
// 		MessageSize = 256 * 1024
// 		delete(SLOMetricNameMap, "tidb_sli_small_txn_write_duration_seconds_bucket")
// 	}()
// 	reader := strings.NewReader(metricData)
// 	metrics := Parser(reader, "")
// 	body := &Message{
// 		Timestamp: time.Now().Unix(),
// 		Instance:  "db-tidb-0",
// 		Metrics:   metrics["tidb_sli_small_txn_write_duration_seconds_bucket"],
// 	}
// 	message := SplitMessage(body)
// 	println(len(message))
// 	require.NotEqual(t, message, 1)

// 	for _, m := range message {
// 		require.Less(t, len(m), MessageSize)
// 	}
// }

// func TestFilterActiveCluster(t *testing.T) {
// 	MetricsCache.Put("test-1--", float64(1))
// 	MetricsCache.Put("test-1-inner_error", float64(1))
// 	message := &Message{
// 		Timestamp: time.Now().Unix(),
// 		Instance:  "test",
// 		Metrics: &prom2json.Family{
// 			Name: "gateway_connection_sla_total",
// 			Type: "COUNTER",
// 			Metrics: []interface{}{
// 				prom2json.Metric{
// 					Labels: map[string]string{
// 						"cluster_id": "1",
// 						"status":     "-",
// 					},
// 					Value: "2",
// 				},
// 				prom2json.Metric{
// 					Labels: map[string]string{
// 						"cluster_id": "2",
// 						"status":     "-",
// 					},
// 					Value: "1",
// 				},
// 				prom2json.Metric{
// 					Labels: map[string]string{
// 						"cluster_id": "1",
// 						"status":     "inner_error",
// 					},
// 					Value: "1",
// 				},
// 			},
// 		},
// 	}
// 	metrics := []interface{}{
// 		prom2json.Metric{
// 			Labels: map[string]string{
// 				"cluster_id": "1",
// 				"status":     "-",
// 			},
// 			Value: "2",
// 		},
// 		prom2json.Metric{
// 			Labels: map[string]string{
// 				"cluster_id": "2",
// 				"status":     "-",
// 			},
// 			Value: "1",
// 		},
// 	}
// 	ok := filterActiveCluster(message)
// 	require.Equal(t, true, ok)
// 	require.Equal(t, metrics, message.Metrics.Metrics)
// }

func createTestGauge() *prometheus.GaugeVec {
	gauge := prometheus.NewGaugeVec(
		prometheus.GaugeOpts{
			Name: "test_metric",
			Help: "A test metric",
		},
		[]string{"label"},
	)
	gauge.WithLabelValues("value1").Set(1.0)
	gauge.WithLabelValues("value2").Set(2.0)
	return gauge
}

func createTestCounter() *prometheus.CounterVec {
	counter := prometheus.NewCounterVec(
		prometheus.CounterOpts{
			Name: "test_total",
			Help: "A test counter",
		},
		[]string{"label"},
	)
	counter.WithLabelValues("value1").Add(5.0)
	counter.WithLabelValues("value2").Add(10.0)
	return counter
}

func TestSplitMessage(t *testing.T) {
	MessageSize = 512
	reg := prometheus.NewRegistry()
	counter := createTestCounter()
	reg.MustRegister(counter)

	family, err := reg.Gather()
	assert.NoError(t, err)

	// Test case 1: small message should not be split
	metrics := prom2json.NewFamily(family[0])
	smallMessage := &Message{
		Timestamp: time.Now().Unix(),
		Instance:  "test-instance",
		Metrics:   metrics,
	}

	split := SplitMessage(smallMessage)
	assert.Len(t, split, 1, "Small message should not be split")

	// Test case 2: large message should be split
	largeReg := prometheus.NewRegistry()
	largeGauge := prometheus.NewGaugeVec(
		prometheus.GaugeOpts{
			Name: "large_test_metric",
			Help: "A large test metric",
		},
		[]string{"index"},
	)

	// create a big message
	for i := 0; i < 1000; i++ {
		largeGauge.WithLabelValues(string(rune('a' + i%26))).Set(float64(i))
	}
	largeReg.MustRegister(largeGauge)

	largeFamily, err := largeReg.Gather()
	assert.NoError(t, err)

	largeMetrics := prom2json.NewFamily(largeFamily[0])
	largeMessage := &Message{
		Timestamp: time.Now().Unix(),
		Instance:  "test-instance",
		Metrics:   largeMetrics,
	}
	split = SplitMessage(largeMessage)
	assert.True(t, len(split) > 1, "Large message should be split into multiple parts")

	// verify that each segment is valid JSON
	for _, part := range split {
		var msg Message
		err := json.Unmarshal([]byte(part), &msg)
		assert.NoError(t, err, "Split part should be valid JSON")
		assert.Equal(t, largeMessage.Instance, msg.Instance)
		assert.Equal(t, largeMessage.Timestamp, msg.Timestamp)
	}
}

func TestGenerateSignatureHashForSLIData(t *testing.T) {
	reg := prometheus.NewRegistry()
	gauge := createTestGauge()
	reg.MustRegister(gauge)

	family, err := reg.Gather()
	assert.NoError(t, err)

	metrics := prom2json.NewFamily(family[0])
	message := &Message{
		Timestamp: time.Now().Unix(),
		Instance:  "test-instance",
		Metrics:   metrics,
	}

	// convert the message to a JSON string
	messageBytes, err := json.Marshal(message)
	assert.NoError(t, err)

	signed, err := GenerateSignatureHashForSLIData(string(messageBytes))
	assert.NoError(t, err, "Should generate signature without error")

	// Verify that the signed message is valid JSON
	var signedMsg MessageWithSignature
	err = json.Unmarshal(signed, &signedMsg)
	assert.NoError(t, err, "Signed message should be valid JSON")

	// Verify that the original message content is preserved
	assert.Equal(t, message.Timestamp, signedMsg.Timestamp)
	assert.Equal(t, message.Instance, signedMsg.Instance)
	assert.Equal(t, message.Metrics.Name, signedMsg.Metrics.Name)

	// Verify that the signature exists and is a hexadecimal string
	assert.Len(t, signedMsg.Signature, 64, "Signature should be 64-character hex string")
}

func TestIRKE(t *testing.T) {
	// Testing with a known SHA-256 hash
	hash := "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" // hash of empty string

	key, err := IRKE(hash)
	assert.NoError(t, err, "IRKE should not return an error")
	assert.Len(t, key, 32, "IRKE should return a 32-character hex string")

	_, err = IRKE("short")
	assert.Error(t, err, "IRKE should return an error for invalid hash length")
}

func TestRotateLeft128(t *testing.T) {
	// Test data: 16 bytes (128 bits)
	data := []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}

	// Rotate by 0: should return the same data
	rotated := rotateLeft128(data, 0)
	assert.Equal(t, data, rotated, "Rotating by 0 bits should return the same data")

	// Rotate by 1
	rotated = rotateLeft128(data, 1)
	expected := []byte{0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0E, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1A, 0x1C, 0x1E}
	assert.Equal(t, expected, rotated, "Rotating by 1 bit should shift correctly")

	// Rotate by 128: should return the same data
	rotated = rotateLeft128(data, 128)
	assert.Equal(t, data, rotated, "Rotating by 128 bits should return the same data")
}

func TestSplitGauge(t *testing.T) {
	reg := prometheus.NewRegistry()
	gauge := prometheus.NewGaugeVec(
		prometheus.GaugeOpts{
			Name: "test_split_metric",
			Help: "A test metric for splitting",
		},
		[]string{"label"},
	)

	for i := 0; i < 10; i++ {
		gauge.WithLabelValues(string(rune('a' + i))).Set(float64(i))
	}
	reg.MustRegister(gauge)

	family, err := reg.Gather()
	assert.NoError(t, err)

	metrics := prom2json.NewFamily(family[0])

	// Split into 3 parts
	result := SplitGauge(metrics, 3)

	assert.Len(t, result, 3, "Should split into 3 parts")

	// check every part
	totalMetrics := 0
	for _, part := range result {
		assert.Equal(t, "test_split_metric", part.Name)
		assert.Equal(t, "GAUGE", part.Type)
		totalMetrics += len(part.Metrics)
	}

	assert.Equal(t, 10, totalMetrics, "Total metrics should be preserved")
}
