// Package salts contains MTProto server salt storage.
package salts

import (
	"sort"
	"sync"
	"time"

	"github.com/gotd/td/internal/mt"
)

// Salts is a simple struct store server salts.
type Salts struct {
	// server salts fetched by getSalts.
	salts    []mt.FutureSalt
	saltsMux sync.Mutex
}

// Get returns next valid salt.
func (s *Salts) Get(deadline time.Time) (int64, bool) {
	s.saltsMux.Lock()
	defer s.saltsMux.Unlock()

check:
	if len(s.salts) < 1 {
		return 0, false
	}

	date := int(deadline.Unix())
	if salt := s.salts[len(s.salts)-1]; salt.ValidUntil > date {
		return salt.Salt, true
	}

	// Filter (in place) from SliceTricks.
	n := 0
	// Check that the salt will be valid until deadline.
	for _, salt := range s.salts {
		// Filter expired salts.
		if salt.ValidUntil > date {
			// Keep valid salt.
			s.salts[n] = salt
			n++
		}
	}
	s.salts = s.salts[:n]
	goto check
}

type saltSlice []mt.FutureSalt

func (s saltSlice) Len() int {
	return len(s)
}

func (s saltSlice) Less(i, j int) bool {
	return s[i].ValidUntil > s[j].ValidUntil
}

func (s saltSlice) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}

// Store stores all given salts.
func (s *Salts) Store(salts []mt.FutureSalt) {
	s.saltsMux.Lock()
	defer s.saltsMux.Unlock()

	s.salts = append(s.salts, salts...)
	// Filter duplicates.
	n := 0
	dedup := make(map[int64]struct{}, len(s.salts)+1)
	for _, salt := range s.salts {
		if _, ok := dedup[salt.Salt]; !ok {
			dedup[salt.Salt] = struct{}{}
			s.salts[n] = salt
			n++
		}
	}
	s.salts = s.salts[:n]

	// Sort slice by valid until.
	sort.Sort(saltSlice(s.salts))
}

// Reset deletes all stored salts.
func (s *Salts) Reset() {
	s.saltsMux.Lock()
	s.salts = s.salts[:0]
	s.saltsMux.Unlock()
}
