package tokens

import (
	"context"
	"encoding/hex"
	"errors"
	"testing"
	"time"

	"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/cache"
	"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/creds/accessbox"
	"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
	apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
	oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
	oidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id/test"
	"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
	"github.com/stretchr/testify/require"
	"go.uber.org/zap/zaptest"
)

type frostfsMock struct {
	objects map[oid.Address][]byte
	errors  map[oid.Address]error
}

func (f *frostfsMock) CreateObject(context.Context, PrmObjectCreate) (oid.ID, error) {
	panic("implement me for test")
}

func (f *frostfsMock) GetCredsPayload(_ context.Context, address oid.Address) ([]byte, error) {
	if err := f.errors[address]; err != nil {
		return nil, err
	}

	data, ok := f.objects[address]
	if !ok {
		return nil, errors.New("not found")
	}
	return data, nil
}

func TestRemovingAccessBox(t *testing.T) {
	ctx := context.Background()

	key, err := keys.NewPrivateKey()
	require.NoError(t, err)

	gateData := []*accessbox.GateData{{
		BearerToken: &bearer.Token{},
		GateKey:     key.PublicKey(),
	}}

	secretKey := "713d0a0b9efc7d22923e17b0402a6a89b4273bc711c8bacb2da1b643d0006aeb"
	sk, err := hex.DecodeString(secretKey)
	require.NoError(t, err)

	accessBox, _, err := accessbox.PackTokens(gateData, sk)
	require.NoError(t, err)
	data, err := accessBox.Marshal()
	require.NoError(t, err)

	addr := oidtest.Address()
	frostfs := &frostfsMock{
		objects: map[oid.Address][]byte{addr: data},
		errors:  map[oid.Address]error{},
	}

	cfg := Config{
		FrostFS: frostfs,
		Key:     key,
		CacheConfig: &cache.Config{
			Size:     10,
			Lifetime: 24 * time.Hour,
			Logger:   zaptest.NewLogger(t),
		},
		RemovingCheckAfterDurations: 0, // means check always
	}

	creds := New(cfg)

	_, err = creds.GetBox(ctx, addr)
	require.NoError(t, err)

	frostfs.errors[addr] = errors.New("network error")
	_, err = creds.GetBox(ctx, addr)
	require.NoError(t, err)

	frostfs.errors[addr] = &apistatus.ObjectAlreadyRemoved{}
	_, err = creds.GetBox(ctx, addr)
	require.Error(t, err)
}