package metrics

import (
	"testing"
	"time"
)

func TestNewMetrics(t *testing.T) {
	m := New()
	if m.CacheHits == nil {
		t.Error("expected CacheHits to be initialized")
	}
	if m.CacheMisses == nil {
		t.Error("expected CacheMisses to be initialized")
	}
	if m.layerTTLs == nil {
		t.Error("expected layerTTLs to be initialized")
	}
	if m.SearchDuration == nil {
		t.Error("expected SearchDuration to be initialized")
	}
	if m.SearchDuration.maxValues != 1000 {
		t.Errorf("expected maxValues 1000, got %d", m.SearchDuration.maxValues)
	}
}

func TestMetricsRecordCacheHit(t *testing.T) {
	m := New()
	m.RecordCacheHit("search")
	m.RecordCacheHit("search")

	m.mu.Lock()
	hits := m.CacheHits["search"]
	m.mu.Unlock()

	if hits != 2 {
		t.Errorf("expected 2 cache hits, got %d", hits)
	}
}

func TestMetricsRecordCacheMiss(t *testing.T) {
	m := New()
	m.RecordCacheMiss("search")
	m.RecordCacheMiss("search")

	m.mu.Lock()
	misses := m.CacheMisses["search"]
	m.mu.Unlock()

	if misses != 2 {
		t.Errorf("expected 2 cache misses, got %d", misses)
	}
}

func TestMetricsRecordAPIQuota(t *testing.T) {
	m := New()
	m.RecordAPIQuota(1000)

	m.mu.Lock()
	quota := m.APIQuotaRemaining
	m.mu.Unlock()

	if quota != 1000 {
		t.Errorf("expected API quota 1000, got %d", quota)
	}
}

func TestMetricsRecordCircuitBreakerTrip(t *testing.T) {
	m := New()
	m.RecordCircuitBreakerTrip()
	m.RecordCircuitBreakerTrip()

	m.mu.Lock()
	trips := m.CircuitBreakerTrips
	m.mu.Unlock()

	if trips != 2 {
		t.Errorf("expected 2 circuit breaker trips, got %d", trips)
	}
}

func TestMetricsRecordSearch(t *testing.T) {
	m := New()
	m.RecordSearch(1000000000) // 1 second in nanoseconds
	m.RecordSearch(2000000000) // 2 seconds in nanoseconds

	m.mu.Lock()
	count := m.SearchCount
	valuesLen := len(m.SearchDuration.values)
	m.mu.Unlock()

	if count != 2 {
		t.Errorf("expected SearchCount 2, got %d", count)
	}
	if valuesLen != 2 {
		t.Errorf("expected 2 duration values, got %d", valuesLen)
	}
}

func TestMetricsRecordSearchTrim(t *testing.T) {
	m := New()
	m.SearchDuration.maxValues = 2

	for i := 0; i < 5; i++ {
		m.RecordSearch(int64(i * 1000000000))
	}

	m.mu.Lock()
	valuesLen := len(m.SearchDuration.values)
	m.mu.Unlock()

	if valuesLen != 2 {
		t.Errorf("expected 2 duration values after trim, got %d", valuesLen)
	}
}

func TestMetricsGetCacheHitRate(t *testing.T) {
	m := New()
	m.RecordCacheHit("search")
	m.RecordCacheHit("search")
	m.RecordCacheMiss("search")

	rate := m.GetCacheHitRate("search")
	if rate != 0.6666666666666666 { // 2/3
		t.Errorf("expected cache hit rate 0.6666666666666666, got %f", rate)
	}

	// Test with no data
	rateEmpty := m.GetCacheHitRate("empty")
	if rateEmpty != 0 {
		t.Errorf("expected cache hit rate 0 for empty layer, got %f", rateEmpty)
	}
}

func TestMetricsGetMetricsJSON(t *testing.T) {
	m := New()
	m.RecordCacheHit("search")
	m.RecordCacheMiss("search")
	m.RecordAPIQuota(500)
	m.RecordCircuitBreakerTrip()
	m.RecordSearch(1000000000)

	json := m.GetMetricsJSON()

	if json["cache_hits"] == nil {
		t.Error("expected cache_hits in JSON")
	}
	if json["cache_misses"] == nil {
		t.Error("expected cache_misses in JSON")
	}
	if json["api_quota_remaining"] != int64(500) {
		t.Errorf("expected api_quota_remaining 500, got %v", json["api_quota_remaining"])
	}
	if json["circuit_breaker_trips"] != int64(1) {
		t.Errorf("expected circuit_breaker_trips 1, got %v", json["circuit_breaker_trips"])
	}
	if json["search_count"] != int64(1) {
		t.Errorf("expected search_count 1, got %v", json["search_count"])
	}
}

func TestMetricsSetAndGetLayerTTL(t *testing.T) {
	m := New()
	m.SetLayerTTL("search", 3600*time.Second)

	ttl, ok := m.GetLayerTTL("search")
	if !ok {
		t.Error("expected TTL to be found for 'search' layer")
	}
	if ttl != 3600*time.Second {
		t.Errorf("expected TTL 3600s, got %v", ttl)
	}

	_, ok = m.GetLayerTTL("nonexistent")
	if ok {
		t.Error("expected TTL to not be found for 'nonexistent' layer")
	}
}
