//go:build !integration

package utils

import (
	"math"
	"strconv"
	"testing"
	"time"

	"github.com/stretchr/testify/require"
)

func TestPrepareExpirationHeader(t *testing.T) {
	tomorrow := time.Now().Add(24 * time.Hour)
	tomorrowUnix := tomorrow.Unix()
	tomorrowUnixNano := tomorrow.UnixNano()
	tomorrowUnixMilli := tomorrowUnixNano / 1e6

	epoch := "100"
	duration := "24h"
	timestampSec := strconv.FormatInt(tomorrowUnix, 10)
	timestampMilli := strconv.FormatInt(tomorrowUnixMilli, 10)
	timestampNano := strconv.FormatInt(tomorrowUnixNano, 10)

	defaultDurations := &EpochDurations{
		CurrentEpoch:  10,
		MsPerBlock:    1000,
		BlockPerEpoch: 101,
	}

	msPerBlock := defaultDurations.BlockPerEpoch * uint64(defaultDurations.MsPerBlock)
	epochPerDay := uint64((24 * time.Hour).Milliseconds()) / msPerBlock
	if uint64((24*time.Hour).Milliseconds())%msPerBlock != 0 {
		epochPerDay++
	}

	defaultExpEpoch := strconv.FormatUint(defaultDurations.CurrentEpoch+epochPerDay, 10)

	for _, transformer := range transformers {
		for _, tc := range []struct {
			name      string
			headers   map[string]string
			durations *EpochDurations
			err       bool
			expected  map[string]string
		}{
			{
				name:      "valid epoch",
				headers:   map[string]string{transformer.expirationEpochAttr(): epoch},
				expected:  map[string]string{transformer.expirationEpochAttr(): epoch},
				durations: defaultDurations,
			},
			{
				name: "valid epoch, valid duration",
				headers: map[string]string{
					transformer.expirationEpochAttr():    epoch,
					transformer.expirationDurationAttr(): duration,
				},
				durations: defaultDurations,
				expected:  map[string]string{transformer.expirationEpochAttr(): epoch},
			},
			{
				name: "valid epoch, valid rfc3339",
				headers: map[string]string{
					transformer.expirationEpochAttr():   epoch,
					transformer.expirationRFC3339Attr(): tomorrow.Format(time.RFC3339),
				},
				durations: defaultDurations,
				expected:  map[string]string{transformer.expirationEpochAttr(): epoch},
			},
			{
				name: "valid epoch, valid timestamp sec",
				headers: map[string]string{
					transformer.expirationEpochAttr():     epoch,
					transformer.expirationTimestampAttr(): timestampSec,
				},
				durations: defaultDurations,
				expected:  map[string]string{transformer.expirationEpochAttr(): epoch},
			},
			{
				name: "valid epoch, valid timestamp milli",
				headers: map[string]string{
					transformer.expirationEpochAttr():     epoch,
					transformer.expirationTimestampAttr(): timestampMilli,
				},
				durations: defaultDurations,
				expected:  map[string]string{transformer.expirationEpochAttr(): epoch},
			},
			{
				name: "valid epoch, valid timestamp nano",
				headers: map[string]string{
					transformer.expirationEpochAttr():     epoch,
					transformer.expirationTimestampAttr(): timestampNano,
				},
				durations: defaultDurations,
				expected:  map[string]string{transformer.expirationEpochAttr(): epoch},
			},
			{
				name:      "valid timestamp sec",
				headers:   map[string]string{transformer.expirationTimestampAttr(): timestampSec},
				durations: defaultDurations,
				expected:  map[string]string{transformer.expirationEpochAttr(): defaultExpEpoch},
			},
			{
				name:      "valid duration",
				headers:   map[string]string{transformer.expirationDurationAttr(): duration},
				durations: defaultDurations,
				expected:  map[string]string{transformer.expirationEpochAttr(): defaultExpEpoch},
			},
			{
				name:      "valid rfc3339",
				headers:   map[string]string{transformer.expirationRFC3339Attr(): tomorrow.Format(time.RFC3339)},
				durations: defaultDurations,
				expected:  map[string]string{transformer.expirationEpochAttr(): defaultExpEpoch},
			},
			{
				name:    "valid max uint 64",
				headers: map[string]string{transformer.expirationRFC3339Attr(): tomorrow.Format(time.RFC3339)},
				durations: &EpochDurations{
					CurrentEpoch:  math.MaxUint64 - 1,
					MsPerBlock:    defaultDurations.MsPerBlock,
					BlockPerEpoch: defaultDurations.BlockPerEpoch,
				},
				expected: map[string]string{transformer.expirationEpochAttr(): strconv.FormatUint(uint64(math.MaxUint64), 10)},
			},
			{
				name:    "invalid timestamp sec",
				headers: map[string]string{transformer.expirationTimestampAttr(): "abc"},
				err:     true,
			},
			{
				name:    "invalid timestamp sec zero",
				headers: map[string]string{transformer.expirationTimestampAttr(): "0"},
				err:     true,
			},
			{
				name:    "invalid duration",
				headers: map[string]string{transformer.expirationDurationAttr(): "1d"},
				err:     true,
			},
			{
				name:    "invalid duration negative",
				headers: map[string]string{transformer.expirationDurationAttr(): "-5h"},
				err:     true,
			},
			{
				name:    "invalid rfc3339",
				headers: map[string]string{transformer.expirationRFC3339Attr(): "abc"},
				err:     true,
			},
			{
				name:    "invalid rfc3339 zero",
				headers: map[string]string{transformer.expirationRFC3339Attr(): time.RFC3339},
				err:     true,
			},
		} {
			t.Run(tc.name, func(t *testing.T) {
				err := transformer.prepareExpirationHeader(tc.headers, tc.durations, time.Now())
				if tc.err {
					require.Error(t, err)
				} else {
					require.NoError(t, err)
					require.Equal(t, tc.expected, tc.headers)
				}
			})
		}
	}
}

func TestSystemBackwardTranslator(t *testing.T) {
	input := []string{
		"__SYSTEM__EXPIRATION_EPOCH",
		"__SYSTEM__RANDOM_ATTR",
		"__NEOFS__EXPIRATION_EPOCH",
		"__NEOFS__RANDOM_ATTR",
	}
	expected := []string{
		"System-Expiration-Epoch",
		"System-Random-Attr",
		"Neofs-Expiration-Epoch",
		"Neofs-Random-Attr",
	}

	for i, str := range input {
		res := BackwardTransformIfSystem(str)
		require.Equal(t, expected[i], res)
	}
}