package dnsfwd

import (
	"net/netip"
	"slices"
	"strings"
	"sync"

	"github.com/miekg/dns"
)

type cache struct {
	mu      sync.RWMutex
	records map[string]*cacheEntry
}

type cacheEntry struct {
	ip4Addrs []netip.Addr
	ip6Addrs []netip.Addr
}

func newCache() *cache {
	return &cache{
		records: make(map[string]*cacheEntry),
	}
}

func (c *cache) get(domain string, reqType uint16) ([]netip.Addr, bool) {
	c.mu.RLock()
	defer c.mu.RUnlock()

	entry, exists := c.records[normalizeDomain(domain)]
	if !exists {
		return nil, false
	}

	switch reqType {
	case dns.TypeA:
		return slices.Clone(entry.ip4Addrs), true
	case dns.TypeAAAA:
		return slices.Clone(entry.ip6Addrs), true
	default:
		return nil, false
	}

}

func (c *cache) set(domain string, reqType uint16, addrs []netip.Addr) {
	c.mu.Lock()
	defer c.mu.Unlock()
	norm := normalizeDomain(domain)
	entry, exists := c.records[norm]
	if !exists {
		entry = &cacheEntry{}
		c.records[norm] = entry
	}

	switch reqType {
	case dns.TypeA:
		entry.ip4Addrs = slices.Clone(addrs)
	case dns.TypeAAAA:
		entry.ip6Addrs = slices.Clone(addrs)
	}
}

// unset removes cached entries for the given domain and request type.
func (c *cache) unset(domain string) {
	c.mu.Lock()
	defer c.mu.Unlock()
	delete(c.records, normalizeDomain(domain))
}

// normalizeDomain converts an input domain into a canonical form used as cache key:
// lowercase and fully-qualified (with trailing dot).
func normalizeDomain(domain string) string {
	// dns.Fqdn ensures trailing dot; ToLower for consistent casing
	return dns.Fqdn(strings.ToLower(domain))
}
