package fakeip

import (
	"net/netip"

	"github.com/Dreamacro/clash/common/cache"
)

type memoryStore struct {
	cacheIP   *cache.LruCache[string, netip.Addr]
	cacheHost *cache.LruCache[netip.Addr, string]
}

// GetByHost implements store.GetByHost
func (m *memoryStore) GetByHost(host string) (netip.Addr, bool) {
	if ip, exist := m.cacheIP.Get(host); exist {
		// ensure ip --> host on head of linked list
		m.cacheHost.Get(ip)
		return ip, true
	}

	return netip.Addr{}, false
}

// PutByHost implements store.PutByHost
func (m *memoryStore) PutByHost(host string, ip netip.Addr) {
	m.cacheIP.Set(host, ip)
}

// GetByIP implements store.GetByIP
func (m *memoryStore) GetByIP(ip netip.Addr) (string, bool) {
	if host, exist := m.cacheHost.Get(ip); exist {
		// ensure host --> ip on head of linked list
		m.cacheIP.Get(host)
		return host, true
	}

	return "", false
}

// PutByIP implements store.PutByIP
func (m *memoryStore) PutByIP(ip netip.Addr, host string) {
	m.cacheHost.Set(ip, host)
}

// DelByIP implements store.DelByIP
func (m *memoryStore) DelByIP(ip netip.Addr) {
	if host, exist := m.cacheHost.Get(ip); exist {
		m.cacheIP.Delete(host)
	}
	m.cacheHost.Delete(ip)
}

// Exist implements store.Exist
func (m *memoryStore) Exist(ip netip.Addr) bool {
	return m.cacheHost.Exist(ip)
}

// CloneTo implements store.CloneTo
// only for memoryStore to memoryStore
func (m *memoryStore) CloneTo(store store) {
	if ms, ok := store.(*memoryStore); ok {
		m.cacheIP.CloneTo(ms.cacheIP)
		m.cacheHost.CloneTo(ms.cacheHost)
	}
}

// FlushFakeIP implements store.FlushFakeIP
func (m *memoryStore) FlushFakeIP() error {
	_ = m.cacheIP.Clear()
	return m.cacheHost.Clear()
}

func newMemoryStore(size int) *memoryStore {
	return &memoryStore{
		cacheIP:   cache.NewLRUCache[string, netip.Addr](cache.WithSize[string, netip.Addr](size)),
		cacheHost: cache.NewLRUCache[netip.Addr, string](cache.WithSize[netip.Addr, string](size)),
	}
}
