package resolver

import (
	"encoding/json"
	"net"
	"os"
	"strings"
	"sync"
	"time"
)

// PeerResolver maps WireGuard peer IPs to peer names
type PeerResolver struct {
	mu       sync.RWMutex
	ipToName map[string]string
	wgDir    string
}

func NewPeerResolver(wgDir string) *PeerResolver {
	r := &PeerResolver{wgDir: wgDir, ipToName: make(map[string]string)}
	r.reload()
	go r.watchReload()
	return r
}

func (r *PeerResolver) PeerForIP(ip net.IP) string {
	r.mu.RLock()
	defer r.mu.RUnlock()
	return r.ipToName[ip.String()]
}

func (r *PeerResolver) reload() {
	newMap := make(map[string]string)

	// WireGuard IPs from conf files (10.1.x.x → peer name)
	clientsDir := r.wgDir + "/clients"
	entries, err := os.ReadDir(clientsDir)
	if err == nil {
		for _, entry := range entries {
			if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".conf") {
				continue
			}
			name := strings.TrimSuffix(entry.Name(), ".conf")
			if ip := parseAddressFromConf(clientsDir + "/" + entry.Name()); ip != "" {
				newMap[ip] = name
			}
		}
	}

	// External IPs from endpoint index (external IP → peer name)
	indexFile := r.wgDir + "/.wgctl/data/peer-history/endpoint_index.json"
	if data, err := os.ReadFile(indexFile); err == nil {
		var index map[string]string
		if json.Unmarshal(data, &index) == nil {
			for ip, peer := range index {
				newMap[ip] = peer
			}
		}
	}

	r.mu.Lock()
	r.ipToName = newMap
	r.mu.Unlock()
}

func (r *PeerResolver) watchReload() {
	ticker := time.NewTicker(60 * time.Second)
	defer ticker.Stop()
	for range ticker.C {
		r.reload()
	}
}

func parseAddressFromConf(path string) string {
	data, err := os.ReadFile(path)
	if err != nil {
		return ""
	}
	for _, line := range strings.Split(string(data), "\n") {
		line = strings.TrimSpace(line)
		if strings.HasPrefix(line, "Address") {
			parts := strings.SplitN(line, "=", 2)
			if len(parts) == 2 {
				ip := strings.TrimSpace(parts[1])
				if idx := strings.Index(ip, "/"); idx != -1 {
					ip = ip[:idx]
				}
				return ip
			}
		}
	}
	return ""
}