package pubsub

import (
	
	

	pb 

	
)

// ErrTooManySubscriptions may be returned by a SubscriptionFilter to signal that there are too many
// subscriptions to process.
var ErrTooManySubscriptions = errors.New("too many subscriptions")

// SubscriptionFilter is a function that tells us whether we are interested in allowing and tracking
// subscriptions for a given topic.
//
// The filter is consulted whenever a subscription notification is received by another peer; if the
// filter returns false, then the notification is ignored.
//
// The filter is also consulted when joining topics; if the filter returns false, then the Join
// operation will result in an error.
type SubscriptionFilter interface {
	// CanSubscribe returns true if the topic is of interest and we can subscribe to it
	CanSubscribe(topic string) bool

	// FilterIncomingSubscriptions is invoked for all RPCs containing subscription notifications.
	// It should filter only the subscriptions of interest and my return an error if (for instance)
	// there are too many subscriptions.
	FilterIncomingSubscriptions(peer.ID, []*pb.RPC_SubOpts) ([]*pb.RPC_SubOpts, error)
}

// WithSubscriptionFilter is a pubsub option that specifies a filter for subscriptions
// in topics of interest.
func ( SubscriptionFilter) Option {
	return func( *PubSub) error {
		.subFilter = 
		return nil
	}
}

// NewAllowlistSubscriptionFilter creates a subscription filter that only allows explicitly
// specified topics for local subscriptions and incoming peer subscriptions.
func ( ...string) SubscriptionFilter {
	 := make(map[string]struct{})
	for ,  := range  {
		[] = struct{}{}
	}

	return &allowlistSubscriptionFilter{allow: }
}

type allowlistSubscriptionFilter struct {
	allow map[string]struct{}
}

var _ SubscriptionFilter = (*allowlistSubscriptionFilter)(nil)

func ( *allowlistSubscriptionFilter) ( string) bool {
	,  := .allow[]
	return 
}

func ( *allowlistSubscriptionFilter) ( peer.ID,  []*pb.RPC_SubOpts) ([]*pb.RPC_SubOpts, error) {
	return FilterSubscriptions(, .CanSubscribe), nil
}

// NewRegexpSubscriptionFilter creates a subscription filter that only allows topics that
// match a regular expression for local subscriptions and incoming peer subscriptions.
//
// Warning: the user should take care to match start/end of string in the supplied regular
// expression, otherwise the filter might match unwanted topics unexpectedly.
func ( *regexp.Regexp) SubscriptionFilter {
	return &rxSubscriptionFilter{allow: }
}

type rxSubscriptionFilter struct {
	allow *regexp.Regexp
}

var _ SubscriptionFilter = (*rxSubscriptionFilter)(nil)

func ( *rxSubscriptionFilter) ( string) bool {
	return .allow.MatchString()
}

func ( *rxSubscriptionFilter) ( peer.ID,  []*pb.RPC_SubOpts) ([]*pb.RPC_SubOpts, error) {
	return FilterSubscriptions(, .CanSubscribe), nil
}

// FilterSubscriptions filters (and deduplicates) a list of subscriptions.
// filter should return true if a topic is of interest.
func ( []*pb.RPC_SubOpts,  func(string) bool) []*pb.RPC_SubOpts {
	 := make(map[string]*pb.RPC_SubOpts)

	for ,  := range  {
		 := .GetTopicid()

		if !() {
			continue
		}

		,  := []
		if  {
			if .GetSubscribe() != .GetSubscribe() {
				delete(, )
			}
		} else {
			[] = 
		}
	}

	if len() == 0 {
		return nil
	}

	 := make([]*pb.RPC_SubOpts, 0, len())
	for ,  := range  {
		 = append(, )
	}

	return 
}

// WrapLimitSubscriptionFilter wraps a subscription filter with a hard limit in the number of
// subscriptions allowed in an RPC message.
func ( SubscriptionFilter,  int) SubscriptionFilter {
	return &limitSubscriptionFilter{filter: , limit: }
}

type limitSubscriptionFilter struct {
	filter SubscriptionFilter
	limit  int
}

var _ SubscriptionFilter = (*limitSubscriptionFilter)(nil)

func ( *limitSubscriptionFilter) ( string) bool {
	return .filter.CanSubscribe()
}

func ( *limitSubscriptionFilter) ( peer.ID,  []*pb.RPC_SubOpts) ([]*pb.RPC_SubOpts, error) {
	if len() > .limit {
		return nil, ErrTooManySubscriptions
	}

	return .filter.FilterIncomingSubscriptions(, )
}