package structr

import (
	"sync"

	"codeberg.org/gruf/go-byteutil"
	"codeberg.org/gruf/go-mangler/v2"
)

// Key represents one key to
// lookup (potentially) stored
// entries in an Index.
type Key struct {
	key string
	raw []any
}

// MakeKey generates Key{} from given parts.
func MakeKey(parts ...any) Key {
	buf := new_buffer()
	buf.B = mangler.AppendMulti(buf.B[:0], parts...)
	key := string(buf.B)
	free_buffer(buf)
	return Key{
		raw: parts,
		key: key,
	}
}

// MakeKeys generates []Key{} from given (multiple) parts.
func MakeKeys(parts ...[]any) []Key {
	keys := make([]Key, len(parts))
	if len(keys) != len(parts) {
		panic(assert("BCE"))
	}
	buf := new_buffer()
	for x, parts := range parts {
		buf.B = mangler.AppendMulti(buf.B[:0], parts...)
		key := string(buf.B)
		keys[x] = Key{
			raw: parts,
			key: key,
		}
	}
	free_buffer(buf)
	return keys
}

// Key returns the underlying cache key string.
// NOTE: this will not be log output friendly.
func (k Key) Key() string {
	return k.key
}

// Equal returns whether keys are equal.
func (k Key) Equal(o Key) bool {
	return (k.key == o.key)
}

// Value returns the raw slice of
// values that comprise this Key.
func (k Key) Values() []any {
	return k.raw
}

var buf_pool sync.Pool

// new_buffer returns a new initialized byte buffer.
func new_buffer() *byteutil.Buffer {
	v := buf_pool.Get()
	if v == nil {
		buf := new(byteutil.Buffer)
		buf.B = make([]byte, 0, 512)
		v = buf
	}
	return v.(*byteutil.Buffer)
}

// free_buffer releases the byte buffer.
func free_buffer(buf *byteutil.Buffer) {
	if cap(buf.B) > int(^uint16(0)) {
		return // drop large bufs
	}
	buf_pool.Put(buf)
}
