package command

import (
	"context"
	"io"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"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/org"
	"github.com/zitadel/zitadel/internal/repository/user"
	"github.com/zitadel/zitadel/internal/zerrors"
)

func TestSessionCommands_getHumanWebAuthNTokens(t *testing.T) {
	userAggr := &user.NewAggregate("user1", "org1").Aggregate

	type fields struct {
		eventstore        *eventstore.Eventstore
		sessionWriteModel *SessionWriteModel
	}
	type args struct {
		userVerification domain.UserVerificationRequirement
	}
	type res struct {
		want *humanWebAuthNTokens
		err  error
	}
	tests := []struct {
		name   string
		fields fields
		args   args
		res    res
	}{
		{
			name: "missing UID",
			fields: fields{
				eventstore:        &eventstore.Eventstore{},
				sessionWriteModel: &SessionWriteModel{},
			},
			args: args{
				domain.UserVerificationRequirementDiscouraged,
			},
			res: res{
				want: nil,
				err:  zerrors.ThrowPreconditionFailed(nil, "COMMAND-eeR2e", "Errors.User.UserIDMissing"),
			},
		},
		{
			name: "passwordless filter error",
			fields: fields{
				eventstore: eventstoreExpect(t,
					expectFilter(
						eventFromEventPusher(
							user.NewHumanAddedEvent(context.Background(),
								userAggr,
								"", "", "", "", "", language.Georgian,
								domain.GenderDiverse, "", true,
							),
						),
					),
					expectFilterError(io.ErrClosedPipe),
				),
				sessionWriteModel: &SessionWriteModel{
					UserID: "user1",
				},
			},
			args: args{
				domain.UserVerificationRequirementDiscouraged,
			},
			res: res{
				want: nil,
				err:  io.ErrClosedPipe,
			},
		},
		{
			name: "ok, discouraged, u2f",
			fields: fields{
				eventstore: eventstoreExpect(t,
					expectFilter(
						eventFromEventPusher(
							user.NewHumanAddedEvent(context.Background(),
								userAggr,
								"", "", "", "", "", language.Georgian,
								domain.GenderDiverse, "", true,
							),
						),
					),
					expectFilter(eventFromEventPusher(
						user.NewHumanWebAuthNAddedEvent(eventstore.NewBaseEventForPush(
							context.Background(), &org.NewAggregate("org1").Aggregate, user.HumanU2FTokenAddedType,
						), "111", "challenge", "rpID"),
					)),
				),
				sessionWriteModel: &SessionWriteModel{
					UserID: "user1",
				},
			},
			args: args{
				domain.UserVerificationRequirementDiscouraged,
			},
			res: res{
				want: &humanWebAuthNTokens{
					human: &domain.Human{
						ObjectRoot: models.ObjectRoot{
							AggregateID:   "user1",
							ResourceOwner: "org1",
						},
						State: domain.UserStateActive,
						Profile: &domain.Profile{
							PreferredLanguage: language.Georgian,
							Gender:            domain.GenderDiverse,
						},
						Email: &domain.Email{},
					},
					tokens: []*domain.WebAuthNToken{{
						ObjectRoot: models.ObjectRoot{
							AggregateID: "org1",
						},
						WebAuthNTokenID: "111",
						State:           domain.MFAStateNotReady,
						Challenge:       "challenge",
						RPID:            "rpID",
					}},
				},
				err: nil,
			},
		},
		{
			name: "ok, preferred, u2f",
			fields: fields{
				eventstore: eventstoreExpect(t,
					expectFilter(
						eventFromEventPusher(
							user.NewHumanAddedEvent(context.Background(),
								userAggr,
								"", "", "", "", "", language.Georgian,
								domain.GenderDiverse, "", true,
							),
						),
					),
					expectFilter(eventFromEventPusher(
						user.NewHumanWebAuthNAddedEvent(eventstore.NewBaseEventForPush(
							context.Background(), &org.NewAggregate("org1").Aggregate, user.HumanU2FTokenAddedType,
						), "111", "challenge", "rpID"),
					)),
				),
				sessionWriteModel: &SessionWriteModel{
					UserID: "user1",
				},
			},
			args: args{
				domain.UserVerificationRequirementPreferred,
			},
			res: res{
				want: &humanWebAuthNTokens{
					human: &domain.Human{
						ObjectRoot: models.ObjectRoot{
							AggregateID:   "user1",
							ResourceOwner: "org1",
						},
						State: domain.UserStateActive,
						Profile: &domain.Profile{
							PreferredLanguage: language.Georgian,
							Gender:            domain.GenderDiverse,
						},
						Email: &domain.Email{},
					},
					tokens: []*domain.WebAuthNToken{{
						ObjectRoot: models.ObjectRoot{
							AggregateID: "org1",
						},
						WebAuthNTokenID: "111",
						State:           domain.MFAStateNotReady,
						Challenge:       "challenge",
						RPID:            "rpID",
					}},
				},
				err: nil,
			},
		},
		{
			name: "ok, required, u2f",
			fields: fields{
				eventstore: eventstoreExpect(t,
					expectFilter(
						eventFromEventPusher(
							user.NewHumanAddedEvent(context.Background(),
								userAggr,
								"", "", "", "", "", language.Georgian,
								domain.GenderDiverse, "", true,
							),
						),
					),
					expectFilter(eventFromEventPusher(
						user.NewHumanWebAuthNAddedEvent(eventstore.NewBaseEventForPush(
							context.Background(), &org.NewAggregate("org1").Aggregate, user.HumanPasswordlessTokenAddedType,
						), "111", "challenge", "rpID"),
					)),
				),
				sessionWriteModel: &SessionWriteModel{
					UserID: "user1",
				},
			},
			args: args{
				domain.UserVerificationRequirementRequired,
			},
			res: res{
				want: &humanWebAuthNTokens{
					human: &domain.Human{
						ObjectRoot: models.ObjectRoot{
							AggregateID:   "user1",
							ResourceOwner: "org1",
						},
						State: domain.UserStateActive,
						Profile: &domain.Profile{
							PreferredLanguage: language.Georgian,
							Gender:            domain.GenderDiverse,
						},
						Email: &domain.Email{},
					},
					tokens: []*domain.WebAuthNToken{{
						ObjectRoot: models.ObjectRoot{
							AggregateID: "org1",
						},
						WebAuthNTokenID: "111",
						State:           domain.MFAStateNotReady,
						Challenge:       "challenge",
						RPID:            "rpID",
					}},
				},
				err: nil,
			},
		},
	}
	for _, tt := range tests {
		s := &SessionCommands{
			eventstore:        tt.fields.eventstore,
			sessionWriteModel: tt.fields.sessionWriteModel,
		}
		got, err := s.getHumanWebAuthNTokens(context.Background(), tt.args.userVerification)
		require.ErrorIs(t, err, tt.res.err)
		assert.Equal(t, tt.res.want, got)
	}
}