package tree

import (
	"crypto/ecdsa"
	"time"

	"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/container"
	frostfsidcore "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/frostfsid"
	"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/netmap"
	"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/pilorama"
	"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger"
	cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
	policyengine "git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine"
	"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
)

type ContainerSource interface {
	container.Source

	DeletionInfo(cid.ID) (*container.DelInfo, error)

	// List must return list of all the containers in the FrostFS network
	// at the moment of a call and any error that does not allow fetching
	// container information.
	List() ([]cid.ID, error)
}

type cfg struct {
	log                      *logger.Logger
	key                      *ecdsa.PrivateKey
	rawPub                   []byte
	nmSource                 netmap.Source
	cnrSource                ContainerSource
	frostfsidSubjectProvider frostfsidcore.SubjectProvider
	eaclSource               container.EACLSource
	forest                   pilorama.Forest
	// replication-related parameters
	replicatorChannelCapacity int
	replicatorWorkerCount     int
	replicatorTimeout         time.Duration
	containerCacheSize        int
	authorizedKeys            [][]byte

	router policyengine.ChainRouter

	metrics MetricsRegister
}

// Option represents configuration option for a tree service.
type Option func(*cfg)

// WithContainerSource sets a container source for a tree service.
// This option is required.
func WithContainerSource(src ContainerSource) Option {
	return func(c *cfg) {
		c.cnrSource = src
	}
}

func WithFrostfsidSubjectProvider(provider frostfsidcore.SubjectProvider) Option {
	return func(c *cfg) {
		c.frostfsidSubjectProvider = provider
	}
}

// WithEACLSource sets a eACL table source for a tree service.
// This option is required.
func WithEACLSource(src container.EACLSource) Option {
	return func(c *cfg) {
		c.eaclSource = src
	}
}

// WithNetmapSource sets a netmap source for a tree service.
// This option is required.
func WithNetmapSource(src netmap.Source) Option {
	return func(c *cfg) {
		c.nmSource = src
	}
}

// WithPrivateKey sets a netmap source for a tree service.
// This option is required.
func WithPrivateKey(key *ecdsa.PrivateKey) Option {
	return func(c *cfg) {
		c.key = key
		c.rawPub = (*keys.PublicKey)(&key.PublicKey).Bytes()
	}
}

// WithLogger sets logger for a tree service.
func WithLogger(log *logger.Logger) Option {
	return func(c *cfg) {
		c.log = log
	}
}

// WithStorage sets tree storage for a service.
func WithStorage(s pilorama.Forest) Option {
	return func(c *cfg) {
		c.forest = s
	}
}

func WithReplicationChannelCapacity(n int) Option {
	return func(c *cfg) {
		if n > 0 {
			c.replicatorChannelCapacity = n
		}
	}
}

func WithReplicationWorkerCount(n int) Option {
	return func(c *cfg) {
		if n > 0 {
			c.replicatorWorkerCount = n
		}
	}
}

func WithContainerCacheSize(n int) Option {
	return func(c *cfg) {
		if n > 0 {
			c.containerCacheSize = n
		}
	}
}

func WithReplicationTimeout(t time.Duration) Option {
	return func(c *cfg) {
		if t > 0 {
			c.replicatorTimeout = t
		}
	}
}

func WithMetrics(v MetricsRegister) Option {
	return func(c *cfg) {
		c.metrics = v
	}
}

// WithAuthorizedKeys returns option to add list of public
// keys that have rights to use Tree service.
func WithAuthorizedKeys(keys keys.PublicKeys) Option {
	return func(c *cfg) {
		c.authorizedKeys = nil
		for _, key := range keys {
			c.authorizedKeys = append(c.authorizedKeys, key.Bytes())
		}
	}
}

func WithAPERouter(router policyengine.ChainRouter) Option {
	return func(c *cfg) {
		c.router = router
	}
}