package container

import (
	"bytes"

	"github.com/google/uuid"
	"github.com/nspcc-dev/neofs-api-go/internal"
	"github.com/nspcc-dev/neofs-api-go/refs"
	"github.com/nspcc-dev/neofs-crypto/test"
	"github.com/nspcc-dev/netmap"
	"github.com/pkg/errors"
)

var (
	_ internal.Custom = (*Container)(nil)

	emptySalt  = (UUID{}).Bytes()
	emptyOwner = (OwnerID{}).Bytes()
)

// New creates new user container based on capacity, OwnerID, ACL and PlacementRules.
func New(cap uint64, owner OwnerID, acl uint32, rules netmap.PlacementRule) (*Container, error) {
	if bytes.Equal(owner[:], emptyOwner) {
		return nil, refs.ErrEmptyOwner
	} else if cap == 0 {
		return nil, refs.ErrEmptyCapacity
	}

	salt, err := uuid.NewRandom()
	if err != nil {
		return nil, errors.Wrap(err, "could not create salt")
	}

	return &Container{
		OwnerID:  owner,
		Salt:     UUID(salt),
		Capacity: cap,
		Rules:    rules,
		BasicACL: acl,
	}, nil
}

// Bytes returns bytes representation of Container.
func (m *Container) Bytes() []byte {
	data, err := m.Marshal()
	if err != nil {
		return nil
	}

	return data
}

// ID returns generated ContainerID based on Container (data).
func (m *Container) ID() (CID, error) {
	if m.Empty() {
		return CID{}, refs.ErrEmptyContainer
	}
	data, err := m.Marshal()
	if err != nil {
		return CID{}, err
	}

	return refs.CIDForBytes(data), nil
}

// Empty checks that container is empty.
func (m *Container) Empty() bool {
	return m.Capacity == 0 || bytes.Equal(m.Salt.Bytes(), emptySalt) || bytes.Equal(m.OwnerID.Bytes(), emptyOwner)
}

// -- Test container definition -- //

// NewTestContainer returns test container.
// WARNING: DON'T USE THIS OUTSIDE TESTS.
func NewTestContainer() (*Container, error) {
	key := test.DecodeKey(0)
	owner, err := refs.NewOwnerID(&key.PublicKey)
	if err != nil {
		return nil, err
	}
	return New(100, owner, 0xFFFFFFFF, netmap.PlacementRule{
		ReplFactor: 2,
		SFGroups: []netmap.SFGroup{
			{
				Selectors: []netmap.Select{
					{Key: "Country", Count: 1},
					{Key: netmap.NodesBucket, Count: 2},
				},
				Filters: []netmap.Filter{
					{Key: "Country", F: netmap.FilterIn("USA")},
				},
			},
		},
	})
}