package dht

import (
	"crypto/sha1"
	"encoding/binary"
	"time"

	"github.com/bradfitz/iter"
)

// Manages creation and validation of tokens issued to querying nodes.
type tokenServer struct {
	// Something only we know that peers can't guess, so they can't deduce valid tokens.
	secret []byte
	// How long between token changes.
	interval time.Duration
	// How many intervals may pass between the current interval, and one used to generate a token before it is invalid.
	maxIntervalDelta int
	timeNow          func() time.Time
}

func (me tokenServer) CreateToken(addr Addr) string {
	return me.createToken(addr, me.getTimeNow())
}

func (me tokenServer) createToken(addr Addr, t time.Time) string {
	h := sha1.New()
	ip := addr.IP().To16()
	if len(ip) != 16 {
		panic(ip)
	}
	h.Write(ip)
	ti := t.UnixNano() / int64(me.interval)
	var b [8]byte
	binary.BigEndian.PutUint64(b[:], uint64(ti))
	h.Write(b[:])
	h.Write(me.secret)
	return string(h.Sum(nil))
}

func (me *tokenServer) ValidToken(token string, addr Addr) bool {
	t := me.getTimeNow()
	for range iter.N(me.maxIntervalDelta + 1) {
		if me.createToken(addr, t) == token {
			return true
		}
		t = t.Add(-me.interval)
	}
	return false
}

func (me *tokenServer) getTimeNow() time.Time {
	if me.timeNow == nil {
		return time.Now()
	}
	return me.timeNow()
}
