package client

import (
	"errors"
	"testing"
	"time"

	"github.com/prometheus/client_golang/prometheus"
	dto "github.com/prometheus/client_model/go"
	"github.com/stretchr/testify/assert"
)

func TestRecordOperationDuration(t *testing.T) {
	// Reset metrics for testing
	mysqlClientOperationDuration.Reset()

	t.Run("records success operation", func(t *testing.T) {
		start := time.Now().Add(-100 * time.Millisecond)
		recordOperationDuration("get", start, nil)

		// Verify metric was recorded
		metrics := collectMetrics(mysqlClientOperationDuration)
		assert.NotEmpty(t, metrics)

		found := false
		for _, m := range metrics {
			labels := getLabels(m)
			if labels["operation"] == "get" && labels["status"] == "success" {
				found = true
				break
			}
		}
		assert.True(t, found, "Expected to find success metric for get operation")
	})

	t.Run("records not found error", func(t *testing.T) {
		start := time.Now().Add(-50 * time.Millisecond)
		recordOperationDuration("get", start, NewNotFound("test"))

		metrics := collectMetrics(mysqlClientOperationDuration)
		assert.NotEmpty(t, metrics)

		found := false
		for _, m := range metrics {
			labels := getLabels(m)
			if labels["operation"] == "get" && labels["status"] == "notfound" {
				found = true
				break
			}
		}
		assert.True(t, found, "Expected to find notfound metric for get operation")
	})

	t.Run("records generic error", func(t *testing.T) {
		start := time.Now().Add(-75 * time.Millisecond)
		recordOperationDuration("create", start, errors.New("some error"))

		metrics := collectMetrics(mysqlClientOperationDuration)
		assert.NotEmpty(t, metrics)

		found := false
		for _, m := range metrics {
			labels := getLabels(m)
			if labels["operation"] == "create" && labels["status"] == "error" {
				found = true
				break
			}
		}
		assert.True(t, found, "Expected to find error metric for create operation")
	})

	t.Run("records different operations", func(t *testing.T) {
		operations := []string{"get", "list", "create", "update", "delete", "patch", "status_update"}

		for _, op := range operations {
			start := time.Now().Add(-10 * time.Millisecond)
			recordOperationDuration(op, start, nil)
		}

		metrics := collectMetrics(mysqlClientOperationDuration)
		assert.NotEmpty(t, metrics)

		// Verify all operations were recorded
		recordedOps := make(map[string]bool)
		for _, m := range metrics {
			labels := getLabels(m)
			if labels["status"] == "success" {
				recordedOps[labels["operation"]] = true
			}
		}

		for _, op := range operations {
			assert.True(t, recordedOps[op], "Expected operation %s to be recorded", op)
		}
	})
}

// Helper function to collect metrics from a histogram
func collectMetrics(histogram *prometheus.HistogramVec) []*dto.Metric {
	metricChan := make(chan prometheus.Metric, 100)
	go func() {
		histogram.Collect(metricChan)
		close(metricChan)
	}()

	var metrics []*dto.Metric
	for metric := range metricChan {
		dtoMetric := &dto.Metric{}
		metric.Write(dtoMetric)
		metrics = append(metrics, dtoMetric)
	}

	return metrics
}

// Helper function to extract labels from a metric
func getLabels(m *dto.Metric) map[string]string {
	labels := make(map[string]string)
	for _, label := range m.Label {
		labels[*label.Name] = *label.Value
	}
	return labels
}
