package entities

import (
	"github.com/google/uuid"
	"go-nkode/config"
	"go-nkode/internal/models"
	"go-nkode/internal/security"
	"log"
)

type User struct {
	Id                 models.UserId
	CustomerId         models.CustomerID
	Email              models.UserEmail
	EncipheredPasscode models.EncipheredNKode
	Kp                 KeypadDimension
	CipherKeys         UserCipherKeys
	Interface          UserInterface
	Renew              bool
	RefreshToken       string
}

func (u *User) DecipherMask(setVals []uint64, passcodeLen int) ([]uint64, error) {
	return u.CipherKeys.DecipherMask(u.EncipheredPasscode.Mask, setVals, passcodeLen)
}

func (u *User) RenewKeys(setXor []uint64, attrXor []uint64) error {
	u.Renew = true
	var err error
	u.CipherKeys.SetKey, err = security.XorLists(setXor[:u.Kp.AttrsPerKey], u.CipherKeys.SetKey)
	if err != nil {
		return err
	}
	u.CipherKeys.AlphaKey, err = security.XorLists(attrXor[:u.Kp.TotalAttrs()], u.CipherKeys.AlphaKey)
	return err
}

func (u *User) RefreshPasscode(passcodeAttrIdx []int, customerAttributes CustomerAttributes) error {
	setVals, err := customerAttributes.SetValsForKp(u.Kp)
	if err != nil {
		return err
	}
	newKeys, err := NewUserCipherKeys(&u.Kp, setVals, u.CipherKeys.MaxNKodeLen)
	if err != nil {
		return err
	}
	encipheredPasscode, err := newKeys.EncipherNKode(passcodeAttrIdx, customerAttributes)
	if err != nil {
		return err
	}

	u.CipherKeys = *newKeys
	u.EncipheredPasscode = *encipheredPasscode
	u.Renew = false
	return nil
}

func (u *User) GetLoginInterface() ([]int, error) {
	return u.Interface.IdxInterface, nil
}

func ValidKeyEntry(user User, customer Customer, selectedKeys []int) ([]int, error) {
	if validKeys := user.Kp.ValidKeySelections(selectedKeys); !validKeys {

		return nil, config.ErrKeyIndexOutOfRange
	}

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

	setVals, err := customer.Attributes.SetValsForKp(user.Kp)
	if err != nil {
		log.Printf("fatal error in validate key entry;invalid user keypad dimensions for user %s with error %v", user.Email, err)
		return nil, config.ErrInternalValidKeyEntry
	}

	passcodeSetVals, err := user.DecipherMask(setVals, passcodeLen)
	if err != nil {
		log.Printf("fatal error in validate key entry;something when wrong deciphering mask;user email %s; error %v", user.Email, err)
		return nil, config.ErrInternalValidKeyEntry
	}
	presumedAttrIdxVals := make([]int, passcodeLen)

	for idx := range presumedAttrIdxVals {
		keyNumb := selectedKeys[idx]
		setIdx, err := customer.Attributes.IndexOfSet(passcodeSetVals[idx])
		if err != nil {
			log.Printf("fatal error in validate key entry;something when wrong getting the IndexOfSet;user email %s; error %v", user.Email, err)
			return nil, config.ErrInternalValidKeyEntry
		}
		selectedAttrIdx, err := user.Interface.GetAttrIdxByKeyNumbSetIdx(setIdx, keyNumb)
		if err != nil {
			log.Printf("fatal error in validate key entry;something when wrong getting the GetAttrIdxByKeyNumbSetIdx;user email %s; error %v", user.Email, err)
			return nil, config.ErrInternalValidKeyEntry
		}
		presumedAttrIdxVals[idx] = selectedAttrIdx
	}
	err = customer.IsValidNKode(user.Kp, presumedAttrIdxVals)
	if err != nil {
		return nil, err
	}
	attrVals, err := customer.Attributes.AttrValsForKp(user.Kp)
	if err != nil {
		return nil, err
	}
	err = user.CipherKeys.ValidPassword(user.EncipheredPasscode.Code, presumedAttrIdxVals, attrVals)
	if err != nil {
		return nil, err
	}

	return presumedAttrIdxVals, nil
}

func NewUser(customer Customer, userEmail string, passcodeIdx []int, ui UserInterface, kp KeypadDimension) (*User, error) {
	_, err := models.ParseEmail(userEmail)
	if err != nil {
		return nil, err
	}
	setVals, err := customer.Attributes.SetValsForKp(kp)
	if err != nil {
		return nil, err
	}
	newKeys, err := 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 := User{
		Id:                 models.UserId(uuid.New()),
		Email:              models.UserEmail(userEmail),
		EncipheredPasscode: *encipheredNKode,
		CipherKeys:         *newKeys,
		Interface:          ui,
		Kp:                 kp,
		CustomerId:         customer.ID,
	}
	return &newUser, nil
}
