package entities

import (
	"github.com/DonovanKelly/sugar-n-spice/set"
	"github.com/google/uuid"
	"go-nkode/config"
	"go-nkode/internal/models"
	"go-nkode/internal/security"
	"go-nkode/internal/sqlc"
	"go-nkode/internal/utils"
)

type Customer struct {
	ID          models.CustomerID
	NKodePolicy models.NKodePolicy
	Attributes  CustomerAttributes
}

func NewCustomer(nkodePolicy models.NKodePolicy) (*Customer, error) {
	customerAttrs, err := NewCustomerAttributes()
	if err != nil {
		return nil, err
	}
	customer := Customer{
		ID:          models.CustomerID(uuid.New()),
		NKodePolicy: nkodePolicy,
		Attributes:  *customerAttrs,
	}

	return &customer, nil
}

func (c *Customer) IsValidNKode(kp KeypadDimension, passcodeAttrIdx []int) error {
	nkodeLen := len(passcodeAttrIdx)
	if nkodeLen < c.NKodePolicy.MinNkodeLen || nkodeLen > c.NKodePolicy.MaxNkodeLen {
		return config.ErrInvalidNKodeLength
	}

	if validIdx := kp.ValidateAttributeIndices(passcodeAttrIdx); !validIdx {
		return config.ErrInvalidNKodeIdx
	}
	passcodeSetVals := make(set.Set[uint64])
	passcodeAttrVals := make(set.Set[uint64])
	attrVals, err := c.Attributes.AttrValsForKp(kp)
	if err != nil {
		return err
	}
	for idx := 0; idx < nkodeLen; idx++ {
		attrVal := attrVals[passcodeAttrIdx[idx]]
		setVal, err := c.Attributes.GetAttrSetVal(attrVal, kp)
		if err != nil {
			return err
		}
		passcodeSetVals.Add(setVal)
		passcodeAttrVals.Add(attrVal)
	}

	if passcodeSetVals.Size() < c.NKodePolicy.DistinctSets {
		return config.ErrTooFewDistinctSet
	}

	if passcodeAttrVals.Size() < c.NKodePolicy.DistinctAttributes {
		return config.ErrTooFewDistinctAttributes
	}
	return nil
}

func (c *Customer) RenewKeys() ([]uint64, []uint64, error) {
	oldAttrs := make([]uint64, len(c.Attributes.AttrVals))
	oldSets := make([]uint64, len(c.Attributes.SetVals))

	copy(oldAttrs, c.Attributes.AttrVals)
	copy(oldSets, c.Attributes.SetVals)

	if err := c.Attributes.Renew(); err != nil {
		return nil, nil, err
	}
	attrsXor, err := security.XorLists(oldAttrs, c.Attributes.AttrVals)
	if err != nil {
		return nil, nil, err
	}
	setXor, err := security.XorLists(oldSets, c.Attributes.SetVals)
	if err != nil {
		return nil, nil, err
	}
	return setXor, attrsXor, nil
}

func (c *Customer) ToSqlcCreateCustomerParams() sqlc.CreateCustomerParams {
	return sqlc.CreateCustomerParams{
		ID:                 uuid.UUID(c.ID).String(),
		MaxNkodeLen:        int64(c.NKodePolicy.MaxNkodeLen),
		MinNkodeLen:        int64(c.NKodePolicy.MinNkodeLen),
		DistinctSets:       int64(c.NKodePolicy.DistinctSets),
		DistinctAttributes: int64(c.NKodePolicy.DistinctAttributes),
		LockOut:            int64(c.NKodePolicy.LockOut),
		Expiration:         int64(c.NKodePolicy.Expiration),
		AttributeValues:    c.Attributes.AttrBytes(),
		SetValues:          c.Attributes.SetBytes(),
		LastRenew:          utils.TimeStamp(),
		CreatedAt:          utils.TimeStamp(),
	}
}
