package middleware

import (
	"context"
	"testing"
	"time"

	"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/creds/accessbox"
	"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
	"github.com/stretchr/testify/require"
)

func TestGetBoxData(t *testing.T) {
	for _, tc := range []struct {
		name  string
		value any
		error string
	}{
		{
			name: "valid",
			value: &Box{
				AccessBox: &accessbox.Box{},
			},
		},
		{
			name:  "invalid data",
			value: "invalid-data",
			error: "couldn't get box from context",
		},
		{
			name:  "box does not exist",
			error: "couldn't get box from context",
		},
		{
			name:  "access box is nil",
			value: &Box{},
			error: "couldn't get box data from context",
		},
	} {
		t.Run(tc.name, func(t *testing.T) {
			ctx := context.WithValue(context.Background(), boxKey, tc.value)
			actual, err := GetBoxData(ctx)
			if tc.error != "" {
				require.Contains(t, err.Error(), tc.error)
				return
			}

			require.NoError(t, err)
			require.NotNil(t, actual)
			require.NotNil(t, actual.Gate)
		})
	}
}

func TestGetAuthHeaders(t *testing.T) {
	for _, tc := range []struct {
		name  string
		value any
		error bool
	}{
		{
			name: "valid",
			value: &Box{
				AuthHeaders: &AuthHeader{
					AccessKeyID: "valid-key",
					Region:      "valid-region",
					SignatureV4: "valid-sign",
				},
			},
		},
		{
			name:  "invalid data",
			value: "invalid-data",
			error: true,
		},
		{
			name:  "box does not exist",
			error: true,
		},
	} {
		t.Run(tc.name, func(t *testing.T) {
			ctx := context.WithValue(context.Background(), boxKey, tc.value)
			actual, err := GetAuthHeaders(ctx)
			if tc.error {
				require.Contains(t, err.Error(), "couldn't get box from context")
				return
			}

			require.NoError(t, err)
			require.Equal(t, tc.value.(*Box).AuthHeaders.AccessKeyID, actual.AccessKeyID)
			require.Equal(t, tc.value.(*Box).AuthHeaders.Region, actual.Region)
			require.Equal(t, tc.value.(*Box).AuthHeaders.SignatureV4, actual.SignatureV4)
		})
	}
}

func TestGetClientTime(t *testing.T) {
	for _, tc := range []struct {
		name  string
		value any
		error string
	}{
		{
			name: "valid",
			value: &Box{
				ClientTime: time.Now(),
			},
		},
		{
			name:  "invalid data",
			value: "invalid-data",
			error: "couldn't get box from context",
		},
		{
			name:  "box does not exist",
			error: "couldn't get box from context",
		},
		{
			name: "zero time",
			value: &Box{
				ClientTime: time.Time{},
			},
			error: "couldn't get client time from context",
		},
	} {
		t.Run(tc.name, func(t *testing.T) {
			ctx := context.WithValue(context.Background(), boxKey, tc.value)
			actual, err := GetClientTime(ctx)
			if tc.error != "" {
				require.Contains(t, err.Error(), tc.error)
				return
			}

			require.NoError(t, err)
			require.Equal(t, tc.value.(*Box).ClientTime, actual)
		})
	}
}

func TestGetAccessBoxAttrs(t *testing.T) {
	for _, tc := range []struct {
		name  string
		value any
		error bool
	}{
		{
			name: "valid",
			value: func() *Box {
				var attr object.Attribute
				attr.SetKey("key")
				attr.SetValue("value")
				return &Box{Attributes: []object.Attribute{attr}}
			}(),
		},
		{
			name:  "invalid data",
			value: "invalid-data",
			error: true,
		},
		{
			name:  "box does not exist",
			error: true,
		},
	} {
		t.Run(tc.name, func(t *testing.T) {
			ctx := context.WithValue(context.Background(), boxKey, tc.value)
			actual, err := GetAccessBoxAttrs(ctx)
			if tc.error {
				require.Contains(t, err.Error(), "couldn't get box from context")
				return
			}

			require.NoError(t, err)
			require.Equal(t, len(tc.value.(*Box).Attributes), len(actual))
			require.Equal(t, tc.value.(*Box).Attributes[0].Key(), actual[0].Key())
			require.Equal(t, tc.value.(*Box).Attributes[0].Value(), actual[0].Value())
		})
	}
}