package model

import (
	"errors"
	"fmt"
	"github.com/google/uuid"
	"go-nkode/hashset"
	py "go-nkode/py-builtin"
	"go-nkode/util"
)

type Customer struct {
	Id          CustomerId
	NKodePolicy NKodePolicy
	Attributes  CustomerAttributes
}

func NewCustomer(nkodePolicy NKodePolicy) (*Customer, error) {
	customerAttrs, err := NewCustomerAttributes()
	if err != nil {
		return nil, err
	}
	customer := Customer{
		Id:          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 {
		return errors.New(fmt.Sprintf("NKode length %d is too short. Minimum nKode length is %d", nkodeLen, c.NKodePolicy.MinNkodeLen))
	}

	validIdx := py.All[int](passcodeAttrIdx, func(i int) bool {
		return i >= 0 && i < kp.TotalAttrs()
	})

	if !validIdx {
		return errors.New(fmt.Sprintf("One or more idx out of range 0-%d in IsValidNKode", kp.TotalAttrs()-1))
	}
	passcodeSetVals := make(hashset.Set[uint64])
	passcodeAttrVals := make(hashset.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 errors.New(fmt.Sprintf("passcode has two few distinct sets min %d, has %d", c.NKodePolicy.DistinctSets, passcodeSetVals.Size()))
	}

	if passcodeAttrVals.Size() < c.NKodePolicy.DistinctAttributes {
		return errors.New(fmt.Sprintf("passcode has two few distinct attributes min %d, has %d", c.NKodePolicy.DistinctAttributes, passcodeAttrVals.Size()))
	}
	return nil
}

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

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

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