package domain_test

import (
	"net/url"
	"testing"

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

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

	validBaseTribe := domaintest.NewBaseTribe(t)

	type args struct {
		id          int
		name        string
		tag         string
		numMembers  int
		numVillages int
		points      int
		allPoints   int
		rank        int
		od          domain.OpponentsDefeated
		profileURL  *url.URL
	}

	tests := []struct {
		name        string
		args        args
		expectedErr error
	}{
		{
			name: "OK",
			args: args{
				id:          validBaseTribe.ID(),
				name:        validBaseTribe.Name(),
				tag:         validBaseTribe.Tag(),
				numMembers:  validBaseTribe.NumMembers(),
				numVillages: validBaseTribe.NumVillages(),
				points:      validBaseTribe.Points(),
				allPoints:   validBaseTribe.AllPoints(),
				rank:        validBaseTribe.Rank(),
				od:          validBaseTribe.OD(),
				profileURL:  validBaseTribe.ProfileURL(),
			},
		},
		{
			name: "OK: tag/name with non-ascii characters",
			args: args{
				id:          validBaseTribe.ID(),
				name:        "Шубутные",
				tag:         "Орловы",
				numMembers:  validBaseTribe.NumMembers(),
				numVillages: validBaseTribe.NumVillages(),
				points:      validBaseTribe.Points(),
				allPoints:   validBaseTribe.AllPoints(),
				rank:        validBaseTribe.Rank(),
				od:          validBaseTribe.OD(),
				profileURL:  validBaseTribe.ProfileURL(),
			},
		},
		{
			name: "ERR: id < 1",
			args: args{
				id:          0,
				name:        validBaseTribe.Name(),
				tag:         validBaseTribe.Tag(),
				numMembers:  validBaseTribe.NumMembers(),
				numVillages: validBaseTribe.NumVillages(),
				points:      validBaseTribe.Points(),
				allPoints:   validBaseTribe.AllPoints(),
				rank:        validBaseTribe.Rank(),
				od:          validBaseTribe.OD(),
				profileURL:  validBaseTribe.ProfileURL(),
			},
			expectedErr: domain.ValidationError{
				Model: "BaseTribe",
				Field: "id",
				Err: domain.MinGreaterEqualError{
					Min:     1,
					Current: 0,
				},
			},
		},
		{
			name: "ERR: len(name) < 1",
			args: args{
				id:          validBaseTribe.ID(),
				name:        "",
				tag:         validBaseTribe.Tag(),
				numMembers:  validBaseTribe.NumMembers(),
				numVillages: validBaseTribe.NumVillages(),
				points:      validBaseTribe.Points(),
				allPoints:   validBaseTribe.AllPoints(),
				rank:        validBaseTribe.Rank(),
				od:          validBaseTribe.OD(),
				profileURL:  validBaseTribe.ProfileURL(),
			},
			expectedErr: domain.ValidationError{
				Model: "BaseTribe",
				Field: "name",
				Err: domain.LenOutOfRangeError{
					Min:     1,
					Max:     255,
					Current: 0,
				},
			},
		},
		{
			name: "ERR: len(name) > 255",
			args: args{
				id:          validBaseTribe.ID(),
				name:        gofakeit.LetterN(256),
				tag:         validBaseTribe.Tag(),
				numMembers:  validBaseTribe.NumMembers(),
				numVillages: validBaseTribe.NumVillages(),
				points:      validBaseTribe.Points(),
				allPoints:   validBaseTribe.AllPoints(),
				rank:        validBaseTribe.Rank(),
				od:          validBaseTribe.OD(),
				profileURL:  validBaseTribe.ProfileURL(),
			},
			expectedErr: domain.ValidationError{
				Model: "BaseTribe",
				Field: "name",
				Err: domain.LenOutOfRangeError{
					Min:     1,
					Max:     255,
					Current: 256,
				},
			},
		},
		{
			name: "ERR: len(tag) < 1",
			args: args{
				id:          validBaseTribe.ID(),
				name:        validBaseTribe.Tag(),
				tag:         "",
				numMembers:  validBaseTribe.NumMembers(),
				numVillages: validBaseTribe.NumVillages(),
				points:      validBaseTribe.Points(),
				allPoints:   validBaseTribe.AllPoints(),
				rank:        validBaseTribe.Rank(),
				od:          validBaseTribe.OD(),
				profileURL:  validBaseTribe.ProfileURL(),
			},
			expectedErr: domain.ValidationError{
				Model: "BaseTribe",
				Field: "tag",
				Err: domain.LenOutOfRangeError{
					Min:     1,
					Max:     10,
					Current: 0,
				},
			},
		},
		{
			name: "ERR: len(tag) > 10",
			args: args{
				id:          validBaseTribe.ID(),
				name:        validBaseTribe.Name(),
				tag:         gofakeit.LetterN(11),
				numMembers:  validBaseTribe.NumMembers(),
				numVillages: validBaseTribe.NumVillages(),
				points:      validBaseTribe.Points(),
				allPoints:   validBaseTribe.AllPoints(),
				rank:        validBaseTribe.Rank(),
				od:          validBaseTribe.OD(),
				profileURL:  validBaseTribe.ProfileURL(),
			},
			expectedErr: domain.ValidationError{
				Model: "BaseTribe",
				Field: "tag",
				Err: domain.LenOutOfRangeError{
					Min:     1,
					Max:     10,
					Current: 11,
				},
			},
		},
		{
			name: "ERR: numMembers < 0",
			args: args{
				id:          validBaseTribe.ID(),
				name:        validBaseTribe.Name(),
				tag:         validBaseTribe.Tag(),
				numMembers:  -1,
				numVillages: validBaseTribe.NumVillages(),
				points:      validBaseTribe.Points(),
				allPoints:   validBaseTribe.AllPoints(),
				rank:        validBaseTribe.Rank(),
				od:          validBaseTribe.OD(),
				profileURL:  validBaseTribe.ProfileURL(),
			},
			expectedErr: domain.ValidationError{
				Model: "BaseTribe",
				Field: "numMembers",
				Err: domain.MinGreaterEqualError{
					Min:     0,
					Current: -1,
				},
			},
		},
		{
			name: "ERR: numVillages < 0",
			args: args{
				id:          validBaseTribe.ID(),
				name:        validBaseTribe.Name(),
				tag:         validBaseTribe.Tag(),
				numMembers:  validBaseTribe.NumMembers(),
				numVillages: -1,
				points:      validBaseTribe.Points(),
				allPoints:   validBaseTribe.AllPoints(),
				rank:        validBaseTribe.Rank(),
				od:          validBaseTribe.OD(),
				profileURL:  validBaseTribe.ProfileURL(),
			},
			expectedErr: domain.ValidationError{
				Model: "BaseTribe",
				Field: "numVillages",
				Err: domain.MinGreaterEqualError{
					Min:     0,
					Current: -1,
				},
			},
		},
		{
			name: "ERR: points < 0",
			args: args{
				id:          validBaseTribe.ID(),
				name:        validBaseTribe.Name(),
				tag:         validBaseTribe.Tag(),
				numMembers:  validBaseTribe.NumMembers(),
				numVillages: validBaseTribe.NumVillages(),
				points:      -1,
				allPoints:   validBaseTribe.AllPoints(),
				rank:        validBaseTribe.Rank(),
				od:          validBaseTribe.OD(),
				profileURL:  validBaseTribe.ProfileURL(),
			},
			expectedErr: domain.ValidationError{
				Model: "BaseTribe",
				Field: "points",
				Err: domain.MinGreaterEqualError{
					Min:     0,
					Current: -1,
				},
			},
		},
		{
			name: "ERR: allPoints < 0",
			args: args{
				id:          validBaseTribe.ID(),
				name:        validBaseTribe.Name(),
				tag:         validBaseTribe.Tag(),
				numMembers:  validBaseTribe.NumMembers(),
				numVillages: validBaseTribe.NumVillages(),
				points:      validBaseTribe.Points(),
				allPoints:   -1,
				rank:        validBaseTribe.Rank(),
				od:          validBaseTribe.OD(),
				profileURL:  validBaseTribe.ProfileURL(),
			},
			expectedErr: domain.ValidationError{
				Model: "BaseTribe",
				Field: "allPoints",
				Err: domain.MinGreaterEqualError{
					Min:     0,
					Current: -1,
				},
			},
		},
		{
			name: "ERR: rank < 0",
			args: args{
				id:          validBaseTribe.ID(),
				name:        validBaseTribe.Name(),
				tag:         validBaseTribe.Tag(),
				numMembers:  validBaseTribe.NumMembers(),
				numVillages: validBaseTribe.NumVillages(),
				points:      validBaseTribe.Points(),
				allPoints:   validBaseTribe.AllPoints(),
				rank:        -1,
				od:          validBaseTribe.OD(),
				profileURL:  validBaseTribe.ProfileURL(),
			},
			expectedErr: domain.ValidationError{
				Model: "BaseTribe",
				Field: "rank",
				Err: domain.MinGreaterEqualError{
					Min:     0,
					Current: -1,
				},
			},
		},
		{
			name: "ERR: profileURL can't be nil",
			args: args{
				id:          validBaseTribe.ID(),
				name:        validBaseTribe.Name(),
				tag:         validBaseTribe.Tag(),
				numMembers:  validBaseTribe.NumMembers(),
				numVillages: validBaseTribe.NumVillages(),
				points:      validBaseTribe.Points(),
				allPoints:   validBaseTribe.AllPoints(),
				rank:        validBaseTribe.Rank(),
				od:          validBaseTribe.OD(),
				profileURL:  nil,
			},
			expectedErr: domain.ValidationError{
				Model: "BaseTribe",
				Field: "profileURL",
				Err:   domain.ErrNil,
			},
		},
	}

	for _, tt := range tests {
		tt := tt

		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()

			res, err := domain.NewBaseTribe(
				tt.args.id,
				tt.args.name,
				tt.args.tag,
				tt.args.numMembers,
				tt.args.numVillages,
				tt.args.points,
				tt.args.allPoints,
				tt.args.rank,
				tt.args.od,
				tt.args.profileURL,
			)
			require.ErrorIs(t, err, tt.expectedErr)
			if tt.expectedErr != nil {
				return
			}
			assert.Equal(t, tt.args.id, res.ID())
			assert.Equal(t, tt.args.name, res.Name())
			assert.Equal(t, tt.args.tag, res.Tag())
			assert.Equal(t, tt.args.numMembers, res.NumMembers())
			assert.Equal(t, tt.args.numVillages, res.NumVillages())
			assert.Equal(t, tt.args.points, res.Points())
			assert.Equal(t, tt.args.allPoints, res.AllPoints())
			assert.Equal(t, tt.args.rank, res.Rank())
			assert.Equal(t, tt.args.od, res.OD())
			assert.Equal(t, tt.args.profileURL, res.ProfileURL())
		})
	}
}
