package podautoscaler

import (
	"sort"
	"sync"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

func TestQueue(t *testing.T) {
	t.Run("basic operations", func(t *testing.T) {
		q := newQueue(2)
		assert.Equal(t, 2, q.size)
		assert.Empty(t, q.items)
		assert.Equal(t, int64(0), q.lastReset)

		// Put first item
		r1 := RawResult{Time: time.Now(), ReplicaCount: 1}
		q.Put(r1)
		items := q.List()
		assert.Len(t, items, 1)
		assert.Equal(t, r1, items[0])

		// Put second item
		r2 := RawResult{Time: time.Now(), ReplicaCount: 2}
		q.Put(r2)
		items = q.List()
		assert.Len(t, items, 2)
		assert.Equal(t, r1, items[0])
		assert.Equal(t, r2, items[1])

		// Put third item (should displace first)
		r3 := RawResult{Time: time.Now(), ReplicaCount: 3}
		q.Put(r3)
		items = q.List()
		assert.Len(t, items, 2)
		assert.Equal(t, r2, items[0])
		assert.Equal(t, r3, items[1])
	})

	t.Run("reset", func(t *testing.T) {
		q := newQueue(5)
		q.Put(RawResult{ReplicaCount: 1})
		assert.NotEmpty(t, q.List())

		beforeReset := time.Now().Unix()
		time.Sleep(time.Second)

		q.Reset()
		afterReset := time.Now().Unix()

		assert.Empty(t, q.List())
		assert.GreaterOrEqual(t, q.LastResetTimestamp(), beforeReset)
		assert.LessOrEqual(t, q.LastResetTimestamp(), afterReset)
	})
}

func TestQueueManager(t *testing.T) {
	key1 := GroupKey{Namespace: "ns1", RefNamespace: "ns1", RefName: "pod1"}
	key2 := GroupKey{Namespace: "ns1", RefNamespace: "ns1", RefName: "pod2"}

	t.Run("getOrCreate and Put", func(t *testing.T) {
		qm := newQueueManager(2)

		r1 := RawResult{ReplicaCount: 1}
		qm.Put(key1, r1)

		items := qm.List(key1)
		assert.Len(t, items, 1)
		assert.Equal(t, r1, items[0])

		// Check independent queues
		qm.Put(key2, RawResult{ReplicaCount: 2})
		items2 := qm.List(key2)
		assert.Len(t, items2, 1)
		assert.Equal(t, int32(2), items2[0].ReplicaCount)

		// key1 should still have 1 item
		assert.Len(t, qm.List(key1), 1)
	})

	t.Run("List non-existent", func(t *testing.T) {
		qm := newQueueManager(2)
		items := qm.List(key1)
		assert.Nil(t, items)
	})

	t.Run("Reset", func(t *testing.T) {
		qm := newQueueManager(2)
		qm.Put(key1, RawResult{ReplicaCount: 1})

		qm.Reset(key1)
		assert.Empty(t, qm.List(key1))
		assert.NotZero(t, qm.LastResetTimestamp(key1))

		// Reset non-existent key should be safe
		qm.Reset(key2)
		assert.Zero(t, qm.LastResetTimestamp(key2))
	})

	t.Run("GroupKeys", func(t *testing.T) {
		qm := newQueueManager(2)
		qm.Put(key1, RawResult{})
		qm.Put(key2, RawResult{})

		keys := qm.GroupKeys()
		assert.Len(t, keys, 2)

		// Sort keys for deterministic comparison since map iteration is random
		sort.Slice(keys, func(i, j int) bool {
			return keys[i].RefName < keys[j].RefName
		})

		assert.Equal(t, key1, keys[0])
		assert.Equal(t, key2, keys[1])
	})

	t.Run("Delete", func(t *testing.T) {
		qm := newQueueManager(2)
		qm.Put(key1, RawResult{})
		assert.Len(t, qm.GroupKeys(), 1)

		qm.Delete(key1)
		assert.Len(t, qm.GroupKeys(), 0)
		assert.Nil(t, qm.List(key1))
	})

	t.Run("Concurrency", func(t *testing.T) {
		qm := newQueueManager(10)
		var wg sync.WaitGroup
		wg.Add(2)

		go func() {
			defer wg.Done()
			for i := 0; i < 100; i++ {
				qm.Put(key1, RawResult{ReplicaCount: int32(i)})
			}
		}()

		go func() {
			defer wg.Done()
			for i := 0; i < 100; i++ {
				qm.List(key1)
			}
		}()

		wg.Wait()

		// Basic check to ensure no panic and state is consistent
		assert.LessOrEqual(t, len(qm.List(key1)), 10)
	})
}
