package model

import (
	"fmt"
	"strings"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestUserInfo_SetDefaultMethod_ShouldConfigureConfigDefault(t *testing.T) {
	none := "none"

	testName := func(i int, have UserInfo, availableMethods []string) string {
		method := have.Method

		if method == "" {
			method = none
		}

		has := ""

		if have.HasTOTP || have.HasDuo || have.HasWebauthn {
			has += " has"

			if have.HasTOTP {
				has += " " + SecondFactorMethodTOTP
			}

			if have.HasDuo {
				has += " " + SecondFactorMethodDuo
			}

			if have.HasWebauthn {
				has += " " + SecondFactorMethodWebauthn
			}
		}

		available := none
		if len(availableMethods) != 0 {
			available = strings.Join(availableMethods, " ")
		}

		return fmt.Sprintf("%d/method %s%s/available methods %s", i+1, method, has, available)
	}

	testCases := []struct {
		have             UserInfo
		availableMethods []string
		changed          bool
		want             UserInfo
	}{
		{
			have: UserInfo{
				Method:      SecondFactorMethodTOTP,
				HasDuo:      true,
				HasTOTP:     true,
				HasWebauthn: true,
			},
			availableMethods: []string{SecondFactorMethodWebauthn, SecondFactorMethodDuo},
			changed:          true,
			want: UserInfo{
				Method:      SecondFactorMethodWebauthn,
				HasDuo:      true,
				HasTOTP:     true,
				HasWebauthn: true,
			},
		},
		{
			have: UserInfo{
				HasDuo:      true,
				HasTOTP:     true,
				HasWebauthn: true,
			},
			availableMethods: []string{SecondFactorMethodTOTP, SecondFactorMethodWebauthn, SecondFactorMethodDuo},
			changed:          true,
			want: UserInfo{
				Method:      SecondFactorMethodTOTP,
				HasDuo:      true,
				HasTOTP:     true,
				HasWebauthn: true,
			},
		},
		{
			have: UserInfo{
				Method:      SecondFactorMethodWebauthn,
				HasDuo:      true,
				HasTOTP:     false,
				HasWebauthn: false,
			},
			availableMethods: []string{SecondFactorMethodTOTP},
			changed:          true,
			want: UserInfo{
				Method:      SecondFactorMethodTOTP,
				HasDuo:      true,
				HasTOTP:     false,
				HasWebauthn: false,
			},
		},
		{
			have: UserInfo{
				Method:      SecondFactorMethodWebauthn,
				HasDuo:      false,
				HasTOTP:     false,
				HasWebauthn: false,
			},
			availableMethods: []string{SecondFactorMethodTOTP},
			changed:          true,
			want: UserInfo{
				Method:      SecondFactorMethodTOTP,
				HasDuo:      false,
				HasTOTP:     false,
				HasWebauthn: false,
			},
		},
		{
			have: UserInfo{
				Method:      SecondFactorMethodTOTP,
				HasDuo:      false,
				HasTOTP:     false,
				HasWebauthn: false,
			},
			availableMethods: []string{SecondFactorMethodWebauthn},
			changed:          true,
			want: UserInfo{
				Method:      SecondFactorMethodWebauthn,
				HasDuo:      false,
				HasTOTP:     false,
				HasWebauthn: false,
			},
		},
		{
			have: UserInfo{
				Method:      SecondFactorMethodTOTP,
				HasDuo:      false,
				HasTOTP:     false,
				HasWebauthn: false,
			},
			availableMethods: []string{SecondFactorMethodDuo},
			changed:          true,
			want: UserInfo{
				Method:      SecondFactorMethodDuo,
				HasDuo:      false,
				HasTOTP:     false,
				HasWebauthn: false,
			},
		},
		{
			have: UserInfo{
				Method:      SecondFactorMethodWebauthn,
				HasDuo:      false,
				HasTOTP:     true,
				HasWebauthn: true,
			},
			availableMethods: []string{SecondFactorMethodTOTP, SecondFactorMethodWebauthn, SecondFactorMethodDuo},
			changed:          false,
			want: UserInfo{
				Method:      SecondFactorMethodWebauthn,
				HasDuo:      false,
				HasTOTP:     true,
				HasWebauthn: true,
			},
		},
		{
			have: UserInfo{
				Method:      "",
				HasDuo:      false,
				HasTOTP:     true,
				HasWebauthn: true,
			},
			availableMethods: []string{SecondFactorMethodWebauthn, SecondFactorMethodDuo},
			changed:          true,
			want: UserInfo{
				Method:      SecondFactorMethodWebauthn,
				HasDuo:      false,
				HasTOTP:     true,
				HasWebauthn: true,
			},
		},
		{
			have: UserInfo{
				Method:      "",
				HasDuo:      false,
				HasTOTP:     true,
				HasWebauthn: true,
			},
			availableMethods: []string{SecondFactorMethodDuo},
			changed:          true,
			want: UserInfo{
				Method:      SecondFactorMethodDuo,
				HasDuo:      false,
				HasTOTP:     true,
				HasWebauthn: true,
			},
		},
		{
			have: UserInfo{
				Method:      "",
				HasDuo:      false,
				HasTOTP:     true,
				HasWebauthn: true,
			},
			availableMethods: nil,
			changed:          false,
			want: UserInfo{
				Method:      "",
				HasDuo:      false,
				HasTOTP:     true,
				HasWebauthn: true,
			},
		},
	}

	for i, tc := range testCases {
		t.Run(testName(i, tc.have, tc.availableMethods), func(t *testing.T) {
			changed := tc.have.SetDefaultPreferred2FAMethod(tc.availableMethods)

			assert.Equal(t, tc.changed, changed)
			assert.Equal(t, tc.want, tc.have)
		})
	}
}