package main

import (
	"sync"
	"time"
)

type CachedUuid struct {
	CleanUuid   string
	Uuid        string
	PlayerName  string
	TimeFetched time.Time
}

type UUIDCache struct {
	mu            sync.RWMutex
	lifetimeLimit time.Duration
	uuidMap       map[string]*CachedUuid
	cleanUuidMap  map[string]*CachedUuid
	playerNameMap map[string]*CachedUuid
}

func NewUUIDCache(lifetime time.Duration) *UUIDCache {
	return &UUIDCache{
		lifetimeLimit: lifetime,
		uuidMap:       make(map[string]*CachedUuid),
		cleanUuidMap:  make(map[string]*CachedUuid),
		playerNameMap: make(map[string]*CachedUuid),
	}
}

func (c *UUIDCache) Add(cachedUuid *CachedUuid) {
	c.mu.Lock()
	defer c.mu.Unlock()
	c.uuidMap[cachedUuid.Uuid] = cachedUuid
	c.cleanUuidMap[cachedUuid.CleanUuid] = cachedUuid
	c.playerNameMap[cachedUuid.PlayerName] = cachedUuid
}

func (c *UUIDCache) GetByUuid(uuid string) (*CachedUuid, bool) {
	c.mu.RLock()
	defer c.mu.RUnlock()
	val, ok := c.uuidMap[uuid]
	return val, ok
}

func (c *UUIDCache) GetByCleanUuid(cleanUuid string) (*CachedUuid, bool) {
	c.mu.RLock()
	defer c.mu.RUnlock()
	val, ok := c.cleanUuidMap[cleanUuid]
	return val, ok
}

func (c *UUIDCache) GetByPlayerName(playerName string) (*CachedUuid, bool) {
	c.mu.RLock()
	defer c.mu.RUnlock()
	val, ok := c.playerNameMap[playerName]
	return val, ok

}

func (c *UUIDCache) Get(id string) (*CachedUuid, bool) {
	val, ok := c.GetByPlayerName(id)
	if ok {
		return val, ok
	}

	val, ok = c.GetByUuid(id)
	if ok {
		return val, ok
	}

	val, ok = c.GetByCleanUuid(id)
	return val, ok
}

func (c *UUIDCache) Delete(uuid string) {
	c.mu.Lock()
	defer c.mu.Unlock()

	if cachedUuid, ok := c.uuidMap[uuid]; ok {
		delete(c.uuidMap, cachedUuid.Uuid)
		delete(c.cleanUuidMap, cachedUuid.CleanUuid)
		delete(c.playerNameMap, cachedUuid.PlayerName)
	}
}

func (c *UUIDCache) Clean() {
	c.mu.Lock()
	defer c.mu.Unlock()

	now := time.Now()

	for uuid, cachedUuid := range c.uuidMap {
		if now.Sub(cachedUuid.TimeFetched) > c.lifetimeLimit {
			delete(c.uuidMap, uuid)
			delete(c.cleanUuidMap, cachedUuid.CleanUuid)
			delete(c.playerNameMap, cachedUuid.PlayerName)
		}
	}
}
