package pkg

import (
	"log"
	"os"
	"strings"
	"sync"
	"time"
)

// LocalResolver provides hostname to IP look-up for faasd core services
type LocalResolver struct {
	Path  string
	Map   map[string]string
	Mutex *sync.RWMutex
}

// NewLocalResolver creates a new resolver for reading from a hosts file
func NewLocalResolver(path string) Resolver {
	return &LocalResolver{
		Path:  path,
		Mutex: &sync.RWMutex{},
		Map:   make(map[string]string),
	}
}

// Start polling the disk for the hosts file in Path
func (l *LocalResolver) Start() {
	var lastStat os.FileInfo

	for {
		rebuild := false
		if info, err := os.Stat(l.Path); err == nil {
			if lastStat == nil {
				rebuild = true
			} else {
				if !lastStat.ModTime().Equal(info.ModTime()) {
					rebuild = true
				}
			}
			lastStat = info
		}

		if rebuild {
			log.Printf("Resolver rebuilding map")
			l.rebuild()
		}
		time.Sleep(time.Second * 3)
	}
}

func (l *LocalResolver) rebuild() {
	l.Mutex.Lock()
	defer l.Mutex.Unlock()

	fileData, fileErr := os.ReadFile(l.Path)
	if fileErr != nil {
		log.Printf("resolver rebuild error: %s", fileErr.Error())
		return
	}

	lines := strings.Split(string(fileData), "\n")

	for _, line := range lines {
		index := strings.Index(line, "\t")

		if len(line) > 0 && index > -1 {
			ip := line[:index]
			host := line[index+1:]
			log.Printf("Resolver: %q=%q", host, ip)
			l.Map[host] = ip
		}
	}
}

// Get resolves a hostname to an IP, or timesout after the duration has passed
func (l *LocalResolver) Get(upstream string, got chan<- string, timeout time.Duration) {
	start := time.Now()
	for {
		if val := l.get(upstream); len(val) > 0 {
			got <- val
			break
		}

		if time.Now().After(start.Add(timeout)) {
			log.Printf("Timed out after %s getting host %q", timeout.String(), upstream)
			break
		}

		time.Sleep(time.Millisecond * 250)
	}
}

func (l *LocalResolver) get(upstream string) string {
	l.Mutex.RLock()
	defer l.Mutex.RUnlock()

	if val, ok := l.Map[upstream]; ok {
		return val
	}

	return ""
}
