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

	validBaseVillage := domaintest.NewBaseVillage(t)

	type args struct {
		id         int
		name       string
		points     int
		x          int
		y          int
		continent  string
		bonus      int
		playerID   int
		profileURL *url.URL
	}

	tests := []struct {
		name        string
		args        args
		expectedErr error
	}{
		{
			name: "OK",
			args: args{
				id:         validBaseVillage.ID(),
				name:       validBaseVillage.Name(),
				points:     validBaseVillage.Points(),
				x:          validBaseVillage.X(),
				y:          validBaseVillage.Y(),
				continent:  validBaseVillage.Continent(),
				bonus:      validBaseVillage.Bonus(),
				playerID:   validBaseVillage.PlayerID(),
				profileURL: validBaseVillage.ProfileURL(),
			},
		},
		{
			name: "ERR: id < 1",
			args: args{
				id:         0,
				name:       validBaseVillage.Name(),
				points:     validBaseVillage.Points(),
				x:          validBaseVillage.X(),
				y:          validBaseVillage.Y(),
				continent:  validBaseVillage.Continent(),
				bonus:      validBaseVillage.Bonus(),
				playerID:   validBaseVillage.PlayerID(),
				profileURL: validBaseVillage.ProfileURL(),
			},
			expectedErr: domain.ValidationError{
				Model: "BaseVillage",
				Field: "id",
				Err: domain.MinGreaterEqualError{
					Min:     1,
					Current: 0,
				},
			},
		},
		{
			name: "ERR: len(name) < 1",
			args: args{
				id:         validBaseVillage.ID(),
				name:       "",
				points:     validBaseVillage.Points(),
				x:          validBaseVillage.X(),
				y:          validBaseVillage.Y(),
				continent:  validBaseVillage.Continent(),
				bonus:      validBaseVillage.Bonus(),
				playerID:   validBaseVillage.PlayerID(),
				profileURL: validBaseVillage.ProfileURL(),
			},
			expectedErr: domain.ValidationError{
				Model: "BaseVillage",
				Field: "name",
				Err: domain.LenOutOfRangeError{
					Min:     1,
					Max:     150,
					Current: 0,
				},
			},
		},
		{
			name: "ERR: len(name) > 150",
			args: args{
				id:         validBaseVillage.ID(),
				name:       gofakeit.LetterN(151),
				points:     validBaseVillage.Points(),
				x:          validBaseVillage.X(),
				y:          validBaseVillage.Y(),
				continent:  validBaseVillage.Continent(),
				bonus:      validBaseVillage.Bonus(),
				playerID:   validBaseVillage.PlayerID(),
				profileURL: validBaseVillage.ProfileURL(),
			},
			expectedErr: domain.ValidationError{
				Model: "BaseVillage",
				Field: "name",
				Err: domain.LenOutOfRangeError{
					Min:     1,
					Max:     150,
					Current: 151,
				},
			},
		},
		{
			name: "ERR: points < 0",
			args: args{
				id:         validBaseVillage.ID(),
				name:       validBaseVillage.Name(),
				points:     -1,
				x:          validBaseVillage.X(),
				y:          validBaseVillage.Y(),
				continent:  validBaseVillage.Continent(),
				bonus:      validBaseVillage.Bonus(),
				playerID:   validBaseVillage.PlayerID(),
				profileURL: validBaseVillage.ProfileURL(),
			},
			expectedErr: domain.ValidationError{
				Model: "BaseVillage",
				Field: "points",
				Err: domain.MinGreaterEqualError{
					Min:     0,
					Current: -1,
				},
			},
		},
		{
			name: "ERR: x < 0",
			args: args{
				id:         validBaseVillage.ID(),
				name:       validBaseVillage.Name(),
				points:     validBaseVillage.Points(),
				x:          -1,
				y:          validBaseVillage.Y(),
				continent:  validBaseVillage.Continent(),
				bonus:      validBaseVillage.Bonus(),
				playerID:   validBaseVillage.PlayerID(),
				profileURL: validBaseVillage.ProfileURL(),
			},
			expectedErr: domain.ValidationError{
				Model: "BaseVillage",
				Field: "x",
				Err: domain.MinGreaterEqualError{
					Min:     0,
					Current: -1,
				},
			},
		},
		{
			name: "ERR: y < 0",
			args: args{
				id:         validBaseVillage.ID(),
				name:       validBaseVillage.Name(),
				points:     validBaseVillage.Points(),
				x:          validBaseVillage.X(),
				y:          -1,
				continent:  validBaseVillage.Continent(),
				bonus:      validBaseVillage.Bonus(),
				playerID:   validBaseVillage.PlayerID(),
				profileURL: validBaseVillage.ProfileURL(),
			},
			expectedErr: domain.ValidationError{
				Model: "BaseVillage",
				Field: "y",
				Err: domain.MinGreaterEqualError{
					Min:     0,
					Current: -1,
				},
			},
		},
		{
			name: "ERR: len(continent) < 1",
			args: args{
				id:         validBaseVillage.ID(),
				name:       validBaseVillage.Name(),
				points:     validBaseVillage.Points(),
				x:          validBaseVillage.X(),
				y:          validBaseVillage.Y(),
				continent:  "",
				bonus:      validBaseVillage.Bonus(),
				playerID:   validBaseVillage.PlayerID(),
				profileURL: validBaseVillage.ProfileURL(),
			},
			expectedErr: domain.ValidationError{
				Model: "BaseVillage",
				Field: "continent",
				Err: domain.LenOutOfRangeError{
					Min:     1,
					Max:     5,
					Current: 0,
				},
			},
		},
		{
			name: "ERR: len(continent) > 5",
			args: args{
				id:         validBaseVillage.ID(),
				name:       validBaseVillage.Name(),
				points:     validBaseVillage.Points(),
				x:          validBaseVillage.X(),
				y:          validBaseVillage.Y(),
				continent:  gofakeit.LetterN(6),
				bonus:      validBaseVillage.Bonus(),
				playerID:   validBaseVillage.PlayerID(),
				profileURL: validBaseVillage.ProfileURL(),
			},
			expectedErr: domain.ValidationError{
				Model: "BaseVillage",
				Field: "continent",
				Err: domain.LenOutOfRangeError{
					Min:     1,
					Max:     5,
					Current: 6,
				},
			},
		},
		{
			name: "ERR: bonus < 0",
			args: args{
				id:         validBaseVillage.ID(),
				name:       validBaseVillage.Name(),
				points:     validBaseVillage.Points(),
				x:          validBaseVillage.X(),
				y:          validBaseVillage.Y(),
				continent:  validBaseVillage.Continent(),
				bonus:      -1,
				playerID:   validBaseVillage.PlayerID(),
				profileURL: validBaseVillage.ProfileURL(),
			},
			expectedErr: domain.ValidationError{
				Model: "BaseVillage",
				Field: "bonus",
				Err: domain.MinGreaterEqualError{
					Min:     0,
					Current: -1,
				},
			},
		},
		{
			name: "ERR: playerID < 0",
			args: args{
				id:         validBaseVillage.ID(),
				name:       validBaseVillage.Name(),
				points:     validBaseVillage.Points(),
				x:          validBaseVillage.X(),
				y:          validBaseVillage.Y(),
				continent:  validBaseVillage.Continent(),
				bonus:      validBaseVillage.Bonus(),
				playerID:   -1,
				profileURL: validBaseVillage.ProfileURL(),
			},
			expectedErr: domain.ValidationError{
				Model: "BaseVillage",
				Field: "playerID",
				Err: domain.MinGreaterEqualError{
					Min:     0,
					Current: -1,
				},
			},
		},
		{
			name: "ERR: profileURL can't be nil",
			args: args{
				id:         validBaseVillage.ID(),
				name:       validBaseVillage.Name(),
				points:     validBaseVillage.Points(),
				x:          validBaseVillage.X(),
				y:          validBaseVillage.Y(),
				continent:  validBaseVillage.Continent(),
				bonus:      validBaseVillage.Bonus(),
				playerID:   validBaseVillage.PlayerID(),
				profileURL: nil,
			},
			expectedErr: domain.ValidationError{
				Model: "BaseVillage",
				Field: "profileURL",
				Err:   domain.ErrNil,
			},
		},
	}

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

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

			res, err := domain.NewBaseVillage(
				tt.args.id,
				tt.args.name,
				tt.args.points,
				tt.args.x,
				tt.args.y,
				tt.args.continent,
				tt.args.bonus,
				tt.args.playerID,
				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.points, res.Points())
			assert.Equal(t, tt.args.x, res.X())
			assert.Equal(t, tt.args.y, res.Y())
			assert.Equal(t, tt.args.continent, res.Continent())
			assert.Equal(t, tt.args.bonus, res.Bonus())
			assert.Equal(t, tt.args.playerID, res.PlayerID())
			assert.Equal(t, tt.args.profileURL, res.ProfileURL())
		})
	}
}
