package auth

import (
	"context"
	"strings"
	"testing"
	"time"

	"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/creds/accessbox"
	"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/creds/tokens"
	apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
	cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
	oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
	"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
	"github.com/stretchr/testify/require"
)

var _ tokens.Credentials = (*credentialsMock)(nil)

type credentialsMock struct {
	boxes map[string]*accessbox.Box
}

func newTokensFrostfsMock() *credentialsMock {
	return &credentialsMock{
		boxes: make(map[string]*accessbox.Box),
	}
}

func (m credentialsMock) addBox(addr oid.Address, box *accessbox.Box) {
	m.boxes[addr.String()] = box
}

func (m credentialsMock) GetBox(_ context.Context, addr oid.Address) (*accessbox.Box, error) {
	box, ok := m.boxes[addr.String()]
	if !ok {
		return nil, apistatus.ObjectNotFound{}
	}

	return box, nil
}

func (m credentialsMock) Put(context.Context, cid.ID, user.ID, *accessbox.AccessBox, uint64, ...*keys.PublicKey) (oid.Address, error) {
	return oid.Address{}, nil
}

func (m credentialsMock) Update(context.Context, oid.Address, user.ID, *accessbox.AccessBox, uint64, ...*keys.PublicKey) (oid.Address, error) {
	return oid.Address{}, nil
}

func TestCheckSign(t *testing.T) {
	var accessKeyAddr oid.Address
	err := accessKeyAddr.DecodeString("8N7CYBY74kxZXoyvA5UNdmovaXqFpwNfvEPsqaN81es2/3tDwq5tR8fByrJcyJwyiuYX7Dae8tyDT7pd8oaL1MBto")
	require.NoError(t, err)

	accessKeyID := strings.ReplaceAll(accessKeyAddr.String(), "/", "0")
	secretKey := "713d0a0b9efc7d22923e17b0402a6a89b4273bc711c8bacb2da1b643d0006aeb"
	awsCreds := credentials.NewStaticCredentials(accessKeyID, secretKey, "")

	reqData := RequestData{
		Method:   "GET",
		Endpoint: "http://localhost:8084",
		Bucket:   "my-bucket",
		Object:   "@obj/name",
	}
	presignData := PresignData{
		Service:  "s3",
		Region:   "spb",
		Lifetime: 10 * time.Minute,
		SignTime: time.Now().UTC(),
	}

	req, err := PresignRequest(awsCreds, reqData, presignData)
	require.NoError(t, err)

	expBox := &accessbox.Box{
		Gate: &accessbox.GateData{
			AccessKey: secretKey,
		},
	}

	mock := newTokensFrostfsMock()
	mock.addBox(accessKeyAddr, expBox)

	c := &center{
		cli:     mock,
		reg:     NewRegexpMatcher(authorizationFieldRegexp),
		postReg: NewRegexpMatcher(postPolicyCredentialRegexp),
	}
	box, err := c.Authenticate(req)
	require.NoError(t, err)
	require.EqualValues(t, expBox, box.AccessBox)
}