package domain_test

import (
	"fmt"
	"slices"
	"testing"
	"time"

	"gitea.dwysokinski.me/twhelp/corev3/internal/domain"
	"gitea.dwysokinski.me/twhelp/corev3/internal/domain/domaintest"
	"github.com/brianvoe/gofakeit/v7"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestNewCreateEnnoblementParams(t *testing.T) {
	t.Parallel()

	server := domaintest.NewServer(t)

	ennoblements := domain.BaseEnnoblements{
		domaintest.NewBaseEnnoblement(t),
		domaintest.NewBaseEnnoblement(t),
		domaintest.NewBaseEnnoblement(t),
	}

	res, err := domain.NewCreateEnnoblementParams(server.Key(), ennoblements)
	require.NoError(t, err)
	assert.Len(t, res, len(ennoblements))
	for i, e := range ennoblements {
		idx := slices.IndexFunc(res, func(params domain.CreateEnnoblementParams) bool {
			return params.Base().VillageID() == e.VillageID() && params.ServerKey() == server.Key()
		})
		require.GreaterOrEqualf(t, idx, 0, "ennoblements[%d] not found", i)

		params := res[idx]

		assert.Equalf(t, e, params.Base(), "ennoblements[%d]", i)
	}
}

func TestEnnoblementSort_IsInConflict(t *testing.T) {
	t.Parallel()

	type args struct {
		sorts [2]domain.EnnoblementSort
	}

	tests := []struct {
		name        string
		args        args
		expectedRes bool
	}{
		{
			name: "OK: id:ASC serverKey:ASC",
			args: args{
				sorts: [2]domain.EnnoblementSort{domain.EnnoblementSortIDASC, domain.EnnoblementSortServerKeyASC},
			},
			expectedRes: false,
		},
		{
			name: "OK: id:DESC serverKey:ASC",
			args: args{
				sorts: [2]domain.EnnoblementSort{domain.EnnoblementSortIDDESC, domain.EnnoblementSortServerKeyASC},
			},
			expectedRes: false,
		},
		{
			name: "OK: id:ASC id:ASC",
			args: args{
				sorts: [2]domain.EnnoblementSort{domain.EnnoblementSortIDASC, domain.EnnoblementSortIDASC},
			},
			expectedRes: true,
		},
		{
			name: "OK: id:ASC id:DESC",
			args: args{
				sorts: [2]domain.EnnoblementSort{domain.EnnoblementSortIDDESC, domain.EnnoblementSortIDDESC},
			},
			expectedRes: true,
		},
		{
			name: "OK: createdAt:ASC createdAt:DESC",
			args: args{
				sorts: [2]domain.EnnoblementSort{domain.EnnoblementSortCreatedAtDESC, domain.EnnoblementSortCreatedAtDESC},
			},
			expectedRes: true,
		},
		{
			name: "OK: serverKey:DESC serverKey:ASC",
			args: args{
				sorts: [2]domain.EnnoblementSort{domain.EnnoblementSortServerKeyDESC, domain.EnnoblementSortServerKeyASC},
			},
			expectedRes: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()

			assert.Equal(t, tt.expectedRes, tt.args.sorts[0].IsInConflict(tt.args.sorts[1]))
		})
	}
}

func TestNewEnnoblementCursor(t *testing.T) {
	t.Parallel()

	validEnnoblementCursor := domaintest.NewEnnoblementCursor(t)

	type args struct {
		id        int
		serverKey string
		createdAt time.Time
	}

	type test struct {
		name        string
		args        args
		expectedErr error
	}

	tests := []test{
		{
			name: "OK",
			args: args{
				id:        validEnnoblementCursor.ID(),
				serverKey: validEnnoblementCursor.ServerKey(),
				createdAt: validEnnoblementCursor.CreatedAt(),
			},
			expectedErr: nil,
		},
		{
			name: "ERR: id < 1",
			args: args{
				id:        0,
				serverKey: validEnnoblementCursor.ServerKey(),
				createdAt: validEnnoblementCursor.CreatedAt(),
			},
			expectedErr: domain.ValidationError{
				Model: "EnnoblementCursor",
				Field: "id",
				Err: domain.MinGreaterEqualError{
					Min:     1,
					Current: 0,
				},
			},
		},
	}

	for _, serverKeyTest := range newServerKeyValidationTests() {
		tests = append(tests, test{
			name: serverKeyTest.name,
			args: args{
				id:        validEnnoblementCursor.ID(),
				serverKey: serverKeyTest.key,
			},
			expectedErr: domain.ValidationError{
				Model: "EnnoblementCursor",
				Field: "serverKey",
				Err:   serverKeyTest.expectedErr,
			},
		})
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()

			ec, err := domain.NewEnnoblementCursor(
				tt.args.id,
				tt.args.serverKey,
				tt.args.createdAt,
			)
			require.ErrorIs(t, err, tt.expectedErr)
			if tt.expectedErr != nil {
				return
			}
			assert.Equal(t, tt.args.id, ec.ID())
			assert.Equal(t, tt.args.serverKey, ec.ServerKey())
			assert.Equal(t, tt.args.createdAt, ec.CreatedAt())
			assert.NotEmpty(t, ec.Encode())
		})
	}
}

func TestListEnnoblementsParams_SetServerKeys(t *testing.T) {
	t.Parallel()

	type args struct {
		serverKeys []string
	}

	type test struct {
		name        string
		args        args
		expectedErr error
	}

	tests := []test{
		{
			name: "OK",
			args: args{
				serverKeys: []string{
					domaintest.RandServerKey(),
				},
			},
		},
	}

	for _, serverKeyTest := range newServerKeyValidationTests() {
		tests = append(tests, test{
			name: serverKeyTest.name,
			args: args{
				serverKeys: []string{serverKeyTest.key},
			},
			expectedErr: domain.SliceElementValidationError{
				Model: "ListEnnoblementsParams",
				Field: "serverKeys",
				Index: 0,
				Err:   serverKeyTest.expectedErr,
			},
		})
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()

			params := domain.NewListEnnoblementsParams()

			require.ErrorIs(t, params.SetServerKeys(tt.args.serverKeys), tt.expectedErr)
			if tt.expectedErr != nil {
				return
			}
			assert.Equal(t, tt.args.serverKeys, params.ServerKeys())
		})
	}
}

func TestListEnnoblementsParams_SetVillageIDs(t *testing.T) {
	t.Parallel()

	type args struct {
		villageIDs []int
	}

	tests := []struct {
		name        string
		args        args
		expectedErr error
	}{
		{
			name: "OK",
			args: args{
				villageIDs: []int{
					domaintest.RandID(),
					domaintest.RandID(),
					domaintest.RandID(),
				},
			},
		},
		{
			name: "ERR: value < 1",
			args: args{
				villageIDs: []int{
					domaintest.RandID(),
					domaintest.RandID(),
					domaintest.RandID(),
					0,
					domaintest.RandID(),
				},
			},
			expectedErr: domain.SliceElementValidationError{
				Model: "ListEnnoblementsParams",
				Field: "villageIDs",
				Index: 3,
				Err: domain.MinGreaterEqualError{
					Min:     1,
					Current: 0,
				},
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()

			params := domain.NewListEnnoblementsParams()

			require.ErrorIs(t, params.SetVillageIDs(tt.args.villageIDs), tt.expectedErr)
			if tt.expectedErr != nil {
				return
			}
			assert.Equal(t, tt.args.villageIDs, params.VillageIDs())
		})
	}
}

func TestListEnnoblementsParams_SetPlayerIDs(t *testing.T) {
	t.Parallel()

	type args struct {
		playerIDs []int
	}

	tests := []struct {
		name        string
		args        args
		expectedErr error
	}{
		{
			name: "OK",
			args: args{
				playerIDs: []int{
					domaintest.RandID(),
					domaintest.RandID(),
					domaintest.RandID(),
				},
			},
		},
		{
			name: "ERR: value < 1",
			args: args{
				playerIDs: []int{
					domaintest.RandID(),
					domaintest.RandID(),
					domaintest.RandID(),
					0,
					domaintest.RandID(),
				},
			},
			expectedErr: domain.SliceElementValidationError{
				Model: "ListEnnoblementsParams",
				Field: "playerIDs",
				Index: 3,
				Err: domain.MinGreaterEqualError{
					Min:     1,
					Current: 0,
				},
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()

			params := domain.NewListEnnoblementsParams()

			require.ErrorIs(t, params.SetPlayerIDs(tt.args.playerIDs), tt.expectedErr)
			if tt.expectedErr != nil {
				return
			}
			assert.Equal(t, tt.args.playerIDs, params.PlayerIDs())
		})
	}
}

func TestListEnnoblementsParams_SetTribeIDs(t *testing.T) {
	t.Parallel()

	type args struct {
		tribeIDs []int
	}

	tests := []struct {
		name        string
		args        args
		expectedErr error
	}{
		{
			name: "OK",
			args: args{
				tribeIDs: []int{
					domaintest.RandID(),
					domaintest.RandID(),
					domaintest.RandID(),
				},
			},
		},
		{
			name: "ERR: value < 1",
			args: args{
				tribeIDs: []int{
					domaintest.RandID(),
					domaintest.RandID(),
					domaintest.RandID(),
					0,
					domaintest.RandID(),
				},
			},
			expectedErr: domain.SliceElementValidationError{
				Model: "ListEnnoblementsParams",
				Field: "tribeIDs",
				Index: 3,
				Err: domain.MinGreaterEqualError{
					Min:     1,
					Current: 0,
				},
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()

			params := domain.NewListEnnoblementsParams()

			require.ErrorIs(t, params.SetTribeIDs(tt.args.tribeIDs), tt.expectedErr)
			if tt.expectedErr != nil {
				return
			}
			assert.Equal(t, tt.args.tribeIDs, params.TribeIDs())
		})
	}
}

func TestListEnnoblementsParams_SetSort(t *testing.T) {
	t.Parallel()

	type args struct {
		sort []domain.EnnoblementSort
	}

	tests := []struct {
		name        string
		args        args
		expectedErr error
	}{
		{
			name: "OK",
			args: args{
				sort: []domain.EnnoblementSort{
					domain.EnnoblementSortCreatedAtASC,
					domain.EnnoblementSortServerKeyASC,
					domain.EnnoblementSortIDASC,
				},
			},
		},
		{
			name: "OK: empty slice",
			args: args{
				sort: nil,
			},
		},
		{
			name: "ERR: len(sort) > 3",
			args: args{
				sort: []domain.EnnoblementSort{
					domain.EnnoblementSortCreatedAtASC,
					domain.EnnoblementSortServerKeyASC,
					domain.EnnoblementSortIDASC,
					domain.EnnoblementSortServerKeyDESC,
				},
			},
			expectedErr: domain.ValidationError{
				Model: "ListEnnoblementsParams",
				Field: "sort",
				Err: domain.LenOutOfRangeError{
					Min:     0,
					Max:     3,
					Current: 4,
				},
			},
		},
		{
			name: "ERR: conflict",
			args: args{
				sort: []domain.EnnoblementSort{
					domain.EnnoblementSortIDASC,
					domain.EnnoblementSortIDDESC,
				},
			},
			expectedErr: domain.ValidationError{
				Model: "ListEnnoblementsParams",
				Field: "sort",
				Err: domain.SortConflictError{
					Sort: [2]string{domain.EnnoblementSortIDASC.String(), domain.EnnoblementSortIDDESC.String()},
				},
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()

			params := domain.NewListEnnoblementsParams()

			require.ErrorIs(t, params.SetSort(tt.args.sort), tt.expectedErr)
			if tt.expectedErr != nil {
				return
			}
			assert.Equal(t, tt.args.sort, params.Sort())
		})
	}
}

func TestListEnnoblementsParams_PrependSort(t *testing.T) {
	t.Parallel()

	defaultNewParams := func(t *testing.T) domain.ListEnnoblementsParams {
		t.Helper()
		return domain.ListEnnoblementsParams{}
	}

	type args struct {
		sort []domain.EnnoblementSort
	}

	tests := []struct {
		name        string
		newParams   func(t *testing.T) domain.ListEnnoblementsParams
		args        args
		expectedErr error
	}{
		{
			name: "OK",
			args: args{
				sort: []domain.EnnoblementSort{
					domain.EnnoblementSortIDASC,
					domain.EnnoblementSortServerKeyASC,
					domain.EnnoblementSortCreatedAtASC,
				},
			},
		},
		{
			name: "OK: custom params",
			newParams: func(t *testing.T) domain.ListEnnoblementsParams {
				t.Helper()
				params := domain.NewListEnnoblementsParams()
				require.NoError(t, params.SetSort([]domain.EnnoblementSort{
					domain.EnnoblementSortIDASC,
					domain.EnnoblementSortServerKeyASC,
				}))
				return params
			},
			args: args{
				sort: []domain.EnnoblementSort{
					domain.EnnoblementSortCreatedAtASC,
				},
			},
		},
		{
			name: "OK: empty slice",
			newParams: func(t *testing.T) domain.ListEnnoblementsParams {
				t.Helper()
				params := domain.NewListEnnoblementsParams()
				require.NoError(t, params.SetSort([]domain.EnnoblementSort{
					domain.EnnoblementSortIDASC,
					domain.EnnoblementSortServerKeyASC,
				}))
				return params
			},
			args: args{
				sort: nil,
			},
		},
		{
			name: "ERR: custom params + len(sort) > sortMaxLength - len(sort)",
			newParams: func(t *testing.T) domain.ListEnnoblementsParams {
				t.Helper()
				params := domain.NewListEnnoblementsParams()
				require.NoError(t, params.SetSort([]domain.EnnoblementSort{
					domain.EnnoblementSortIDASC,
					domain.EnnoblementSortServerKeyASC,
				}))
				return params
			},
			args: args{
				sort: []domain.EnnoblementSort{
					domain.EnnoblementSortCreatedAtASC,
					domain.EnnoblementSortCreatedAtASC,
				},
			},
			expectedErr: domain.ValidationError{
				Model: "ListEnnoblementsParams",
				Field: "sort",
				Err: domain.LenOutOfRangeError{
					Min:     0,
					Max:     1,
					Current: 2,
				},
			},
		},
		{
			name:      "ERR: len(sort) > 3",
			newParams: defaultNewParams,
			args: args{
				sort: []domain.EnnoblementSort{
					domain.EnnoblementSortCreatedAtASC,
					domain.EnnoblementSortCreatedAtASC,
					domain.EnnoblementSortCreatedAtASC,
					domain.EnnoblementSortCreatedAtASC,
				},
			},
			expectedErr: domain.ValidationError{
				Model: "ListEnnoblementsParams",
				Field: "sort",
				Err: domain.LenOutOfRangeError{
					Min:     0,
					Max:     3,
					Current: 4,
				},
			},
		},
		{
			name: "ERR: conflict",
			args: args{
				sort: []domain.EnnoblementSort{
					domain.EnnoblementSortCreatedAtASC,
					domain.EnnoblementSortCreatedAtDESC,
				},
			},
			expectedErr: domain.ValidationError{
				Model: "ListEnnoblementsParams",
				Field: "sort",
				Err: domain.SortConflictError{
					Sort: [2]string{domain.EnnoblementSortCreatedAtASC.String(), domain.EnnoblementSortCreatedAtDESC.String()},
				},
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()

			newParams := defaultNewParams
			if tt.newParams != nil {
				newParams = tt.newParams
			}
			params := newParams(t)

			expectedSort := params.Sort()

			require.ErrorIs(t, params.PrependSort(tt.args.sort), tt.expectedErr)
			if tt.expectedErr != nil {
				return
			}
			assert.Equal(t, append(tt.args.sort, expectedSort...), params.Sort())
		})
	}
}

func TestListEnnoblementsParams_PrependSortString(t *testing.T) {
	t.Parallel()

	defaultNewParams := func(t *testing.T) domain.ListEnnoblementsParams {
		t.Helper()
		return domain.ListEnnoblementsParams{}
	}
	defaultAllowed := []domain.EnnoblementSort{
		domain.EnnoblementSortCreatedAtASC,
		domain.EnnoblementSortCreatedAtDESC,
		domain.EnnoblementSortIDASC,
		domain.EnnoblementSortIDDESC,
		domain.EnnoblementSortServerKeyASC,
		domain.EnnoblementSortServerKeyDESC,
	}
	defaultMaxLength := 3

	type args struct {
		sort      []string
		allowed   []domain.EnnoblementSort
		maxLength int
	}

	tests := []struct {
		name         string
		newParams    func(t *testing.T) domain.ListEnnoblementsParams
		args         args
		expectedSort []domain.EnnoblementSort
		expectedErr  error
	}{
		{
			name: "OK: [serverKey:ASC, createdAt:ASC, id:ASC]",
			args: args{
				sort: []string{
					"serverKey:ASC",
					"createdAt:ASC",
					"id:ASC",
				},
				allowed:   defaultAllowed,
				maxLength: defaultMaxLength,
			},
			expectedSort: []domain.EnnoblementSort{
				domain.EnnoblementSortServerKeyASC,
				domain.EnnoblementSortCreatedAtASC,
				domain.EnnoblementSortIDASC,
			},
		},
		{
			name: "OK: [serverKey:DESC, createdAt:DESC, id:DESC]",
			args: args{
				sort: []string{
					"serverKey:DESC",
					"createdAt:DESC",
					"id:DESC",
				},
				allowed:   defaultAllowed,
				maxLength: defaultMaxLength,
			},
			expectedSort: []domain.EnnoblementSort{
				domain.EnnoblementSortServerKeyDESC,
				domain.EnnoblementSortCreatedAtDESC,
				domain.EnnoblementSortIDDESC,
			},
		},
		{
			name: "OK: custom params",
			newParams: func(t *testing.T) domain.ListEnnoblementsParams {
				t.Helper()
				params := domain.NewListEnnoblementsParams()
				require.NoError(t, params.SetSort([]domain.EnnoblementSort{
					domain.EnnoblementSortIDASC,
					domain.EnnoblementSortServerKeyASC,
				}))
				return params
			},
			args: args{
				sort: []string{
					"createdAt:ASC",
				},
				allowed:   defaultAllowed,
				maxLength: defaultMaxLength,
			},
			expectedSort: []domain.EnnoblementSort{
				domain.EnnoblementSortCreatedAtASC,
				domain.EnnoblementSortIDASC,
				domain.EnnoblementSortServerKeyASC,
			},
		},
		{
			name: "OK: empty slice",
			args: args{
				sort:      nil,
				allowed:   defaultAllowed,
				maxLength: defaultMaxLength,
			},
		},
		{
			name: "ERR: custom params + len(sort) > sortMaxLength - len(sort)",
			newParams: func(t *testing.T) domain.ListEnnoblementsParams {
				t.Helper()
				params := domain.NewListEnnoblementsParams()
				require.NoError(t, params.SetSort([]domain.EnnoblementSort{
					domain.EnnoblementSortServerKeyASC,
					domain.EnnoblementSortIDASC,
				}))
				return params
			},
			args: args{
				sort: []string{
					"createdAt:ASC",
					"createdAt:DESC",
				},
				allowed:   defaultAllowed,
				maxLength: defaultMaxLength,
			},
			expectedErr: domain.ValidationError{
				Model: "ListEnnoblementsParams",
				Field: "sort",
				Err: domain.LenOutOfRangeError{
					Min:     0,
					Max:     1,
					Current: 2,
				},
			},
		},
		{
			name:      "ERR: len(sort) > maxLength",
			newParams: defaultNewParams,
			args: args{
				sort: []string{
					"serverKey:ASC",
					"createdAt:ASC",
				},
				allowed:   defaultAllowed,
				maxLength: 1,
			},
			expectedErr: domain.ValidationError{
				Model: "ListEnnoblementsParams",
				Field: "sort",
				Err: domain.LenOutOfRangeError{
					Min:     0,
					Max:     1,
					Current: 2,
				},
			},
		},
		{
			name:      "ERR: unsupported sort string",
			newParams: defaultNewParams,
			args: args{
				sort: []string{
					"createdAt:",
				},
				allowed:   defaultAllowed,
				maxLength: defaultMaxLength,
			},
			expectedErr: domain.SliceElementValidationError{
				Model: "ListEnnoblementsParams",
				Field: "sort",
				Index: 0,
				Err: domain.UnsupportedSortStringError{
					Sort: "createdAt:",
				},
			},
		},
		{
			name: "ERR: conflict",
			args: args{
				sort: []string{
					"createdAt:ASC",
					"createdAt:DESC",
				},
				allowed:   defaultAllowed,
				maxLength: defaultMaxLength,
			},
			expectedErr: domain.ValidationError{
				Model: "ListEnnoblementsParams",
				Field: "sort",
				Err: domain.SortConflictError{
					Sort: [2]string{domain.EnnoblementSortCreatedAtASC.String(), domain.EnnoblementSortCreatedAtDESC.String()},
				},
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()

			newParams := defaultNewParams
			if tt.newParams != nil {
				newParams = tt.newParams
			}
			params := newParams(t)

			require.ErrorIs(t, params.PrependSortString(tt.args.sort, tt.args.allowed, tt.args.maxLength), tt.expectedErr)
			if tt.expectedErr != nil {
				return
			}
			assert.Equal(t, tt.expectedSort, params.Sort())
		})
	}
}

func TestListEnnoblementsParams_SetEncodedCursor(t *testing.T) {
	t.Parallel()

	validCursor := domaintest.NewEnnoblementCursor(t)

	type args struct {
		cursor string
	}

	tests := []struct {
		name           string
		args           args
		expectedCursor domain.EnnoblementCursor
		expectedErr    error
	}{
		{
			name: "OK",
			args: args{
				cursor: validCursor.Encode(),
			},
			expectedCursor: validCursor,
		},
		{
			name: "ERR: len(cursor) < 1",
			args: args{
				cursor: "",
			},
			expectedErr: domain.ValidationError{
				Model: "ListEnnoblementsParams",
				Field: "cursor",
				Err: domain.LenOutOfRangeError{
					Min:     1,
					Max:     1000,
					Current: 0,
				},
			},
		},
		{
			name: "ERR: len(cursor) > 1000",
			args: args{
				cursor: gofakeit.LetterN(1001),
			},
			expectedErr: domain.ValidationError{
				Model: "ListEnnoblementsParams",
				Field: "cursor",
				Err: domain.LenOutOfRangeError{
					Min:     1,
					Max:     1000,
					Current: 1001,
				},
			},
		},
		{
			name: "ERR: malformed base64",
			args: args{
				cursor: "112345",
			},
			expectedErr: domain.ValidationError{
				Model: "ListEnnoblementsParams",
				Field: "cursor",
				Err:   domain.ErrInvalidCursor,
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()

			params := domain.NewListEnnoblementsParams()

			require.ErrorIs(t, params.SetEncodedCursor(tt.args.cursor), tt.expectedErr)
			if tt.expectedErr != nil {
				return
			}
			assert.Equal(t, tt.args.cursor, params.Cursor().Encode())
		})
	}
}

func TestListEnnoblementsParams_SetLimit(t *testing.T) {
	t.Parallel()

	type args struct {
		limit int
	}

	tests := []struct {
		name        string
		args        args
		expectedErr error
	}{
		{
			name: "OK",
			args: args{
				limit: domain.EnnoblementListMaxLimit,
			},
		},
		{
			name: "ERR: limit < 1",
			args: args{
				limit: 0,
			},
			expectedErr: domain.ValidationError{
				Model: "ListEnnoblementsParams",
				Field: "limit",
				Err: domain.MinGreaterEqualError{
					Min:     1,
					Current: 0,
				},
			},
		},
		{
			name: fmt.Sprintf("ERR: limit > %d", domain.EnnoblementListMaxLimit),
			args: args{
				limit: domain.EnnoblementListMaxLimit + 1,
			},
			expectedErr: domain.ValidationError{
				Model: "ListEnnoblementsParams",
				Field: "limit",
				Err: domain.MaxLessEqualError{
					Max:     domain.EnnoblementListMaxLimit,
					Current: domain.EnnoblementListMaxLimit + 1,
				},
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()

			params := domain.NewListEnnoblementsParams()

			require.ErrorIs(t, params.SetLimit(tt.args.limit), tt.expectedErr)
			if tt.expectedErr != nil {
				return
			}
			assert.Equal(t, tt.args.limit, params.Limit())
		})
	}
}

func TestNewListEnnoblementsResult(t *testing.T) {
	t.Parallel()

	ennoblements := domain.Ennoblements{
		domaintest.NewEnnoblement(t),
		domaintest.NewEnnoblement(t),
		domaintest.NewEnnoblement(t),
	}
	next := domaintest.NewEnnoblement(t)

	t.Run("OK: with next", func(t *testing.T) {
		t.Parallel()

		res, err := domain.NewListEnnoblementsResult(ennoblements, next)
		require.NoError(t, err)
		assert.Equal(t, ennoblements, res.Ennoblements())
		assert.Equal(t, ennoblements[0].ID(), res.Self().ID())
		assert.Equal(t, ennoblements[0].ServerKey(), res.Self().ServerKey())
		assert.Equal(t, ennoblements[0].CreatedAt(), res.Self().CreatedAt())
		assert.Equal(t, next.ID(), res.Next().ID())
		assert.Equal(t, next.ServerKey(), res.Next().ServerKey())
		assert.Equal(t, next.CreatedAt(), res.Next().CreatedAt())
	})

	t.Run("OK: without next", func(t *testing.T) {
		t.Parallel()

		res, err := domain.NewListEnnoblementsResult(ennoblements, domain.Ennoblement{})
		require.NoError(t, err)
		assert.Equal(t, ennoblements, res.Ennoblements())
		assert.Equal(t, ennoblements[0].ID(), res.Self().ID())
		assert.Equal(t, ennoblements[0].ServerKey(), res.Self().ServerKey())
		assert.Equal(t, ennoblements[0].CreatedAt(), res.Self().CreatedAt())
		assert.True(t, res.Next().IsZero())
	})

	t.Run("OK: 0 ennoblements", func(t *testing.T) {
		t.Parallel()

		res, err := domain.NewListEnnoblementsResult(nil, domain.Ennoblement{})
		require.NoError(t, err)
		assert.Zero(t, res.Ennoblements())
		assert.True(t, res.Self().IsZero())
		assert.True(t, res.Next().IsZero())
	})
}

func TestNewListEnnoblementsWithRelationsResult(t *testing.T) {
	t.Parallel()

	ennoblements := domain.EnnoblementsWithRelations{
		domaintest.NewEnnoblementWithRelations(t),
		domaintest.NewEnnoblementWithRelations(t),
		domaintest.NewEnnoblementWithRelations(t),
	}
	next := domaintest.NewEnnoblementWithRelations(t)

	t.Run("OK: with next", func(t *testing.T) {
		t.Parallel()

		res, err := domain.NewListEnnoblementsWithRelationsResult(ennoblements, next)
		require.NoError(t, err)
		assert.Equal(t, ennoblements, res.Ennoblements())
		assert.Equal(t, ennoblements[0].Ennoblement().ID(), res.Self().ID())
		assert.Equal(t, ennoblements[0].Ennoblement().ServerKey(), res.Self().ServerKey())
		assert.Equal(t, ennoblements[0].Ennoblement().CreatedAt(), res.Self().CreatedAt())
		assert.Equal(t, next.Ennoblement().ID(), res.Next().ID())
		assert.Equal(t, next.Ennoblement().ServerKey(), res.Next().ServerKey())
		assert.Equal(t, next.Ennoblement().CreatedAt(), res.Next().CreatedAt())
	})

	t.Run("OK: without next", func(t *testing.T) {
		t.Parallel()

		res, err := domain.NewListEnnoblementsWithRelationsResult(ennoblements, domain.EnnoblementWithRelations{})
		require.NoError(t, err)
		assert.Equal(t, ennoblements, res.Ennoblements())
		assert.Equal(t, ennoblements[0].Ennoblement().ID(), res.Self().ID())
		assert.Equal(t, ennoblements[0].Ennoblement().ServerKey(), res.Self().ServerKey())
		assert.Equal(t, ennoblements[0].Ennoblement().CreatedAt(), res.Self().CreatedAt())
		assert.True(t, res.Next().IsZero())
	})

	t.Run("OK: 0 ennoblements", func(t *testing.T) {
		t.Parallel()

		res, err := domain.NewListEnnoblementsWithRelationsResult(nil, domain.EnnoblementWithRelations{})
		require.NoError(t, err)
		assert.Zero(t, res.Ennoblements())
		assert.True(t, res.Self().IsZero())
		assert.True(t, res.Next().IsZero())
	})
}
