package nkode

import (
	"errors"
	"fmt"
	m "go-nkode/core/model"
)

type NKodeAPI struct {
	Db             DbAccessor
	SignupSessions map[m.SessionId]UserSignSession
}

func NewNKodeAPI(db DbAccessor) NKodeAPI {
	return NKodeAPI{
		Db:             db,
		SignupSessions: make(map[m.SessionId]UserSignSession),
	}
}

func (n *NKodeAPI) CreateNewCustomer(nkodePolicy m.NKodePolicy, id *m.CustomerId) (*m.CustomerId, error) {
	newCustomer, err := m.NewCustomer(nkodePolicy)
	if id != nil {
		newCustomer.Id = *id
	}
	if err != nil {
		return nil, err
	}
	err = n.Db.WriteNewCustomer(*newCustomer)

	if err != nil {
		return nil, err
	}
	return &newCustomer.Id, nil
}

func (n *NKodeAPI) GenerateSignupInterface(username m.Username, customerId m.CustomerId, kp m.KeypadDimension) (*m.GenerateSignupInterfaceResp, error) {
	signupSession, err := NewSignupSession(username, kp, customerId)
	if err != nil {
		return nil, err
	}
	n.SignupSessions[signupSession.Id] = *signupSession
	resp := m.GenerateSignupInterfaceResp{
		UserInterface: signupSession.SetIdxInterface,
		SessionId:     signupSession.Id,
	}
	return &resp, nil
}

func (n *NKodeAPI) SetNKode(customerId m.CustomerId, sessionId m.SessionId, keySelection m.KeySelection) (m.IdxInterface, error) {
	_, err := n.Db.GetCustomer(customerId)

	if err != nil {
		return nil, err
	}
	session, exists := n.SignupSessions[sessionId]
	if !exists {
		return nil, errors.New(fmt.Sprintf("session id does not exist %s", sessionId))
	}
	confirmInterface, err := session.SetUserNKode(keySelection)
	if err != nil {
		return nil, err
	}
	n.SignupSessions[sessionId] = session
	return confirmInterface, nil
}

func (n *NKodeAPI) ConfirmNKode(customerId m.CustomerId, sessionId m.SessionId, keySelection m.KeySelection) error {
	session, exists := n.SignupSessions[sessionId]
	if !exists {
		return errors.New(fmt.Sprintf("session id does not exist %s", sessionId))
	}
	customer, err := n.Db.GetCustomer(customerId)
	if err != nil {
		return err
	}
	passcode, err := session.DeducePasscode(keySelection)
	if err != nil {
		return err
	}
	err = customer.IsValidNKode(session.Kp, passcode)
	if err != nil {
		return err
	}
	user, err := NewUser(*customer, session.Username, passcode, session.LoginUserInterface, session.Kp)
	if err != nil {
		return err
	}
	delete(n.SignupSessions, session.Id)
	err = n.Db.WriteNewUser(*user)
	return err
}

func (n *NKodeAPI) GetLoginInterface(username m.Username, customerId m.CustomerId) (m.IdxInterface, error) {
	user, err := n.Db.GetUser(username, customerId)
	if err != nil {
		return nil, err
	}
	err = user.Interface.PartialInterfaceShuffle()
	if err != nil {
		return nil, err
	}
	err = n.Db.UpdateUserInterface(user.Id, user.Interface)
	if err != nil {
		return nil, err
	}
	return user.Interface.IdxInterface, nil
}

func (n *NKodeAPI) Login(customerId m.CustomerId, username m.Username, keySelection m.KeySelection) error {
	customer, err := n.Db.GetCustomer(customerId)
	if err != nil {
		return err
	}
	user, err := n.Db.GetUser(username, customerId)
	if err != nil {
		return errors.New(fmt.Sprintf("user dne %s", username))
	}
	passcode, err := ValidKeyEntry(*user, *customer, keySelection)
	if err != nil {
		return err
	}
	if user.Renew {
		err = n.Db.RefreshUser(*user, passcode, customer.Attributes)
		if err != nil {
			return err
		}
	}
	return nil
}

func (n *NKodeAPI) RenewAttributes(customerId m.CustomerId) error {
	return n.Db.Renew(customerId)
}
