package podautoscaler

import (
	"sync"
	"time"

	corev1 "k8s.io/api/core/v1"
)

// RawResult represents a single metric data point for a specific resource in a namespace.
type RawResult struct {
	// Resource is the name of the resource (cpu/memory).
	Resource corev1.ResourceName
	// Time is the time when the measurement was taken.
	Time time.Time
	// ReplicaCount is the number of replicas at the time of measurement.
	ReplicaCount int32
	// Utilization is the utilization percentage of the resource.
	Utilization int32
	// RawAverageValue is the raw average value of the resource.
	RawAverageValue int64
	// Pods is the total number of pods considered in the measurement.
	Pods int
}

// queue is a fixed-size queue that holds RawResult items.
// When the queue reaches its maximum size, the oldest item is removed to make space for new items.
type queue struct {
	size      int
	items     []RawResult
	lastReset int64 // unix timestamp
}

func newQueue(size int) *queue {
	return &queue{
		size:  size,
		items: make([]RawResult, 0, size),
	}
}

// Put adds a new RawResult to the queue.
// If the queue is full, it removes the oldest item.
func (q *queue) Put(cr RawResult) {
	if len(q.items) == q.size {
		q.items = q.items[1:]
	}
	q.items = append(q.items, cr)
}

// List returns a copy of the current items in the queue.
func (q *queue) List() []RawResult {
	var ret []RawResult
	ret = append(ret, q.items...)
	return ret
}

// Reset clears the queue and updates the last reset timestamp.
func (q *queue) Reset() {
	q.lastReset = time.Now().Unix()
	q.items = q.items[:0]
}

// LastResetTimestamp returns the last reset timestamp of the queue.
func (q *queue) LastResetTimestamp() int64 {
	return q.lastReset
}

// queueManager manages multiple queues identified by GroupKey.
// It provides thread-safe access to the queues.
type queueManager struct {
	mut    sync.Mutex
	size   int
	queues map[GroupKey]*queue
}

func newQueueManager(size int) *queueManager {
	return &queueManager{
		size:   size,
		queues: make(map[GroupKey]*queue),
	}
}

func (qm *queueManager) getOrCreate(groupKey GroupKey) *queue {
	qm.mut.Lock()
	defer qm.mut.Unlock()

	if q, ok := qm.queues[groupKey]; ok {
		return q
	}

	q := newQueue(qm.size)
	qm.queues[groupKey] = q
	return q
}

// Put adds a RawResult to the queue identified by the given GroupKey.
func (qm *queueManager) Put(groupKey GroupKey, rr RawResult) {
	q := qm.getOrCreate(groupKey)
	q.Put(rr)
}

func (qm *queueManager) get(groupKey GroupKey) (*queue, bool) {
	qm.mut.Lock()
	defer qm.mut.Unlock()

	q, ok := qm.queues[groupKey]
	return q, ok
}

// LastResetTimestamp returns the last reset timestamp of the queue identified by the given GroupKey.
func (qm *queueManager) LastResetTimestamp(groupKey GroupKey) int64 {
	q, ok := qm.get(groupKey)
	if !ok {
		return 0
	}
	return q.LastResetTimestamp()
}

// List returns the list of RawResult items in the queue identified by the given GroupKey.
func (qm *queueManager) List(groupKey GroupKey) []RawResult {
	q, ok := qm.get(groupKey)
	if !ok {
		return nil
	}
	return q.List()
}

// Reset clears the queue identified by the given GroupKey.
func (qm *queueManager) Reset(groupKey GroupKey) {
	q, ok := qm.get(groupKey)
	if ok {
		q.Reset()
	}
}

// GroupKeys returns a slice of all GroupKeys that have associated queues.
func (qm *queueManager) GroupKeys() []GroupKey {
	qm.mut.Lock()
	defer qm.mut.Unlock()

	keys := make([]GroupKey, 0, len(qm.queues))
	for k := range qm.queues {
		keys = append(keys, k)
	}
	return keys
}

// Delete removes the queue associated with the given GroupKey.
func (qm *queueManager) Delete(groupKey GroupKey) {
	qm.mut.Lock()
	defer qm.mut.Unlock()

	delete(qm.queues, groupKey)
}
