package command

import (
	"context"
	"testing"
	"time"

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

	"github.com/zitadel/zitadel/internal/crypto"
	"github.com/zitadel/zitadel/internal/domain"
	"github.com/zitadel/zitadel/internal/eventstore"
	"github.com/zitadel/zitadel/internal/repository/user"
)

func TestCommandSide_userExistsWriteModel(t *testing.T) {
	type fields struct {
		eventstore func(t *testing.T) *eventstore.Eventstore
	}
	type args struct {
		ctx    context.Context
		userID string
	}
	type res struct {
		want *UserV2WriteModel
		err  func(error) bool
	}

	userAgg := user.NewAggregate("user1", "org1")

	tests := []struct {
		name   string
		fields fields
		args   args
		res    res
	}{
		{
			name: "user added",
			fields: fields{
				eventstore: expectEventstore(
					expectFilter(
						eventFromEventPusher(
							newAddHumanEvent("$plain$x$password", true, true, "", language.English),
						),
					),
				),
			},
			args: args{
				ctx:    context.Background(),
				userID: "user1",
			},
			res: res{
				want: &UserV2WriteModel{
					HumanWriteModel:   true,
					MachineWriteModel: true,
					WriteModel: eventstore.WriteModel{
						AggregateID:       "user1",
						Events:            []eventstore.Event{},
						ProcessedSequence: 0,
						ResourceOwner:     "org1",
					},
					UserName:               "username",
					FirstName:              "firstname",
					LastName:               "lastname",
					DisplayName:            "firstname lastname",
					PreferredLanguage:      language.English,
					PasswordEncodedHash:    "$plain$x$password",
					PasswordChangeRequired: true,
					Email:                  "email@test.ch",
					IsEmailVerified:        false,
					InitCode:               nil,
					InitCodeCreationDate:   time.Time{},
					InitCodeExpiry:         0,
					UserState:              domain.UserStateActive,
				},
			},
		},
		{
			name: "user registered",
			fields: fields{
				eventstore: expectEventstore(
					expectFilter(
						eventFromEventPusher(
							newRegisterHumanEvent("username", "$plain$x$password", true, true, "", language.English),
						),
					),
				),
			},
			args: args{
				ctx:    context.Background(),
				userID: "user1",
			},
			res: res{
				want: &UserV2WriteModel{
					HumanWriteModel:   true,
					MachineWriteModel: true,
					WriteModel: eventstore.WriteModel{
						AggregateID:   "user1",
						Events:        []eventstore.Event{},
						ResourceOwner: "org1",
					},
					UserName:               "username",
					FirstName:              "firstname",
					LastName:               "lastname",
					DisplayName:            "firstname lastname",
					PasswordEncodedHash:    "$plain$x$password",
					PasswordChangeRequired: true,
					Email:                  "email@test.ch",
					IsEmailVerified:        false,
					InitCode:               nil,
					InitCodeCreationDate:   time.Time{},
					InitCodeExpiry:         0,
					PreferredLanguage:      language.English,
					UserState:              domain.UserStateActive,
				},
			},
		},
		{
			name: "user machine added",
			fields: fields{
				eventstore: expectEventstore(
					expectFilter(
						eventFromEventPusher(
							newAddMachineEvent(true, domain.OIDCTokenTypeBearer),
						),
					),
				),
			},
			args: args{
				ctx:    context.Background(),
				userID: "user1",
			},
			res: res{
				want: &UserV2WriteModel{
					HumanWriteModel:   true,
					MachineWriteModel: true,
					WriteModel: eventstore.WriteModel{
						AggregateID:       "user1",
						Events:            []eventstore.Event{},
						ProcessedSequence: 0,
						ResourceOwner:     "org1",
					},
					UserName:        "username",
					Name:            "name",
					Description:     "description",
					AccessTokenType: domain.OIDCTokenTypeBearer,
					UserState:       domain.UserStateActive,
				},
			},
		},
		{
			name: "user added with init code",
			fields: fields{
				eventstore: expectEventstore(
					expectFilter(
						eventFromEventPusher(
							newRegisterHumanEvent("username", "$plain$x$password", true, true, "", language.English),
						),
						eventFromEventPusher(
							user.NewHumanInitialCodeAddedEvent(context.Background(),
								&userAgg.Aggregate,
								&crypto.CryptoValue{
									CryptoType: crypto.TypeEncryption,
									Algorithm:  "enc",
									KeyID:      "id",
									Crypted:    []byte("a"),
								},
								time.Hour*1,
								"authRequestID",
							),
						),
					),
				),
			},
			args: args{
				ctx:    context.Background(),
				userID: "user1",
			},
			res: res{
				want: &UserV2WriteModel{
					HumanWriteModel:   true,
					MachineWriteModel: true,
					WriteModel: eventstore.WriteModel{
						AggregateID:       "user1",
						Events:            []eventstore.Event{},
						ProcessedSequence: 0,
						ResourceOwner:     "org1",
					},
					UserName:               "username",
					FirstName:              "firstname",
					LastName:               "lastname",
					DisplayName:            "firstname lastname",
					PreferredLanguage:      language.English,
					PasswordEncodedHash:    "$plain$x$password",
					PasswordChangeRequired: true,
					Email:                  "email@test.ch",
					IsEmailVerified:        false,
					InitCode: &crypto.CryptoValue{
						CryptoType: crypto.TypeEncryption,
						Algorithm:  "enc",
						KeyID:      "id",
						Crypted:    []byte("a"),
					},
					InitCodeCreationDate: time.Time{},
					InitCodeExpiry:       time.Hour * 1,
					UserState:            domain.UserStateInitial,
				},
			},
		},
		{
			name: "user added with initialized",
			fields: fields{
				eventstore: expectEventstore(
					expectFilter(
						eventFromEventPusher(
							newRegisterHumanEvent("username", "$plain$x$password", true, true, "", language.English),
						),
						eventFromEventPusher(
							user.NewHumanInitialCodeAddedEvent(context.Background(),
								&userAgg.Aggregate,
								&crypto.CryptoValue{
									CryptoType: crypto.TypeEncryption,
									Algorithm:  "enc",
									KeyID:      "id",
									Crypted:    []byte("a"),
								},
								time.Hour*1,
								"authRequestID",
							),
						),
						eventFromEventPusher(
							user.NewHumanInitializedCheckSucceededEvent(context.Background(),
								&userAgg.Aggregate,
							),
						),
					),
				),
			},
			args: args{
				ctx:    context.Background(),
				userID: "user1",
			},
			res: res{
				want: &UserV2WriteModel{
					HumanWriteModel:   true,
					MachineWriteModel: true,
					WriteModel: eventstore.WriteModel{
						AggregateID:       "user1",
						Events:            []eventstore.Event{},
						ProcessedSequence: 0,
						ResourceOwner:     "org1",
					},
					UserName:               "username",
					FirstName:              "firstname",
					LastName:               "lastname",
					DisplayName:            "firstname lastname",
					PreferredLanguage:      language.English,
					PasswordEncodedHash:    "$plain$x$password",
					PasswordChangeRequired: true,
					Email:                  "email@test.ch",
					IsEmailVerified:        false,
					UserState:              domain.UserStateActive,
				},
			},
		},
		{
			name: "user added with initialized failed",
			fields: fields{
				eventstore: expectEventstore(
					expectFilter(
						eventFromEventPusher(
							newRegisterHumanEvent("username", "$plain$x$password", true, true, "", language.English),
						),
						eventFromEventPusher(
							user.NewHumanInitialCodeAddedEvent(context.Background(),
								&userAgg.Aggregate,
								&crypto.CryptoValue{
									CryptoType: crypto.TypeEncryption,
									Algorithm:  "enc",
									KeyID:      "id",
									Crypted:    []byte("a"),
								},
								time.Hour*1,
								"authRequestID",
							),
						),
						eventFromEventPusher(
							user.NewHumanInitializedCheckFailedEvent(context.Background(),
								&userAgg.Aggregate,
							),
						),
					),
				),
			},
			args: args{
				ctx:    context.Background(),
				userID: "user1",
			},
			res: res{
				want: &UserV2WriteModel{
					HumanWriteModel:   true,
					MachineWriteModel: true,
					WriteModel: eventstore.WriteModel{
						AggregateID:       "user1",
						Events:            []eventstore.Event{},
						ProcessedSequence: 0,
						ResourceOwner:     "org1",
					},
					UserName:               "username",
					FirstName:              "firstname",
					LastName:               "lastname",
					DisplayName:            "firstname lastname",
					PreferredLanguage:      language.English,
					PasswordEncodedHash:    "$plain$x$password",
					PasswordChangeRequired: true,
					Email:                  "email@test.ch",
					IsEmailVerified:        false,
					InitCode: &crypto.CryptoValue{
						CryptoType: crypto.TypeEncryption,
						Algorithm:  "enc",
						KeyID:      "id",
						Crypted:    []byte("a"),
					},
					InitCodeCreationDate: time.Time{},
					InitCodeExpiry:       time.Hour * 1,
					InitCheckFailedCount: 1,
					UserState:            domain.UserStateInitial,
				},
			},
		},
		{
			name: "user added with username changed",
			fields: fields{
				eventstore: expectEventstore(
					expectFilter(
						eventFromEventPusher(
							newRegisterHumanEvent("username", "$plain$x$password", true, true, "", language.English),
						),
						eventFromEventPusher(
							user.NewUsernameChangedEvent(context.Background(),
								&userAgg.Aggregate,
								"username",
								"changed",
								true,
							),
						),
					),
				),
			},
			args: args{
				ctx:    context.Background(),
				userID: "user1",
			},
			res: res{
				want: &UserV2WriteModel{
					HumanWriteModel:   true,
					MachineWriteModel: true,
					WriteModel: eventstore.WriteModel{
						AggregateID:       "user1",
						Events:            []eventstore.Event{},
						ProcessedSequence: 0,
						ResourceOwner:     "org1",
					},
					UserName:               "changed",
					FirstName:              "firstname",
					LastName:               "lastname",
					DisplayName:            "firstname lastname",
					PreferredLanguage:      language.English,
					PasswordEncodedHash:    "$plain$x$password",
					PasswordChangeRequired: true,
					Email:                  "email@test.ch",
					IsEmailVerified:        false,
					UserState:              domain.UserStateActive,
				},
			},
		},
		{
			name: "user removed",
			fields: fields{
				eventstore: expectEventstore(
					expectFilter(
						eventFromEventPusher(
							newAddHumanEvent("$plain$x$password", true, true, "", language.English),
						),
						eventFromEventPusher(
							user.NewUserRemovedEvent(context.Background(),
								&userAgg.Aggregate,
								"username",
								[]*domain.UserIDPLink{},
								true,
							),
						),
					),
				),
			},
			args: args{
				ctx:    context.Background(),
				userID: "user1",
			},
			res: res{
				want: &UserV2WriteModel{
					HumanWriteModel:   true,
					MachineWriteModel: true,
					WriteModel: eventstore.WriteModel{
						AggregateID:       "user1",
						Events:            []eventstore.Event{},
						ProcessedSequence: 0,
						ResourceOwner:     "org1",
					},
					UserName:               "username",
					FirstName:              "firstname",
					LastName:               "lastname",
					DisplayName:            "firstname lastname",
					PreferredLanguage:      language.English,
					PasswordEncodedHash:    "$plain$x$password",
					PasswordChangeRequired: true,
					Email:                  "email@test.ch",
					IsEmailVerified:        false,
					UserState:              domain.UserStateDeleted,
				},
			},
		},
		{
			name: "user machine removed",
			fields: fields{
				eventstore: expectEventstore(
					expectFilter(
						eventFromEventPusher(
							newAddMachineEvent(true, domain.OIDCTokenTypeBearer),
						),
						eventFromEventPusher(
							user.NewUserRemovedEvent(context.Background(),
								&userAgg.Aggregate,
								"username",
								[]*domain.UserIDPLink{},
								true,
							),
						),
					),
				),
			},
			args: args{
				ctx:    context.Background(),
				userID: "user1",
			},
			res: res{
				want: &UserV2WriteModel{
					HumanWriteModel:   true,
					MachineWriteModel: true,
					WriteModel: eventstore.WriteModel{
						AggregateID:       "user1",
						Events:            []eventstore.Event{},
						ProcessedSequence: 0,
						ResourceOwner:     "org1",
					},
					UserName:        "username",
					Name:            "name",
					Description:     "description",
					AccessTokenType: domain.OIDCTokenTypeBearer,
					UserState:       domain.UserStateDeleted,
				},
			},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			r := &Commands{
				eventstore: tt.fields.eventstore(t),
			}
			wm, err := r.userExistsWriteModel(tt.args.ctx, tt.args.userID)
			if tt.res.err == nil {
				if !assert.NoError(t, err) {
					t.FailNow()
				}
			} else if !tt.res.err(err) {
				t.Errorf("got wrong err: %v ", err)
				return
			}
			if tt.res.err == nil {
				assert.Equal(t, tt.res.want, wm)
			}
		})
	}
}

func TestCommandSide_userHumanWriteModel_profile(t *testing.T) {
	type fields struct {
		eventstore func(t *testing.T) *eventstore.Eventstore
	}
	type args struct {
		ctx    context.Context
		userID string
	}
	type res struct {
		want *UserV2WriteModel
		err  func(error) bool
	}

	userAgg := user.NewAggregate("user1", "org1")
	tests := []struct {
		name   string
		fields fields
		args   args
		res    res
	}{
		{
			name: "user added with profile changed",
			fields: fields{
				eventstore: expectEventstore(
					expectFilter(
						eventFromEventPusher(
							newRegisterHumanEvent("username", "$plain$x$password", true, true, "", language.English),
						),
						eventFromEventPusher(
							func() eventstore.Command {
								cmd, _ := user.NewHumanProfileChangedEvent(context.Background(),
									&userAgg.Aggregate,
									[]user.ProfileChanges{
										user.ChangeFirstName("changedfn"),
										user.ChangeLastName("changedln"),
										user.ChangeNickName("changednn"),
										user.ChangeDisplayName("changeddn"),
										user.ChangePreferredLanguage(language.Afrikaans),
										user.ChangeGender(domain.GenderDiverse),
									},
								)
								return cmd
							}(),
						),
					),
				),
			},
			args: args{
				ctx:    context.Background(),
				userID: "user1",
			},
			res: res{
				want: &UserV2WriteModel{
					HumanWriteModel:   true,
					ProfileWriteModel: true,
					StateWriteModel:   true,
					WriteModel: eventstore.WriteModel{
						AggregateID:       "user1",
						Events:            []eventstore.Event{},
						ProcessedSequence: 0,
						ResourceOwner:     "org1",
					},
					UserName:               "username",
					FirstName:              "changedfn",
					LastName:               "changedln",
					DisplayName:            "changeddn",
					NickName:               "changednn",
					PreferredLanguage:      language.Afrikaans,
					Gender:                 domain.GenderDiverse,
					PasswordEncodedHash:    "$plain$x$password",
					PasswordChangeRequired: true,
					Email:                  "email@test.ch",
					IsEmailVerified:        false,
					UserState:              domain.UserStateActive,
				},
			},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			r := &Commands{
				eventstore: tt.fields.eventstore(t),
			}
			wm, err := r.userHumanWriteModel(tt.args.ctx, tt.args.userID, true, false, false, false, false, false)
			if tt.res.err == nil {
				if !assert.NoError(t, err) {
					t.FailNow()
				}
			} else if !tt.res.err(err) {
				t.Errorf("got wrong err: %v ", err)
				return
			}
			if tt.res.err == nil {
				assert.Equal(t, tt.res.want, wm)
			}
		})
	}
}

func TestCommandSide_userHumanWriteModel_email(t *testing.T) {
	type fields struct {
		eventstore func(t *testing.T) *eventstore.Eventstore
	}
	type args struct {
		ctx    context.Context
		userID string
	}
	type res struct {
		want *UserV2WriteModel
		err  func(error) bool
	}

	userAgg := user.NewAggregate("user1", "org1")

	tests := []struct {
		name   string
		fields fields
		args   args
		res    res
	}{
		{
			name: "user added email changed",
			fields: fields{
				eventstore: expectEventstore(
					expectFilter(
						eventFromEventPusher(
							newAddHumanEvent("$plain$x$password", true, true, "", language.English),
						),
						eventFromEventPusher(
							user.NewHumanEmailChangedEvent(context.Background(),
								&userAgg.Aggregate,
								"changed@test.com",
							),
						),
					),
				),
			},
			args: args{
				ctx:    context.Background(),
				userID: "user1",
			},
			res: res{
				want: &UserV2WriteModel{
					HumanWriteModel: true,
					EmailWriteModel: true,
					StateWriteModel: true,
					WriteModel: eventstore.WriteModel{
						AggregateID:       "user1",
						Events:            []eventstore.Event{},
						ProcessedSequence: 0,
						ResourceOwner:     "org1",
					},
					UserName:               "username",
					FirstName:              "firstname",
					LastName:               "lastname",
					DisplayName:            "firstname lastname",
					PreferredLanguage:      language.English,
					PasswordEncodedHash:    "$plain$x$password",
					PasswordChangeRequired: true,
					Email:                  "changed@test.com",
					IsEmailVerified:        false,
					InitCode:               nil,
					InitCodeCreationDate:   time.Time{},
					InitCodeExpiry:         0,
					UserState:              domain.UserStateActive,
				},
			},
		},
		{
			name: "user added with email code",
			fields: fields{
				eventstore: expectEventstore(
					expectFilter(
						eventFromEventPusher(
							newRegisterHumanEvent("username", "$plain$x$password", true, true, "", language.English),
						),
						eventFromEventPusher(
							user.NewHumanEmailCodeAddedEventV2(context.Background(),
								&userAgg.Aggregate,
								&crypto.CryptoValue{
									CryptoType: crypto.TypeEncryption,
									Algorithm:  "enc",
									KeyID:      "id",
									Crypted:    []byte("a"),
								},
								time.Hour*1,
								"",
								false,
								"",
							),
						),
					),
				),
			},
			args: args{
				ctx:    context.Background(),
				userID: "user1",
			},
			res: res{
				want: &UserV2WriteModel{
					HumanWriteModel: true,
					EmailWriteModel: true,
					StateWriteModel: true,
					WriteModel: eventstore.WriteModel{
						AggregateID:       "user1",
						Events:            []eventstore.Event{},
						ProcessedSequence: 0,
						ResourceOwner:     "org1",
					},
					UserName:               "username",
					FirstName:              "firstname",
					LastName:               "lastname",
					DisplayName:            "firstname lastname",
					PreferredLanguage:      language.English,
					PasswordEncodedHash:    "$plain$x$password",
					PasswordChangeRequired: true,
					Email:                  "email@test.ch",
					IsEmailVerified:        false,
					EmailCode: &crypto.CryptoValue{
						CryptoType: crypto.TypeEncryption,
						Algorithm:  "enc",
						KeyID:      "id",
						Crypted:    []byte("a"),
					},
					EmailCodeCreationDate: time.Time{},
					EmailCodeExpiry:       time.Hour * 1,
					UserState:             domain.UserStateActive,
				},
			},
		},
		{
			name: "user added with email code verified",
			fields: fields{
				eventstore: expectEventstore(
					expectFilter(
						eventFromEventPusher(
							newRegisterHumanEvent("username", "$plain$x$password", true, true, "", language.English),
						),
						eventFromEventPusher(
							user.NewHumanEmailCodeAddedEventV2(context.Background(),
								&userAgg.Aggregate,
								&crypto.CryptoValue{
									CryptoType: crypto.TypeEncryption,
									Algorithm:  "enc",
									KeyID:      "id",
									Crypted:    []byte("a"),
								},
								time.Hour*1,
								"",
								false,
								"",
							),
						),
						eventFromEventPusher(
							user.NewHumanEmailVerifiedEvent(context.Background(),
								&userAgg.Aggregate,
							),
						),
					),
				),
			},
			args: args{
				ctx:    context.Background(),
				userID: "user1",
			},
			res: res{
				want: &UserV2WriteModel{
					HumanWriteModel: true,
					EmailWriteModel: true,
					StateWriteModel: true,
					WriteModel: eventstore.WriteModel{
						AggregateID:       "user1",
						Events:            []eventstore.Event{},
						ProcessedSequence: 0,
						ResourceOwner:     "org1",
					},
					UserName:               "username",
					FirstName:              "firstname",
					LastName:               "lastname",
					DisplayName:            "firstname lastname",
					PreferredLanguage:      language.English,
					PasswordEncodedHash:    "$plain$x$password",
					PasswordChangeRequired: true,
					Email:                  "email@test.ch",
					IsEmailVerified:        true,
					UserState:              domain.UserStateActive,
				},
			},
		},
		{
			name: "user added with email code verified failed",
			fields: fields{
				eventstore: expectEventstore(
					expectFilter(
						eventFromEventPusher(
							newRegisterHumanEvent("username", "$plain$x$password", true, true, "", language.English),
						),
						eventFromEventPusher(
							user.NewHumanEmailCodeAddedEventV2(context.Background(),
								&userAgg.Aggregate,
								&crypto.CryptoValue{
									CryptoType: crypto.TypeEncryption,
									Algorithm:  "enc",
									KeyID:      "id",
									Crypted:    []byte("a"),
								},
								time.Hour*1,
								"",
								false,
								"",
							),
						),
						eventFromEventPusher(
							user.NewHumanEmailVerificationFailedEvent(context.Background(),
								&userAgg.Aggregate,
							),
						),
					),
				),
			},
			args: args{
				ctx:    context.Background(),
				userID: "user1",
			},
			res: res{
				want: &UserV2WriteModel{
					HumanWriteModel: true,
					EmailWriteModel: true,
					StateWriteModel: true,
					WriteModel: eventstore.WriteModel{
						AggregateID:       "user1",
						Events:            []eventstore.Event{},
						ProcessedSequence: 0,
						ResourceOwner:     "org1",
					},
					UserName:               "username",
					FirstName:              "firstname",
					LastName:               "lastname",
					DisplayName:            "firstname lastname",
					PreferredLanguage:      language.English,
					PasswordEncodedHash:    "$plain$x$password",
					PasswordChangeRequired: true,
					Email:                  "email@test.ch",
					IsEmailVerified:        false,
					EmailCode: &crypto.CryptoValue{
						CryptoType: crypto.TypeEncryption,
						Algorithm:  "enc",
						KeyID:      "id",
						Crypted:    []byte("a"),
					},
					EmailCodeCreationDate: time.Time{},
					EmailCodeExpiry:       time.Hour * 1,
					EmailCheckFailedCount: 1,
					UserState:             domain.UserStateActive,
				},
			},
		},
		{
			name: "user added with email code verified, then changed",
			fields: fields{
				eventstore: expectEventstore(
					expectFilter(
						eventFromEventPusher(
							newRegisterHumanEvent("username", "$plain$x$password", true, true, "", language.English),
						),
						eventFromEventPusher(
							user.NewHumanEmailCodeAddedEventV2(context.Background(),
								&userAgg.Aggregate,
								&crypto.CryptoValue{
									CryptoType: crypto.TypeEncryption,
									Algorithm:  "enc",
									KeyID:      "id",
									Crypted:    []byte("a"),
								},
								time.Hour*1,
								"",
								false,
								"",
							),
						),
						eventFromEventPusher(
							user.NewHumanEmailVerifiedEvent(context.Background(),
								&userAgg.Aggregate,
							),
						),
						eventFromEventPusher(
							user.NewHumanEmailChangedEvent(context.Background(),
								&userAgg.Aggregate,
								"changed@test.com",
							),
						),
					),
				),
			},
			args: args{
				ctx:    context.Background(),
				userID: "user1",
			},
			res: res{
				want: &UserV2WriteModel{
					HumanWriteModel: true,
					EmailWriteModel: true,
					StateWriteModel: true,
					WriteModel: eventstore.WriteModel{
						AggregateID:       "user1",
						Events:            []eventstore.Event{},
						ProcessedSequence: 0,
						ResourceOwner:     "org1",
					},
					UserName:               "username",
					FirstName:              "firstname",
					LastName:               "lastname",
					DisplayName:            "firstname lastname",
					PreferredLanguage:      language.English,
					PasswordEncodedHash:    "$plain$x$password",
					PasswordChangeRequired: true,
					Email:                  "changed@test.com",
					IsEmailVerified:        false,
					UserState:              domain.UserStateActive,
				},
			},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			r := &Commands{
				eventstore: tt.fields.eventstore(t),
			}
			wm, err := r.userHumanWriteModel(tt.args.ctx, tt.args.userID, false, true, false, false, false, false)
			if tt.res.err == nil {
				if !assert.NoError(t, err) {
					t.FailNow()
				}
			} else if !tt.res.err(err) {
				t.Errorf("got wrong err: %v ", err)
				return
			}
			if tt.res.err == nil {
				assert.Equal(t, tt.res.want, wm)
			}
		})
	}
}

func TestCommandSide_userHumanWriteModel_phone(t *testing.T) {
	type fields struct {
		eventstore func(t *testing.T) *eventstore.Eventstore
	}
	type args struct {
		ctx    context.Context
		userID string
	}
	type res struct {
		want *UserV2WriteModel
		err  func(error) bool
	}

	userAgg := user.NewAggregate("user1", "org1")

	tests := []struct {
		name   string
		fields fields
		args   args
		res    res
	}{
		{
			name: "user added phone changed",
			fields: fields{
				eventstore: expectEventstore(
					expectFilter(
						eventFromEventPusher(
							newAddHumanEvent("$plain$x$password", true, true, "", language.English),
						),
						eventFromEventPusher(
							user.NewHumanPhoneChangedEvent(context.Background(),
								&userAgg.Aggregate,
								"+41791234567",
							),
						),
					),
				),
			},
			args: args{
				ctx:    context.Background(),
				userID: "user1",
			},
			res: res{
				want: &UserV2WriteModel{
					HumanWriteModel: true,
					PhoneWriteModel: true,
					StateWriteModel: true,
					WriteModel: eventstore.WriteModel{
						AggregateID:       "user1",
						Events:            []eventstore.Event{},
						ProcessedSequence: 0,
						ResourceOwner:     "org1",
					},
					UserName:               "username",
					FirstName:              "firstname",
					LastName:               "lastname",
					DisplayName:            "firstname lastname",
					PreferredLanguage:      language.English,
					PasswordEncodedHash:    "$plain$x$password",
					PasswordChangeRequired: true,
					Email:                  "email@test.ch",
					IsEmailVerified:        false,
					Phone:                  "+41791234567",
					IsPhoneVerified:        false,
					UserState:              domain.UserStateActive,
				},
			},
		},
		{
			name: "user added with phone code",
			fields: fields{
				eventstore: expectEventstore(
					expectFilter(
						eventFromEventPusher(
							newRegisterHumanEvent("username", "$plain$x$password", true, true, "", language.English),
						),
						eventFromEventPusher(
							user.NewHumanPhoneChangedEvent(context.Background(),
								&userAgg.Aggregate,
								"+41791234567",
							),
						),
						eventFromEventPusher(
							user.NewHumanPhoneCodeAddedEventV2(context.Background(),
								&userAgg.Aggregate,
								&crypto.CryptoValue{
									CryptoType: crypto.TypeEncryption,
									Algorithm:  "enc",
									KeyID:      "id",
									Crypted:    []byte("a"),
								},
								time.Hour*1,
								false,
								"",
							),
						),
					),
				),
			},
			args: args{
				ctx:    context.Background(),
				userID: "user1",
			},
			res: res{
				want: &UserV2WriteModel{
					HumanWriteModel: true,
					PhoneWriteModel: true,
					StateWriteModel: true,
					WriteModel: eventstore.WriteModel{
						AggregateID:       "user1",
						Events:            []eventstore.Event{},
						ProcessedSequence: 0,
						ResourceOwner:     "org1",
					},
					UserName:               "username",
					FirstName:              "firstname",
					LastName:               "lastname",
					DisplayName:            "firstname lastname",
					PreferredLanguage:      language.English,
					PasswordEncodedHash:    "$plain$x$password",
					PasswordChangeRequired: true,
					Email:                  "email@test.ch",
					IsEmailVerified:        false,
					Phone:                  "+41791234567",
					IsPhoneVerified:        false,
					PhoneCode: &crypto.CryptoValue{
						CryptoType: crypto.TypeEncryption,
						Algorithm:  "enc",
						KeyID:      "id",
						Crypted:    []byte("a"),
					},
					PhoneCodeCreationDate: time.Time{},
					PhoneCodeExpiry:       time.Hour * 1,
					UserState:             domain.UserStateActive,
				},
			},
		},
		{
			name: "user added with phone code external",
			fields: fields{
				eventstore: expectEventstore(
					expectFilter(
						eventFromEventPusher(
							newRegisterHumanEvent("username", "$plain$x$password", true, true, "", language.English),
						),
						eventFromEventPusher(
							user.NewHumanPhoneChangedEvent(context.Background(),
								&userAgg.Aggregate,
								"+41791234567",
							),
						),
						eventFromEventPusher(
							user.NewHumanPhoneCodeAddedEventV2(context.Background(),
								&userAgg.Aggregate,
								nil,
								0,
								false,
								"id",
							),
						),
					),
				),
			},
			args: args{
				ctx:    context.Background(),
				userID: "user1",
			},
			res: res{
				want: &UserV2WriteModel{
					HumanWriteModel: true,
					PhoneWriteModel: true,
					StateWriteModel: true,
					WriteModel: eventstore.WriteModel{
						AggregateID:       "user1",
						Events:            []eventstore.Event{},
						ProcessedSequence: 0,
						ResourceOwner:     "org1",
					},
					UserName:               "username",
					FirstName:              "firstname",
					LastName:               "lastname",
					DisplayName:            "firstname lastname",
					PreferredLanguage:      language.English,
					PasswordEncodedHash:    "$plain$x$password",
					PasswordChangeRequired: true,
					Email:                  "email@test.ch",
					IsEmailVerified:        false,
					Phone:                  "+41791234567",
					IsPhoneVerified:        false,
					PhoneCode:              nil,
					PhoneCodeCreationDate:  time.Time{},
					PhoneCodeExpiry:        0,
					UserState:              domain.UserStateActive,
				},
			},
		},
		{
			name: "user added with phone code verified",
			fields: fields{
				eventstore: expectEventstore(
					expectFilter(
						eventFromEventPusher(
							newRegisterHumanEvent("username", "$plain$x$password", true, true, "", language.English),
						),
						eventFromEventPusher(
							user.NewHumanPhoneChangedEvent(context.Background(),
								&userAgg.Aggregate,
								"+41791234567",
							),
						),
						eventFromEventPusher(
							user.NewHumanPhoneCodeAddedEventV2(context.Background(),
								&userAgg.Aggregate,
								&crypto.CryptoValue{
									CryptoType: crypto.TypeEncryption,
									Algorithm:  "enc",
									KeyID:      "id",
									Crypted:    []byte("a"),
								},
								time.Hour*1,
								false,
								"",
							),
						),
						eventFromEventPusher(
							user.NewHumanPhoneVerifiedEvent(context.Background(),
								&userAgg.Aggregate,
							),
						),
					),
				),
			},
			args: args{
				ctx:    context.Background(),
				userID: "user1",
			},
			res: res{
				want: &UserV2WriteModel{
					HumanWriteModel: true,
					PhoneWriteModel: true,
					StateWriteModel: true,
					WriteModel: eventstore.WriteModel{
						AggregateID:       "user1",
						Events:            []eventstore.Event{},
						ProcessedSequence: 0,
						ResourceOwner:     "org1",
					},
					UserName:               "username",
					FirstName:              "firstname",
					LastName:               "lastname",
					DisplayName:            "firstname lastname",
					PreferredLanguage:      language.English,
					PasswordEncodedHash:    "$plain$x$password",
					PasswordChangeRequired: true,
					Email:                  "email@test.ch",
					IsEmailVerified:        false,
					Phone:                  "+41791234567",
					IsPhoneVerified:        true,
					UserState:              domain.UserStateActive,
				},
			},
		},
		{
			name: "user added with phone code verified failed",
			fields: fields{
				eventstore: expectEventstore(
					expectFilter(
						eventFromEventPusher(
							newRegisterHumanEvent("username", "$plain$x$password", true, true, "", language.English),
						),

						eventFromEventPusher(
							user.NewHumanPhoneChangedEvent(context.Background(),
								&userAgg.Aggregate,
								"+41791234567",
							),
						),
						eventFromEventPusher(
							user.NewHumanPhoneCodeAddedEventV2(context.Background(),
								&userAgg.Aggregate,
								&crypto.CryptoValue{
									CryptoType: crypto.TypeEncryption,
									Algorithm:  "enc",
									KeyID:      "id",
									Crypted:    []byte("a"),
								},
								time.Hour*1,
								false,
								"",
							),
						),
						eventFromEventPusher(
							user.NewHumanPhoneVerificationFailedEvent(context.Background(),
								&userAgg.Aggregate,
							),
						),
					),
				),
			},
			args: args{
				ctx:    context.Background(),
				userID: "user1",
			},
			res: res{
				want: &UserV2WriteModel{
					HumanWriteModel: true,
					PhoneWriteModel: true,
					StateWriteModel: true,
					WriteModel: eventstore.WriteModel{
						AggregateID:       "user1",
						Events:            []eventstore.Event{},
						ProcessedSequence: 0,
						ResourceOwner:     "org1",
					},
					UserName:               "username",
					FirstName:              "firstname",
					LastName:               "lastname",
					DisplayName:            "firstname lastname",
					PreferredLanguage:      language.English,
					PasswordEncodedHash:    "$plain$x$password",
					PasswordChangeRequired: true,
					Email:                  "email@test.ch",
					IsEmailVerified:        false,
					Phone:                  "+41791234567",
					IsPhoneVerified:        false,
					PhoneCode: &crypto.CryptoValue{
						CryptoType: crypto.TypeEncryption,
						Algorithm:  "enc",
						KeyID:      "id",
						Crypted:    []byte("a"),
					},
					PhoneCodeCreationDate: time.Time{},
					PhoneCodeExpiry:       time.Hour * 1,
					PhoneCheckFailedCount: 1,
					UserState:             domain.UserStateActive,
				},
			},
		},
		{
			name: "user added with email code verified, then changed",
			fields: fields{
				eventstore: expectEventstore(
					expectFilter(
						eventFromEventPusher(
							newRegisterHumanEvent("username", "$plain$x$password", true, true, "", language.English),
						),
						eventFromEventPusher(
							user.NewHumanPhoneChangedEvent(context.Background(),
								&userAgg.Aggregate,
								"+41791234567",
							),
						),
						eventFromEventPusher(
							user.NewHumanPhoneCodeAddedEventV2(context.Background(),
								&userAgg.Aggregate,
								&crypto.CryptoValue{
									CryptoType: crypto.TypeEncryption,
									Algorithm:  "enc",
									KeyID:      "id",
									Crypted:    []byte("a"),
								},
								time.Hour*1,
								false,
								"",
							),
						),
						eventFromEventPusher(
							user.NewHumanPhoneVerifiedEvent(context.Background(),
								&userAgg.Aggregate,
							),
						),
						eventFromEventPusher(
							user.NewHumanPhoneChangedEvent(context.Background(),
								&userAgg.Aggregate,
								"+41797654321",
							),
						),
					),
				),
			},
			args: args{
				ctx:    context.Background(),
				userID: "user1",
			},
			res: res{
				want: &UserV2WriteModel{
					HumanWriteModel: true,
					PhoneWriteModel: true,
					StateWriteModel: true,
					WriteModel: eventstore.WriteModel{
						AggregateID:       "user1",
						Events:            []eventstore.Event{},
						ProcessedSequence: 0,
						ResourceOwner:     "org1",
					},
					UserName:               "username",
					FirstName:              "firstname",
					LastName:               "lastname",
					DisplayName:            "firstname lastname",
					PreferredLanguage:      language.English,
					PasswordEncodedHash:    "$plain$x$password",
					PasswordChangeRequired: true,
					Email:                  "email@test.ch",
					IsEmailVerified:        false,
					Phone:                  "+41797654321",
					IsPhoneVerified:        false,
					UserState:              domain.UserStateActive,
				},
			},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			r := &Commands{
				eventstore: tt.fields.eventstore(t),
			}
			wm, err := r.userHumanWriteModel(tt.args.ctx, tt.args.userID, false, false, true, false, false, false)
			if tt.res.err == nil {
				if !assert.NoError(t, err) {
					t.FailNow()
				}
			} else if !tt.res.err(err) {
				t.Errorf("got wrong err: %v ", err)
				return
			}
			if tt.res.err == nil {
				assert.Equal(t, tt.res.want, wm)
			}
		})
	}
}

func TestCommandSide_userHumanWriteModel_password(t *testing.T) {
	type fields struct {
		eventstore func(t *testing.T) *eventstore.Eventstore
	}
	type args struct {
		ctx    context.Context
		userID string
	}
	type res struct {
		want *UserV2WriteModel
		err  func(error) bool
	}

	userAgg := user.NewAggregate("user1", "org1")

	tests := []struct {
		name   string
		fields fields
		args   args
		res    res
	}{
		{
			name: "user added password hashchanged",
			fields: fields{
				eventstore: expectEventstore(
					expectFilter(
						eventFromEventPusher(
							newAddHumanEvent("$plain$x$password", true, true, "", language.English),
						),
						eventFromEventPusher(
							user.NewHumanPasswordHashUpdatedEvent(context.Background(),
								&userAgg.Aggregate,
								"hash",
							),
						),
					),
				),
			},
			args: args{
				ctx:    context.Background(),
				userID: "user1",
			},
			res: res{
				want: &UserV2WriteModel{
					HumanWriteModel:    true,
					PasswordWriteModel: true,
					StateWriteModel:    true,
					WriteModel: eventstore.WriteModel{
						AggregateID:       "user1",
						Events:            []eventstore.Event{},
						ProcessedSequence: 0,
						ResourceOwner:     "org1",
					},
					UserName:               "username",
					FirstName:              "firstname",
					LastName:               "lastname",
					DisplayName:            "firstname lastname",
					PreferredLanguage:      language.English,
					PasswordEncodedHash:    "hash",
					PasswordChangeRequired: true,
					Email:                  "email@test.ch",
					IsEmailVerified:        false,
					UserState:              domain.UserStateActive,
				},
			},
		},
		{
			name: "user added password changed",
			fields: fields{
				eventstore: expectEventstore(
					expectFilter(
						eventFromEventPusher(
							newAddHumanEvent("$plain$x$password", true, true, "", language.English),
						),
						eventFromEventPusher(
							user.NewHumanPasswordChangedEvent(context.Background(),
								&userAgg.Aggregate,
								"hash",
								false,
								"",
							),
						),
					),
				),
			},
			args: args{
				ctx:    context.Background(),
				userID: "user1",
			},
			res: res{
				want: &UserV2WriteModel{
					HumanWriteModel:    true,
					PasswordWriteModel: true,
					StateWriteModel:    true,
					WriteModel: eventstore.WriteModel{
						AggregateID:       "user1",
						Events:            []eventstore.Event{},
						ProcessedSequence: 0,
						ResourceOwner:     "org1",
					},
					UserName:               "username",
					FirstName:              "firstname",
					LastName:               "lastname",
					DisplayName:            "firstname lastname",
					PreferredLanguage:      language.English,
					PasswordEncodedHash:    "hash",
					PasswordChangeRequired: false,
					Email:                  "email@test.ch",
					IsEmailVerified:        false,
					UserState:              domain.UserStateActive,
				},
			},
		},
		{
			name: "user added with password code",
			fields: fields{
				eventstore: expectEventstore(
					expectFilter(
						eventFromEventPusher(
							newRegisterHumanEvent("username", "$plain$x$password", true, true, "", language.English),
						),
						eventFromEventPusher(
							user.NewHumanPasswordCodeAddedEventV2(context.Background(),
								&userAgg.Aggregate,
								&crypto.CryptoValue{
									CryptoType: crypto.TypeEncryption,
									Algorithm:  "enc",
									KeyID:      "id",
									Crypted:    []byte("a"),
								},
								time.Hour*1,
								domain.NotificationTypeEmail,
								"",
								false,
								"",
							),
						),
					),
				),
			},
			args: args{
				ctx:    context.Background(),
				userID: "user1",
			},
			res: res{
				want: &UserV2WriteModel{
					HumanWriteModel:    true,
					PasswordWriteModel: true,
					StateWriteModel:    true,
					WriteModel: eventstore.WriteModel{
						AggregateID:       "user1",
						Events:            []eventstore.Event{},
						ProcessedSequence: 0,
						ResourceOwner:     "org1",
					},
					UserName:               "username",
					FirstName:              "firstname",
					LastName:               "lastname",
					DisplayName:            "firstname lastname",
					PreferredLanguage:      language.English,
					PasswordEncodedHash:    "$plain$x$password",
					PasswordChangeRequired: true,
					Email:                  "email@test.ch",
					IsEmailVerified:        false,
					PasswordCode: &crypto.CryptoValue{
						CryptoType: crypto.TypeEncryption,
						Algorithm:  "enc",
						KeyID:      "id",
						Crypted:    []byte("a"),
					},
					PasswordCodeCreationDate: time.Time{},
					PasswordCodeExpiry:       time.Hour * 1,
					UserState:                domain.UserStateActive,
				},
			},
		},
		{
			name: "user added with password code and then change",
			fields: fields{
				eventstore: expectEventstore(
					expectFilter(
						eventFromEventPusher(
							newRegisterHumanEvent("username", "$plain$x$password", true, true, "", language.English),
						),
						eventFromEventPusher(
							user.NewHumanPasswordCodeAddedEventV2(context.Background(),
								&userAgg.Aggregate,
								&crypto.CryptoValue{
									CryptoType: crypto.TypeEncryption,
									Algorithm:  "enc",
									KeyID:      "id",
									Crypted:    []byte("a"),
								},
								time.Hour*1,
								domain.NotificationTypeEmail,
								"",
								false,
								"",
							),
						),
						eventFromEventPusher(
							user.NewHumanPasswordChangedEvent(context.Background(),
								&userAgg.Aggregate,
								"hash",
								true,
								"",
							),
						),
					),
				),
			},
			args: args{
				ctx:    context.Background(),
				userID: "user1",
			},
			res: res{
				want: &UserV2WriteModel{
					HumanWriteModel:    true,
					PasswordWriteModel: true,
					StateWriteModel:    true,
					WriteModel: eventstore.WriteModel{
						AggregateID:       "user1",
						Events:            []eventstore.Event{},
						ProcessedSequence: 0,
						ResourceOwner:     "org1",
					},
					UserName:               "username",
					FirstName:              "firstname",
					LastName:               "lastname",
					DisplayName:            "firstname lastname",
					PreferredLanguage:      language.English,
					PasswordEncodedHash:    "hash",
					PasswordChangeRequired: true,
					Email:                  "email@test.ch",
					IsEmailVerified:        false,
					UserState:              domain.UserStateActive,
				},
			},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			r := &Commands{
				eventstore: tt.fields.eventstore(t),
			}
			wm, err := r.userHumanWriteModel(tt.args.ctx, tt.args.userID, false, false, false, true, false, false)
			if tt.res.err == nil {
				if !assert.NoError(t, err) {
					t.FailNow()
				}
			} else if !tt.res.err(err) {
				t.Errorf("got wrong err: %v ", err)
				return
			}
			if tt.res.err == nil {
				assert.Equal(t, tt.res.want, wm)
			}
		})
	}
}

func TestCommandSide_userStateWriteModel(t *testing.T) {
	type fields struct {
		eventstore func(t *testing.T) *eventstore.Eventstore
	}
	type args struct {
		ctx    context.Context
		userID string
	}
	type res struct {
		want *UserV2WriteModel
		err  func(error) bool
	}

	userAgg := user.NewAggregate("user1", "org1")

	tests := []struct {
		name   string
		fields fields
		args   args
		res    res
	}{
		{
			name: "user added",
			fields: fields{
				eventstore: expectEventstore(
					expectFilter(
						eventFromEventPusher(
							newAddHumanEvent("$plain$x$password", true, true, "", language.English),
						),
					),
				),
			},
			args: args{
				ctx:    context.Background(),
				userID: "user1",
			},
			res: res{
				want: &UserV2WriteModel{
					HumanWriteModel:   true,
					MachineWriteModel: true,
					StateWriteModel:   true,
					WriteModel: eventstore.WriteModel{
						AggregateID:       "user1",
						Events:            []eventstore.Event{},
						ProcessedSequence: 0,
						ResourceOwner:     "org1",
					},
					UserName:                 "username",
					FirstName:                "firstname",
					LastName:                 "lastname",
					DisplayName:              "firstname lastname",
					PreferredLanguage:        language.English,
					PasswordEncodedHash:      "$plain$x$password",
					PasswordChangeRequired:   true,
					PasswordCheckFailedCount: 0,
					Email:                    "email@test.ch",
					IsEmailVerified:          false,
					UserState:                domain.UserStateActive,
				},
			},
		},
		{
			name: "user added initialized",
			fields: fields{
				eventstore: expectEventstore(
					expectFilter(
						eventFromEventPusher(
							newAddHumanEvent("$plain$x$password", true, true, "", language.English),
						),
						eventFromEventPusher(
							user.NewHumanInitializedCheckSucceededEvent(context.Background(),
								&userAgg.Aggregate,
							),
						),
					),
				),
			},
			args: args{
				ctx:    context.Background(),
				userID: "user1",
			},
			res: res{
				want: &UserV2WriteModel{
					HumanWriteModel:   true,
					MachineWriteModel: true,
					StateWriteModel:   true,
					WriteModel: eventstore.WriteModel{
						AggregateID:       "user1",
						Events:            []eventstore.Event{},
						ProcessedSequence: 0,
						ResourceOwner:     "org1",
					},
					UserName:                 "username",
					FirstName:                "firstname",
					LastName:                 "lastname",
					DisplayName:              "firstname lastname",
					PreferredLanguage:        language.English,
					PasswordEncodedHash:      "$plain$x$password",
					PasswordChangeRequired:   true,
					PasswordCheckFailedCount: 0,
					Email:                    "email@test.ch",
					IsEmailVerified:          false,
					UserState:                domain.UserStateActive,
				},
			},
		},
		{
			name: "user machine added",
			fields: fields{
				eventstore: expectEventstore(
					expectFilter(
						eventFromEventPusher(
							newAddMachineEvent(true, domain.OIDCTokenTypeBearer),
						),
					),
				),
			},
			args: args{
				ctx:    context.Background(),
				userID: "user1",
			},
			res: res{
				want: &UserV2WriteModel{
					HumanWriteModel:   true,
					MachineWriteModel: true,
					StateWriteModel:   true,
					WriteModel: eventstore.WriteModel{
						AggregateID:       "user1",
						Events:            []eventstore.Event{},
						ProcessedSequence: 0,
						ResourceOwner:     "org1",
					},
					UserName:        "username",
					Name:            "name",
					Description:     "description",
					AccessTokenType: domain.OIDCTokenTypeBearer,
					UserState:       domain.UserStateActive,
				},
			},
		},
		{
			name: "user added locked",
			fields: fields{
				eventstore: expectEventstore(
					expectFilter(
						eventFromEventPusher(
							newAddHumanEvent("$plain$x$password", true, true, "", language.English),
						),
						eventFromEventPusher(
							user.NewHumanPasswordCheckFailedEvent(context.Background(),
								&userAgg.Aggregate,
								nil,
							),
						),
						eventFromEventPusher(
							user.NewHumanPasswordCheckFailedEvent(context.Background(),
								&userAgg.Aggregate,
								nil,
							),
						),
						eventFromEventPusher(
							user.NewHumanPasswordCheckFailedEvent(context.Background(),
								&userAgg.Aggregate,
								nil,
							),
						),
						eventFromEventPusher(
							user.NewUserLockedEvent(context.Background(),
								&userAgg.Aggregate,
							),
						),
					),
				),
			},
			args: args{
				ctx:    context.Background(),
				userID: "user1",
			},
			res: res{
				want: &UserV2WriteModel{
					HumanWriteModel:   true,
					MachineWriteModel: true,
					StateWriteModel:   true,
					WriteModel: eventstore.WriteModel{
						AggregateID:       "user1",
						Events:            []eventstore.Event{},
						ProcessedSequence: 0,
						ResourceOwner:     "org1",
					},
					UserName:                 "username",
					FirstName:                "firstname",
					LastName:                 "lastname",
					DisplayName:              "firstname lastname",
					PreferredLanguage:        language.English,
					PasswordEncodedHash:      "$plain$x$password",
					PasswordChangeRequired:   true,
					PasswordCheckFailedCount: 3,
					Email:                    "email@test.ch",
					IsEmailVerified:          false,
					UserState:                domain.UserStateLocked,
				},
			},
		},
		{
			name: "user added locked and unlocked",
			fields: fields{
				eventstore: expectEventstore(
					expectFilter(
						eventFromEventPusher(
							newRegisterHumanEvent("username", "$plain$x$password", true, true, "", language.English),
						),
						eventFromEventPusher(
							user.NewHumanPasswordCheckFailedEvent(context.Background(),
								&userAgg.Aggregate,
								nil,
							),
						),
						eventFromEventPusher(
							user.NewHumanPasswordCheckFailedEvent(context.Background(),
								&userAgg.Aggregate,
								nil,
							),
						),
						eventFromEventPusher(
							user.NewHumanPasswordCheckFailedEvent(context.Background(),
								&userAgg.Aggregate,
								nil,
							),
						),
						eventFromEventPusher(
							user.NewUserLockedEvent(context.Background(),
								&userAgg.Aggregate,
							),
						),
						eventFromEventPusher(
							user.NewUserUnlockedEvent(context.Background(),
								&userAgg.Aggregate,
							),
						),
					),
				),
			},
			args: args{
				ctx:    context.Background(),
				userID: "user1",
			},
			res: res{
				want: &UserV2WriteModel{
					HumanWriteModel:   true,
					MachineWriteModel: true,
					StateWriteModel:   true,
					WriteModel: eventstore.WriteModel{
						AggregateID:       "user1",
						Events:            []eventstore.Event{},
						ProcessedSequence: 0,
						ResourceOwner:     "org1",
					},
					UserName:                 "username",
					FirstName:                "firstname",
					LastName:                 "lastname",
					DisplayName:              "firstname lastname",
					PreferredLanguage:        language.English,
					PasswordEncodedHash:      "$plain$x$password",
					PasswordChangeRequired:   true,
					PasswordCheckFailedCount: 0,
					Email:                    "email@test.ch",
					IsEmailVerified:          false,
					UserState:                domain.UserStateActive,
				},
			},
		},
		{
			name: "user added deactivated",
			fields: fields{
				eventstore: expectEventstore(
					expectFilter(
						eventFromEventPusher(
							newRegisterHumanEvent("username", "$plain$x$password", true, true, "", language.English),
						),
						eventFromEventPusher(
							user.NewUserDeactivatedEvent(context.Background(),
								&userAgg.Aggregate,
							),
						),
					),
				),
			},
			args: args{
				ctx:    context.Background(),
				userID: "user1",
			},
			res: res{
				want: &UserV2WriteModel{
					HumanWriteModel:   true,
					MachineWriteModel: true,
					StateWriteModel:   true,
					WriteModel: eventstore.WriteModel{
						AggregateID:       "user1",
						Events:            []eventstore.Event{},
						ProcessedSequence: 0,
						ResourceOwner:     "org1",
					},
					UserName:               "username",
					FirstName:              "firstname",
					LastName:               "lastname",
					DisplayName:            "firstname lastname",
					PreferredLanguage:      language.English,
					PasswordEncodedHash:    "$plain$x$password",
					PasswordChangeRequired: true,
					Email:                  "email@test.ch",
					IsEmailVerified:        false,
					UserState:              domain.UserStateInactive,
				},
			},
		},
		{
			name: "user added deactivated and reactived",
			fields: fields{
				eventstore: expectEventstore(
					expectFilter(
						eventFromEventPusher(
							newRegisterHumanEvent("username", "$plain$x$password", true, true, "", language.English),
						),
						eventFromEventPusher(
							user.NewUserDeactivatedEvent(context.Background(),
								&userAgg.Aggregate,
							),
						),
						eventFromEventPusher(
							user.NewUserReactivatedEvent(context.Background(),
								&userAgg.Aggregate,
							),
						),
					),
				),
			},
			args: args{
				ctx:    context.Background(),
				userID: "user1",
			},
			res: res{
				want: &UserV2WriteModel{
					HumanWriteModel:   true,
					MachineWriteModel: true,
					StateWriteModel:   true,
					WriteModel: eventstore.WriteModel{
						AggregateID:       "user1",
						Events:            []eventstore.Event{},
						ProcessedSequence: 0,
						ResourceOwner:     "org1",
					},
					UserName:               "username",
					FirstName:              "firstname",
					LastName:               "lastname",
					DisplayName:            "firstname lastname",
					PreferredLanguage:      language.English,
					PasswordEncodedHash:    "$plain$x$password",
					PasswordChangeRequired: true,
					Email:                  "email@test.ch",
					IsEmailVerified:        false,
					UserState:              domain.UserStateActive,
				},
			},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			r := &Commands{
				eventstore: tt.fields.eventstore(t),
			}
			wm, err := r.userStateWriteModel(tt.args.ctx, tt.args.userID)
			if tt.res.err == nil {
				if !assert.NoError(t, err) {
					t.FailNow()
				}
			} else if !tt.res.err(err) {
				t.Errorf("got wrong err: %v ", err)
				return
			}
			if tt.res.err == nil {
				assert.Equal(t, tt.res.want, wm)
			}
		})
	}
}

func TestCommandSide_userHumanWriteModel_avatar(t *testing.T) {
	type fields struct {
		eventstore func(t *testing.T) *eventstore.Eventstore
	}
	type args struct {
		ctx    context.Context
		userID string
	}
	type res struct {
		want *UserV2WriteModel
		err  func(error) bool
	}

	userAgg := user.NewAggregate("user1", "org1")

	tests := []struct {
		name   string
		fields fields
		args   args
		res    res
	}{
		{
			name: "user added with avatar",
			fields: fields{
				eventstore: expectEventstore(
					expectFilter(
						eventFromEventPusher(
							newAddHumanEvent("$plain$x$password", true, true, "", language.English),
						),
						eventFromEventPusher(
							user.NewHumanAvatarAddedEvent(context.Background(),
								&userAgg.Aggregate,
								"key",
							),
						),
					),
				),
			},
			args: args{
				ctx:    context.Background(),
				userID: "user1",
			},
			res: res{
				want: &UserV2WriteModel{
					HumanWriteModel:  true,
					AvatarWriteModel: true,
					StateWriteModel:  true,
					WriteModel: eventstore.WriteModel{
						AggregateID:       "user1",
						Events:            []eventstore.Event{},
						ProcessedSequence: 0,
						ResourceOwner:     "org1",
					},
					UserName:               "username",
					FirstName:              "firstname",
					LastName:               "lastname",
					DisplayName:            "firstname lastname",
					PreferredLanguage:      language.English,
					PasswordEncodedHash:    "$plain$x$password",
					PasswordChangeRequired: true,
					Email:                  "email@test.ch",
					IsEmailVerified:        false,
					UserState:              domain.UserStateActive,
					Avatar:                 "key",
				},
			},
		},

		{
			name: "user added with avatar and then removed",
			fields: fields{
				eventstore: expectEventstore(
					expectFilter(
						eventFromEventPusher(
							newAddHumanEvent("$plain$x$password", true, true, "", language.English),
						),
						eventFromEventPusher(
							user.NewHumanAvatarAddedEvent(context.Background(),
								&userAgg.Aggregate,
								"key",
							),
						),
						eventFromEventPusher(
							user.NewHumanAvatarRemovedEvent(context.Background(),
								&userAgg.Aggregate,
								"key",
							),
						),
					),
				),
			},
			args: args{
				ctx:    context.Background(),
				userID: "user1",
			},
			res: res{
				want: &UserV2WriteModel{
					HumanWriteModel:  true,
					AvatarWriteModel: true,
					StateWriteModel:  true,
					WriteModel: eventstore.WriteModel{
						AggregateID:       "user1",
						Events:            []eventstore.Event{},
						ProcessedSequence: 0,
						ResourceOwner:     "org1",
					},
					UserName:               "username",
					FirstName:              "firstname",
					LastName:               "lastname",
					DisplayName:            "firstname lastname",
					PreferredLanguage:      language.English,
					PasswordEncodedHash:    "$plain$x$password",
					PasswordChangeRequired: true,
					Email:                  "email@test.ch",
					IsEmailVerified:        false,
					UserState:              domain.UserStateActive,
				},
			},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			r := &Commands{
				eventstore: tt.fields.eventstore(t),
			}
			wm, err := r.userHumanWriteModel(tt.args.ctx, tt.args.userID, false, false, false, false, true, false)
			if tt.res.err == nil {
				if !assert.NoError(t, err) {
					t.FailNow()
				}
			} else if !tt.res.err(err) {
				t.Errorf("got wrong err: %v ", err)
				return
			}
			if tt.res.err == nil {
				assert.Equal(t, tt.res.want, wm)
			}
		})
	}
}

func TestCommandSide_userHumanWriteModel_idpLinks(t *testing.T) {
	type fields struct {
		eventstore func(t *testing.T) *eventstore.Eventstore
	}
	type args struct {
		ctx    context.Context
		userID string
	}
	type res struct {
		want *UserV2WriteModel
		err  func(error) bool
	}

	userAgg := user.NewAggregate("user1", "org1")

	tests := []struct {
		name   string
		fields fields
		args   args
		res    res
	}{
		{
			name: "user added with idp link",
			fields: fields{
				eventstore: expectEventstore(
					expectFilter(
						eventFromEventPusher(
							newAddHumanEvent("$plain$x$password", true, true, "", language.English),
						),
						eventFromEventPusher(
							user.NewUserIDPLinkAddedEvent(context.Background(),
								&userAgg.Aggregate,
								"idp",
								"name",
								"externalID",
							),
						),
					),
				),
			},
			args: args{
				ctx:    context.Background(),
				userID: "user1",
			},
			res: res{
				want: &UserV2WriteModel{
					HumanWriteModel:   true,
					MachineWriteModel: true,
					StateWriteModel:   true,
					IDPLinkWriteModel: true,
					WriteModel: eventstore.WriteModel{
						AggregateID:       "user1",
						Events:            []eventstore.Event{},
						ProcessedSequence: 0,
						ResourceOwner:     "org1",
					},
					UserName:               "username",
					FirstName:              "firstname",
					LastName:               "lastname",
					DisplayName:            "firstname lastname",
					PreferredLanguage:      language.English,
					PasswordEncodedHash:    "$plain$x$password",
					PasswordChangeRequired: true,
					Email:                  "email@test.ch",
					IsEmailVerified:        false,
					UserState:              domain.UserStateActive,
					IDPLinks: []*domain.UserIDPLink{
						{IDPConfigID: "idp", DisplayName: "name", ExternalUserID: "externalID"},
					},
				},
			},
		},
		{
			name: "user added with idp links",
			fields: fields{
				eventstore: expectEventstore(
					expectFilter(
						eventFromEventPusher(
							newAddHumanEvent("$plain$x$password", true, true, "", language.English),
						),
						eventFromEventPusher(
							user.NewUserIDPLinkAddedEvent(context.Background(),
								&userAgg.Aggregate,
								"idp1",
								"name1",
								"externalID1",
							),
						),
						eventFromEventPusher(
							user.NewUserIDPLinkAddedEvent(context.Background(),
								&userAgg.Aggregate,
								"idp2",
								"name2",
								"externalID2",
							),
						),
						eventFromEventPusher(
							user.NewUserIDPLinkAddedEvent(context.Background(),
								&userAgg.Aggregate,
								"idp3",
								"name3",
								"externalID3",
							),
						),
					),
				),
			},
			args: args{
				ctx:    context.Background(),
				userID: "user1",
			},
			res: res{
				want: &UserV2WriteModel{
					HumanWriteModel:   true,
					MachineWriteModel: true,
					StateWriteModel:   true,
					IDPLinkWriteModel: true,
					WriteModel: eventstore.WriteModel{
						AggregateID:       "user1",
						Events:            []eventstore.Event{},
						ProcessedSequence: 0,
						ResourceOwner:     "org1",
					},
					UserName:               "username",
					FirstName:              "firstname",
					LastName:               "lastname",
					DisplayName:            "firstname lastname",
					PreferredLanguage:      language.English,
					PasswordEncodedHash:    "$plain$x$password",
					PasswordChangeRequired: true,
					Email:                  "email@test.ch",
					IsEmailVerified:        false,
					UserState:              domain.UserStateActive,
					IDPLinks: []*domain.UserIDPLink{
						{IDPConfigID: "idp1", DisplayName: "name1", ExternalUserID: "externalID1"},
						{IDPConfigID: "idp2", DisplayName: "name2", ExternalUserID: "externalID2"},
						{IDPConfigID: "idp3", DisplayName: "name3", ExternalUserID: "externalID3"},
					},
				},
			},
		},
		{
			name: "user added with idp links and removed",
			fields: fields{
				eventstore: expectEventstore(
					expectFilter(
						eventFromEventPusher(
							newAddHumanEvent("$plain$x$password", true, true, "", language.English),
						),
						eventFromEventPusher(
							user.NewUserIDPLinkAddedEvent(context.Background(),
								&userAgg.Aggregate,
								"idp1",
								"name1",
								"externalID1",
							),
						),
						eventFromEventPusher(
							user.NewUserIDPLinkAddedEvent(context.Background(),
								&userAgg.Aggregate,
								"idp2",
								"name2",
								"externalID2",
							),
						),
						eventFromEventPusher(
							user.NewUserIDPLinkAddedEvent(context.Background(),
								&userAgg.Aggregate,
								"idp3",
								"name3",
								"externalID3",
							),
						),
						eventFromEventPusher(
							user.NewUserIDPLinkCascadeRemovedEvent(context.Background(),
								&userAgg.Aggregate,
								"idp2",
								"externalID2",
							),
						),
						eventFromEventPusher(
							user.NewUserIDPLinkRemovedEvent(context.Background(),
								&userAgg.Aggregate,
								"idp3",
								"externalID3",
							),
						),
						eventFromEventPusher(
							user.NewUserIDPLinkAddedEvent(context.Background(),
								&userAgg.Aggregate,
								"idp4",
								"name4",
								"externalID4",
							),
						),
					),
				),
			},
			args: args{
				ctx:    context.Background(),
				userID: "user1",
			},
			res: res{
				want: &UserV2WriteModel{
					HumanWriteModel:   true,
					MachineWriteModel: true,
					StateWriteModel:   true,
					IDPLinkWriteModel: true,
					WriteModel: eventstore.WriteModel{
						AggregateID:       "user1",
						Events:            []eventstore.Event{},
						ProcessedSequence: 0,
						ResourceOwner:     "org1",
					},
					UserName:               "username",
					FirstName:              "firstname",
					LastName:               "lastname",
					DisplayName:            "firstname lastname",
					PreferredLanguage:      language.English,
					PasswordEncodedHash:    "$plain$x$password",
					PasswordChangeRequired: true,
					Email:                  "email@test.ch",
					IsEmailVerified:        false,
					UserState:              domain.UserStateActive,
					IDPLinks: []*domain.UserIDPLink{
						{IDPConfigID: "idp1", DisplayName: "name1", ExternalUserID: "externalID1"},
						{IDPConfigID: "idp4", DisplayName: "name4", ExternalUserID: "externalID4"},
					},
				},
			},
		},
		{
			name: "user added with idp link and removed",
			fields: fields{
				eventstore: expectEventstore(
					expectFilter(
						eventFromEventPusher(
							newAddHumanEvent("$plain$x$password", true, true, "", language.English),
						),
						eventFromEventPusher(
							user.NewUserIDPLinkAddedEvent(context.Background(),
								&userAgg.Aggregate,
								"idp",
								"name",
								"externalID",
							),
						),
						eventFromEventPusher(
							user.NewUserIDPLinkRemovedEvent(context.Background(),
								&userAgg.Aggregate,
								"idp",
								"externalID",
							),
						),
					),
				),
			},
			args: args{
				ctx:    context.Background(),
				userID: "user1",
			},
			res: res{
				want: &UserV2WriteModel{
					HumanWriteModel:   true,
					MachineWriteModel: true,
					StateWriteModel:   true,
					IDPLinkWriteModel: true,
					WriteModel: eventstore.WriteModel{
						AggregateID:       "user1",
						Events:            []eventstore.Event{},
						ProcessedSequence: 0,
						ResourceOwner:     "org1",
					},
					UserName:               "username",
					FirstName:              "firstname",
					LastName:               "lastname",
					DisplayName:            "firstname lastname",
					PreferredLanguage:      language.English,
					PasswordEncodedHash:    "$plain$x$password",
					PasswordChangeRequired: true,
					Email:                  "email@test.ch",
					IsEmailVerified:        false,
					UserState:              domain.UserStateActive,
					IDPLinks:               []*domain.UserIDPLink{},
				},
			},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			r := &Commands{
				eventstore: tt.fields.eventstore(t),
			}
			wm, err := r.userRemoveWriteModel(tt.args.ctx, tt.args.userID)
			if tt.res.err == nil {
				if !assert.NoError(t, err) {
					t.FailNow()
				}
			} else if !tt.res.err(err) {
				t.Errorf("got wrong err: %v ", err)
				return
			}
			if tt.res.err == nil {
				assert.Equal(t, tt.res.want, wm)
			}
		})
	}
}