package keeper_test

import (
	"testing"
	"time"

	sdk "github.com/cosmos/cosmos-sdk/types"
	"github.com/stretchr/testify/require"

	"github.com/0glabs/0g-chain/x/evmutil/keeper"
	"github.com/0glabs/0g-chain/x/evmutil/types"
)

func TestSequenceManager(t *testing.T) {
	k, ctx := setupKeeper(t)
	sm := keeper.NewSequenceManager(k)

	addr := sdk.AccAddress([]byte("test_address"))

	// Test GetNextSequence
	t.Run("GetNextSequence - Account not found", func(t *testing.T) {
		_, err := sm.GetNextSequence(ctx, addr)
		require.Error(t, err)
		require.Contains(t, err.Error(), "account not found")
	})

	// Create account
	acc := k.AccountKeeper().NewAccountWithAddress(ctx, addr)
	k.AccountKeeper().SetAccount(ctx, acc)

	t.Run("GetNextSequence - Success", func(t *testing.T) {
		seq, err := sm.GetNextSequence(ctx, addr)
		require.NoError(t, err)
		require.Equal(t, uint64(0), seq)
	})

	// Test ValidateSequence
	t.Run("ValidateSequence - Invalid sequence", func(t *testing.T) {
		err := sm.ValidateSequence(ctx, addr, 1)
		require.Error(t, err)
		require.ErrorIs(t, err, types.ErrSequenceMismatch)
	})

	t.Run("ValidateSequence - Valid sequence", func(t *testing.T) {
		err := sm.ValidateSequence(ctx, addr, 0)
		require.NoError(t, err)
	})

	// Test IncrementSequence
	t.Run("IncrementSequence - Success", func(t *testing.T) {
		err := sm.IncrementSequence(ctx, addr)
		require.NoError(t, err)

		seq, err := sm.GetNextSequence(ctx, addr)
		require.NoError(t, err)
		require.Equal(t, uint64(1), seq)
	})
}

func TestExecuteWithRetry(t *testing.T) {
	k, ctx := setupKeeper(t)

	config := types.RetryConfig{
		MaxAttempts:     3,
		BackoffInterval: 10 * time.Millisecond,
		MaxBackoff:      50 * time.Millisecond,
	}

	t.Run("Success on first attempt", func(t *testing.T) {
		msg := &types.MsgConvertCoinToERC20{
			// setup test message
		}

		err := k.ExecuteWithRetry(ctx, msg, config)
		require.NoError(t, err)
	})

	t.Run("Success after retry", func(t *testing.T) {
		// Setup a message that will fail with sequence mismatch first
		msg := &types.MsgConvertCoinToERC20{
			// setup test message that will fail first then succeed
		}

		err := k.ExecuteWithRetry(ctx, msg, config)
		require.NoError(t, err)
	})

	t.Run("Fail after max retries", func(t *testing.T) {
		msg := &types.MsgConvertCoinToERC20{
			// setup test message that will always fail
		}

		err := k.ExecuteWithRetry(ctx, msg, config)
		require.Error(t, err)
		require.ErrorIs(t, err, types.ErrMaxRetriesReached)
	})
}