package command

import (
	"context"
	"testing"

	"github.com/stretchr/testify/assert"
	"golang.org/x/text/language"

	"github.com/zitadel/zitadel/internal/domain"
	"github.com/zitadel/zitadel/internal/eventstore"
	"github.com/zitadel/zitadel/internal/eventstore/v1/models"
	"github.com/zitadel/zitadel/internal/repository/user"
	"github.com/zitadel/zitadel/internal/zerrors"
)

func TestCommandSide_ChangeHumanProfile(t *testing.T) {
	type fields struct {
		eventstore *eventstore.Eventstore
	}
	type args struct {
		ctx           context.Context
		address       *domain.Profile
		resourceOwner string
	}
	type res struct {
		want *domain.Profile
		err  func(error) bool
	}
	tests := []struct {
		name   string
		fields fields
		args   args
		res    res
	}{
		{
			name: "user not existing, precondition error",
			fields: fields{
				eventstore: eventstoreExpect(t,
					expectFilter(),
				),
			},
			args: args{
				ctx: context.Background(),
				address: &domain.Profile{
					ObjectRoot: models.ObjectRoot{
						AggregateID: "user1",
					},
					FirstName:         "firstname",
					LastName:          "lastname",
					NickName:          "nickname",
					DisplayName:       "displayname",
					PreferredLanguage: AllowedLanguage,
					Gender:            domain.GenderFemale,
				},
				resourceOwner: "org1",
			},
			res: res{
				err: zerrors.IsPreconditionFailed,
			},
		},
		{
			name: "profile not changed, precondition error",
			fields: fields{
				eventstore: eventstoreExpect(
					t,
					expectFilter(
						eventFromEventPusher(
							user.NewHumanAddedEvent(context.Background(),
								&user.NewAggregate("user1", "org1").Aggregate,
								"username",
								"firstname",
								"lastname",
								"nickname",
								"displayname",
								AllowedLanguage,
								domain.GenderFemale,
								"email",
								true,
							),
						),
					),
				),
			},
			args: args{
				ctx: context.Background(),
				address: &domain.Profile{
					ObjectRoot: models.ObjectRoot{
						AggregateID: "user1",
					},
					FirstName:         "firstname",
					LastName:          "lastname",
					NickName:          "nickname",
					DisplayName:       "displayname",
					PreferredLanguage: AllowedLanguage,
					Gender:            domain.GenderFemale,
				},
				resourceOwner: "org1",
			},
			res: res{
				err: zerrors.IsPreconditionFailed,
			},
		},
		{
			name: "profile changed, ok",
			fields: fields{
				eventstore: eventstoreExpect(
					t,
					expectFilter(
						eventFromEventPusher(
							user.NewHumanAddedEvent(context.Background(),
								&user.NewAggregate("user1", "org1").Aggregate,
								"username",
								"firstname",
								"lastname",
								"nickname",
								"displayname",
								DisallowedLanguage,
								domain.GenderUnspecified,
								"email",
								true,
							),
						),
					),
					expectPush(
						newProfileChangedEvent(context.Background(),
							"user1", "org1",
							"firstname2",
							"lastname2",
							"nickname2",
							"displayname2",
							AllowedLanguage,
							domain.GenderMale,
						),
					),
				),
			},
			args: args{
				ctx: context.Background(),
				address: &domain.Profile{
					ObjectRoot: models.ObjectRoot{
						AggregateID: "user1",
					},
					FirstName:         "firstname2",
					LastName:          "lastname2",
					NickName:          "nickname2",
					DisplayName:       "displayname2",
					PreferredLanguage: AllowedLanguage,
					Gender:            domain.GenderMale,
				},
				resourceOwner: "org1",
			},
			res: res{
				want: &domain.Profile{
					ObjectRoot: models.ObjectRoot{
						AggregateID:   "user1",
						ResourceOwner: "org1",
					},
					FirstName:         "firstname2",
					LastName:          "lastname2",
					NickName:          "nickname2",
					DisplayName:       "displayname2",
					PreferredLanguage: AllowedLanguage,
					Gender:            domain.GenderMale,
				},
			},
		},
		{
			name: "undefined preferred language, ok",
			fields: fields{
				eventstore: eventstoreExpect(
					t,
					expectFilter(
						eventFromEventPusher(
							user.NewHumanAddedEvent(context.Background(),
								&user.NewAggregate("user1", "org1").Aggregate,
								"username",
								"firstname",
								"lastname",
								"nickname",
								"displayname",
								DisallowedLanguage,
								domain.GenderUnspecified,
								"email",
								true,
							),
						),
					),
					expectPush(
						newProfileChangedEvent(context.Background(),
							"user1", "org1",
							"firstname2",
							"lastname2",
							"nickname2",
							"displayname2",
							language.Und,
							domain.GenderMale,
						),
					),
				),
			},
			args: args{
				ctx: context.Background(),
				address: &domain.Profile{
					ObjectRoot: models.ObjectRoot{
						AggregateID: "user1",
					},
					FirstName:   "firstname2",
					LastName:    "lastname2",
					NickName:    "nickname2",
					DisplayName: "displayname2",
					Gender:      domain.GenderMale,
				},
				resourceOwner: "org1",
			},
			res: res{
				want: &domain.Profile{
					ObjectRoot: models.ObjectRoot{
						AggregateID:   "user1",
						ResourceOwner: "org1",
					},
					FirstName:         "firstname2",
					LastName:          "lastname2",
					NickName:          "nickname2",
					DisplayName:       "displayname2",
					PreferredLanguage: language.Und,
					Gender:            domain.GenderMale,
				},
			},
		}, {
			name: "unsupported preferred language, ok",
			fields: fields{
				eventstore: eventstoreExpect(
					t,
					expectFilter(
						eventFromEventPusher(
							user.NewHumanAddedEvent(context.Background(),
								&user.NewAggregate("user1", "org1").Aggregate,
								"username",
								"firstname",
								"lastname",
								"nickname",
								"displayname",
								DisallowedLanguage,
								domain.GenderUnspecified,
								"email",
								true,
							),
						),
					),
					expectPush(
						newProfileChangedEvent(context.Background(),
							"user1", "org1",
							"firstname2",
							"lastname2",
							"nickname2",
							"displayname2",
							UnsupportedLanguage,
							domain.GenderMale,
						),
					),
				),
			},
			args: args{
				ctx: context.Background(),
				address: &domain.Profile{
					ObjectRoot: models.ObjectRoot{
						AggregateID: "user1",
					},
					FirstName:         "firstname2",
					LastName:          "lastname2",
					NickName:          "nickname2",
					DisplayName:       "displayname2",
					PreferredLanguage: UnsupportedLanguage,
					Gender:            domain.GenderMale,
				},
				resourceOwner: "org1",
			},
			res: res{
				want: &domain.Profile{
					ObjectRoot: models.ObjectRoot{
						AggregateID:   "user1",
						ResourceOwner: "org1",
					},
					FirstName:         "firstname2",
					LastName:          "lastname2",
					NickName:          "nickname2",
					DisplayName:       "displayname2",
					PreferredLanguage: UnsupportedLanguage,
					Gender:            domain.GenderMale,
				},
			},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			r := &Commands{
				eventstore: tt.fields.eventstore,
			}
			got, err := r.ChangeHumanProfile(tt.args.ctx, tt.args.address)
			if tt.res.err == nil {
				assert.NoError(t, err)
			}
			if tt.res.err != nil && !tt.res.err(err) {
				t.Errorf("got wrong err: %v ", err)
			}
			if tt.res.err == nil {
				assert.Equal(t, tt.res.want, got)
			}
		})
	}
}

func newProfileChangedEvent(ctx context.Context, userID, resourceOwner, fistName, lastName, nickName, displayName string, lang language.Tag, gender domain.Gender) *user.HumanProfileChangedEvent {
	event, _ := user.NewHumanProfileChangedEvent(ctx,
		&user.NewAggregate(userID, resourceOwner).Aggregate,
		[]user.ProfileChanges{
			user.ChangeFirstName(fistName),
			user.ChangeLastName(lastName),
			user.ChangeNickName(nickName),
			user.ChangeDisplayName(displayName),
			user.ChangePreferredLanguage(lang),
			user.ChangeGender(gender),
		},
	)
	return event
}