package entities

import (
	"github.com/DonovanKelly/sugar-n-spice/all"
	"github.com/stretchr/testify/assert"
	"go-nkode/internal/models"
	"testing"
)

func TestUserCipherKeys_EncipherSaltHashCode(t *testing.T) {
	kp := KeypadDimension{AttrsPerKey: 10, NumbOfKeys: 8}
	maxNKodeLen := 10
	customerAttrs, err := NewCustomerAttributes()
	assert.NoError(t, err)
	setVals, err := customerAttrs.SetValsForKp(kp)
	assert.NoError(t, err)
	attrVals, err := customerAttrs.AttrValsForKp(kp)
	assert.NoError(t, err)
	newUser, err := NewUserCipherKeys(&kp, setVals, maxNKodeLen)
	assert.NoError(t, err)
	passcodeIdx := []int{0, 1, 2, 3}
	encipher0, err := newUser.EncipherSaltHashCode(passcodeIdx, attrVals)
	assert.NoError(t, err)
	err = newUser.ValidPassword(encipher0, passcodeIdx, attrVals)
	assert.NoError(t, err)

	passcodeIdxInvalid := []int{1, 0, 3, 2}
	assert.NoError(t, err)
	err = newUser.ValidPassword(encipher0, passcodeIdxInvalid, attrVals)
	assert.Error(t, err)
}

func TestUserCipherKeys_EncipherDecipherMask(t *testing.T) {
	kp := KeypadDimension{AttrsPerKey: 10, NumbOfKeys: 8}
	maxNKodeLen := 10

	customerAttrs, err := NewCustomerAttributes()
	assert.NoError(t, err)
	setVals, err := customerAttrs.SetValsForKp(kp)
	assert.NoError(t, err)
	attrVals, err := customerAttrs.AttrValsForKp(kp)
	assert.NoError(t, err)
	newUser, err := NewUserCipherKeys(&kp, setVals, maxNKodeLen)
	assert.NoError(t, err)
	passcodeIdx := []int{0, 1, 2, 3}
	originalSetVals := make([]uint64, len(passcodeIdx))

	for idx, val := range passcodeIdx {
		attr := attrVals[val]
		originalSetVals[idx], err = customerAttrs.GetAttrSetVal(attr, kp)
		assert.NoError(t, err)
	}
	encipheredCode, err := newUser.EncipherNKode(passcodeIdx, *customerAttrs)
	assert.NoError(t, err)
	passcodeSetVals, err := newUser.DecipherMask(encipheredCode.Mask, setVals, len(passcodeIdx))
	assert.NoError(t, err)

	for idx, setVal := range passcodeSetVals {
		assert.Equal(t, setVal, originalSetVals[idx])
	}
}

func TestUserInterface_RandomShuffle(t *testing.T) {
	kp := KeypadDimension{
		AttrsPerKey: 10,
		NumbOfKeys:  8,
	}
	mockSvgInterface := make(models.SvgIdInterface, kp.TotalAttrs())
	userInterface, err := NewUserInterface(&kp, mockSvgInterface)
	assert.NoError(t, err)
	userInterfaceCopy := make([]int, len(userInterface.IdxInterface))
	copy(userInterfaceCopy, userInterface.IdxInterface)

	err = userInterface.RandomShuffle()
	assert.NoError(t, err)

	assert.Equal(t, len(userInterface.IdxInterface), len(userInterfaceCopy))
	equalCount := 0
	for idx, val := range userInterface.IdxInterface {
		if val == userInterfaceCopy[idx] {
			equalCount++
		}
	}
	assert.NotEqual(t, equalCount, len(userInterface.IdxInterface))
}

func TestUserInterface_DisperseInterface(t *testing.T) {

	for idx := 0; idx < 10000; idx++ {
		kp := KeypadDimension{AttrsPerKey: 7, NumbOfKeys: 10}
		mockSvgInterface := make(models.SvgIdInterface, kp.TotalAttrs())
		userInterface, err := NewUserInterface(&kp, mockSvgInterface)
		assert.NoError(t, err)
		preDispersion, err := userInterface.AttributeAdjacencyGraph()
		assert.NoError(t, err)
		err = userInterface.DisperseInterface()
		assert.NoError(t, err)
		postDispersion, err := userInterface.AttributeAdjacencyGraph()
		assert.Equal(t, len(postDispersion), len(preDispersion))
		for attr, adjAttrs := range preDispersion {
			postAdjAttrs := postDispersion[attr]
			assert.Equal(t, adjAttrs.Size(), postAdjAttrs.Size())
			assert.True(t, adjAttrs.IsDisjoint(postAdjAttrs))
		}
	}
}

func TestUserInterface_PartialInterfaceShuffle(t *testing.T) {
	kp := KeypadDimension{AttrsPerKey: 7, NumbOfKeys: 10}
	mockSvgInterface := make(models.SvgIdInterface, kp.TotalAttrs())
	userInterface, err := NewUserInterface(&kp, mockSvgInterface)
	assert.NoError(t, err)
	preShuffle := userInterface.IdxInterface
	err = userInterface.LoginShuffle()
	assert.NoError(t, err)
	postShuffle := userInterface.IdxInterface

	shuffleCompare := make([]bool, len(postShuffle))
	for idx, val := range preShuffle {
		shuffleCompare[idx] = val == postShuffle[idx]
	}

	allTrue := all.All[bool](shuffleCompare, func(n bool) bool {
		return n == true
	})
	assert.False(t, allTrue)

	allFalse := all.All[bool](shuffleCompare, func(n bool) bool {
		return n == false
	})

	assert.False(t, allFalse)

}
