package restic

import (
	"maps"
	"sort"
)

// IDSet is a set of IDs.
type IDSet map[ID]struct{}

// NewIDSet returns a new IDSet, populated with ids.
func NewIDSet(ids ...ID) IDSet {
	m := make(IDSet)
	for _, id := range ids {
		m[id] = struct{}{}
	}

	return m
}

// Has returns true iff id is contained in the set.
func (s IDSet) Has(id ID) bool {
	_, ok := s[id]
	return ok
}

// Insert adds id to the set.
func (s IDSet) Insert(id ID) {
	s[id] = struct{}{}
}

// Delete removes id from the set.
func (s IDSet) Delete(id ID) {
	delete(s, id)
}

// List returns a sorted slice of all IDs in the set.
func (s IDSet) List() IDs {
	list := make(IDs, 0, len(s))
	for id := range s {
		list = append(list, id)
	}

	sort.Sort(list)

	return list
}

// Equals returns true iff s equals other.
func (s IDSet) Equals(other IDSet) bool { return maps.Equal(s, other) }

// Merge adds the blobs in other to the current set.
func (s IDSet) Merge(other IDSet) { maps.Copy(s, other) }

// Intersect returns a new set containing the IDs that are present in both sets.
func (s IDSet) Intersect(other IDSet) (result IDSet) {
	result = NewIDSet()

	set1 := s
	set2 := other

	// iterate over the smaller set
	if len(set2) < len(set1) {
		set1, set2 = set2, set1
	}

	for id := range set1 {
		if set2.Has(id) {
			result.Insert(id)
		}
	}

	return result
}

// Sub returns a new set containing all IDs that are present in s but not in
// other.
func (s IDSet) Sub(other IDSet) (result IDSet) {
	result = NewIDSet()
	for id := range s {
		if !other.Has(id) {
			result.Insert(id)
		}
	}

	return result
}

func (s IDSet) String() string {
	str := s.List().String()
	return "{" + str[1:len(str)-1] + "}"
}

func (s IDSet) Clone() IDSet { return maps.Clone(s) }
