package nkode

import (
	"errors"
	"github.com/google/uuid"
	m "go-nkode/core/model"
	py "go-nkode/py-builtin"
)

var KeyIndexOutOfRange error = errors.New("one or more keys is out of range")

func ValidKeyEntry(user m.User, customer m.Customer, selectedKeys []int) ([]int, error) {
	validKeys := py.All[int](selectedKeys, func(idx int) bool {
		return 0 <= idx && idx < user.Kp.NumbOfKeys
	})
	if !validKeys {
		panic(KeyIndexOutOfRange)
	}

	var err error
	passcodeLen := len(selectedKeys)
	err = customer.NKodePolicy.ValidLength(passcodeLen)
	if err != nil {
		return nil, err
	}

	setVals, err := customer.Attributes.SetValsForKp(user.Kp)
	if err != nil {
		return nil, err
	}

	passcodeSetVals, err := user.DecipherMask(setVals, passcodeLen)
	if err != nil {
		return nil, err
	}
	presumedAttrIdxVals := make([]int, passcodeLen)

	for idx := range presumedAttrIdxVals {
		keyNumb := selectedKeys[idx]
		setIdx, err := customer.Attributes.IndexOfSet(passcodeSetVals[idx])
		if err != nil {
			return nil, err
		}
		selectedAttrIdx, err := user.Interface.GetAttrIdxByKeyNumbSetIdx(setIdx, keyNumb)
		if err != nil {
			return nil, err
		}
		presumedAttrIdxVals[idx] = selectedAttrIdx
	}
	err = customer.IsValidNKode(user.Kp, presumedAttrIdxVals)
	if err != nil {
		panic(err)
	}
	attrVals, err := customer.Attributes.AttrValsForKp(user.Kp)
	if err != nil {
		panic(err)
	}
	err = user.UserKeys.ValidPassword(user.EncipheredPasscode.Code, presumedAttrIdxVals, attrVals)
	if err != nil {
		return nil, err
	}

	return presumedAttrIdxVals, nil
}

func NewUser(customer m.Customer, username m.Username, passcodeIdx []int, ui m.UserInterface, kp m.KeypadDimension) (*m.User, error) {
	setVals, err := customer.Attributes.SetValsForKp(kp)
	if err != nil {
		return nil, err
	}
	newKeys, err := m.NewUserCipherKeys(&kp, setVals, customer.NKodePolicy.MaxNkodeLen)
	if err != nil {
		return nil, err
	}
	encipheredNKode, err := newKeys.EncipherNKode(passcodeIdx, customer.Attributes)
	if err != nil {
		return nil, err
	}
	newUser := m.User{
		Id:                 m.UserId(uuid.New()),
		Username:           username,
		EncipheredPasscode: *encipheredNKode,
		UserKeys:           *newKeys,
		Interface:          ui,
		Kp:                 kp,
		CustomerId:         customer.Id,
	}
	return &newUser, nil
}
