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 TestNewBasePlayer(t *testing.T) {
	t.Parallel()

	validBasePlayer := domaintest.NewBasePlayer(t)

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

	tests := []struct {
		name        string
		args        args
		expectedErr error
	}{
		{
			name: "OK",
			args: args{
				id:          validBasePlayer.ID(),
				name:        validBasePlayer.Name(),
				numVillages: validBasePlayer.NumVillages(),
				points:      validBasePlayer.Points(),
				rank:        validBasePlayer.Rank(),
				tribeID:     validBasePlayer.TribeID(),
				od:          validBasePlayer.OD(),
				profileURL:  validBasePlayer.ProfileURL(),
			},
		},
		{
			name: "ERR: id < 1",
			args: args{
				id:          0,
				name:        validBasePlayer.Name(),
				numVillages: validBasePlayer.NumVillages(),
				points:      validBasePlayer.Points(),
				rank:        validBasePlayer.Rank(),
				tribeID:     validBasePlayer.TribeID(),
				od:          validBasePlayer.OD(),
				profileURL:  validBasePlayer.ProfileURL(),
			},
			expectedErr: domain.ValidationError{
				Model: "BasePlayer",
				Field: "id",
				Err: domain.MinGreaterEqualError{
					Min:     1,
					Current: 0,
				},
			},
		},
		{
			name: "ERR: len(name) < 1",
			args: args{
				id:          validBasePlayer.ID(),
				name:        "",
				numVillages: validBasePlayer.NumVillages(),
				points:      validBasePlayer.Points(),
				rank:        validBasePlayer.Rank(),
				tribeID:     validBasePlayer.TribeID(),
				od:          validBasePlayer.OD(),
				profileURL:  validBasePlayer.ProfileURL(),
			},
			expectedErr: domain.ValidationError{
				Model: "BasePlayer",
				Field: "name",
				Err: domain.LenOutOfRangeError{
					Min:     1,
					Max:     150,
					Current: 0,
				},
			},
		},
		{
			name: "ERR: len(name) > 150",
			args: args{
				id:          validBasePlayer.ID(),
				name:        gofakeit.LetterN(151),
				numVillages: validBasePlayer.NumVillages(),
				points:      validBasePlayer.Points(),
				rank:        validBasePlayer.Rank(),
				tribeID:     validBasePlayer.TribeID(),
				od:          validBasePlayer.OD(),
				profileURL:  validBasePlayer.ProfileURL(),
			},
			expectedErr: domain.ValidationError{
				Model: "BasePlayer",
				Field: "name",
				Err: domain.LenOutOfRangeError{
					Min:     1,
					Max:     150,
					Current: 151,
				},
			},
		},
		{
			name: "ERR: numVillages < 0",
			args: args{
				id:          validBasePlayer.ID(),
				name:        validBasePlayer.Name(),
				numVillages: -1,
				points:      validBasePlayer.Points(),
				rank:        validBasePlayer.Rank(),
				tribeID:     validBasePlayer.TribeID(),
				od:          validBasePlayer.OD(),
				profileURL:  validBasePlayer.ProfileURL(),
			},
			expectedErr: domain.ValidationError{
				Model: "BasePlayer",
				Field: "numVillages",
				Err: domain.MinGreaterEqualError{
					Min:     0,
					Current: -1,
				},
			},
		},
		{
			name: "ERR: points < 0",
			args: args{
				id:          validBasePlayer.ID(),
				name:        validBasePlayer.Name(),
				numVillages: validBasePlayer.NumVillages(),
				points:      -1,
				rank:        validBasePlayer.Rank(),
				tribeID:     validBasePlayer.TribeID(),
				od:          validBasePlayer.OD(),
				profileURL:  validBasePlayer.ProfileURL(),
			},
			expectedErr: domain.ValidationError{
				Model: "BasePlayer",
				Field: "points",
				Err: domain.MinGreaterEqualError{
					Min:     0,
					Current: -1,
				},
			},
		},
		{
			name: "ERR: rank < 0",
			args: args{
				id:          validBasePlayer.ID(),
				name:        validBasePlayer.Name(),
				numVillages: validBasePlayer.NumVillages(),
				points:      validBasePlayer.Points(),
				rank:        -1,
				tribeID:     validBasePlayer.TribeID(),
				od:          validBasePlayer.OD(),
				profileURL:  validBasePlayer.ProfileURL(),
			},
			expectedErr: domain.ValidationError{
				Model: "BasePlayer",
				Field: "rank",
				Err: domain.MinGreaterEqualError{
					Min:     0,
					Current: -1,
				},
			},
		},
		{
			name: "ERR: tribeID < 0",
			args: args{
				id:          validBasePlayer.ID(),
				name:        validBasePlayer.Name(),
				numVillages: validBasePlayer.NumVillages(),
				points:      validBasePlayer.Points(),
				rank:        validBasePlayer.Rank(),
				tribeID:     -1,
				od:          validBasePlayer.OD(),
				profileURL:  validBasePlayer.ProfileURL(),
			},
			expectedErr: domain.ValidationError{
				Model: "BasePlayer",
				Field: "tribeID",
				Err: domain.MinGreaterEqualError{
					Min:     0,
					Current: -1,
				},
			},
		},
		{
			name: "ERR: profileURL can't be nil",
			args: args{
				id:          validBasePlayer.ID(),
				name:        validBasePlayer.Name(),
				numVillages: validBasePlayer.NumVillages(),
				points:      validBasePlayer.Points(),
				rank:        validBasePlayer.Rank(),
				tribeID:     validBasePlayer.TribeID(),
				od:          validBasePlayer.OD(),
				profileURL:  nil,
			},
			expectedErr: domain.ValidationError{
				Model: "BasePlayer",
				Field: "profileURL",
				Err:   domain.ErrNil,
			},
		},
	}

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

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

			res, err := domain.NewBasePlayer(
				tt.args.id,
				tt.args.name,
				tt.args.numVillages,
				tt.args.points,
				tt.args.rank,
				tt.args.tribeID,
				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.numVillages, res.NumVillages())
			assert.Equal(t, tt.args.points, res.Points())
			assert.Equal(t, tt.args.rank, res.Rank())
			assert.Equal(t, tt.args.tribeID, res.TribeID())
			assert.Equal(t, tt.args.od, res.OD())
			assert.Equal(t, tt.args.profileURL, res.ProfileURL())
		})
	}
}
