package cache

import (
	"time"

	lru "github.com/hashicorp/golang-lru"
)

type Key interface {
	Key() interface{}
}
type Value interface {
	Stale() bool
	Value() interface{}
}
type item struct {
	key      interface{}
	value    interface{}
	expireOn time.Time
}

func NewItem(key, value interface{}, expires time.Duration) *item {
	return &item{
		key:      key,
		value:    value,
		expireOn: time.Now().Add(expires),
	}
}
func (e *item) Stale() bool {
	if e == nil || e.value == nil {
		return true
	}

	return time.Now().After(e.expireOn)
}
func (s *item) Value() interface{} {
	return s.value
}

type Cacher interface {
	Add(Key, Value)
	Has(Key) bool
	Get(Key) (Value, bool)
	Remove(Key)
}

type arcCache struct {
	cache *lru.ARCCache
}

func NewARC(size int) (Cacher, error) {
	arc, err := lru.NewARC(size)
	if err != nil {
		return nil, err
	}

	return &arcCache{cache: arc}, nil
}
func (c *arcCache) Add(key Key, value Value) {
	c.cache.Add(key.Key(), value)
}
func (c *arcCache) Get(key Key) (Value, bool) {
	if v, ok := c.cache.Get(key.Key()); ok {
		if value, ok := v.(Value); ok && !value.Stale() {
			return value, true
		}
		c.cache.Remove(key.Key())
	}
	return nil, false
}
func (c *arcCache) Has(key Key) bool {
	_, ok := c.Get(key)
	return ok
}
func (c *arcCache) Remove(key Key) {
	c.cache.Remove(key.Key())
}
