package chat

import (
	"encoding/hex"
	"fmt"
	"sync"

	"golang.org/x/crypto/argon2"
)

// UserID uniquely identifies an individual user who has posted a message in a
// Room.
type UserID struct {

	// Name will be the user's chosen display name.
	Name string `json:"name"`

	// Hash will be a hex string generated from a secret only the user knows.
	Hash string `json:"id"`
}

// UserIDCalculator is used to calculate UserIDs.
type UserIDCalculator struct {

	// Secret is used when calculating UserID Hash salts.
	Secret []byte

	// TimeCost, MemoryCost, and Threads are used as inputs to the Argon2id
	// algorithm which is used to generate the Hash.
	TimeCost, MemoryCost uint32
	Threads              uint8

	// HashLen specifies the number of bytes the Hash should be.
	HashLen uint32

	// Lock, if set, forces concurrent Calculate calls to occur sequentially.
	Lock *sync.Mutex
}

// NewUserIDCalculator returns a UserIDCalculator with sane defaults.
func NewUserIDCalculator(secret []byte) *UserIDCalculator {
	return &UserIDCalculator{
		Secret:     secret,
		TimeCost:   15,
		MemoryCost: 128 * 1024,
		Threads:    2,
		HashLen:    16,
		Lock:       new(sync.Mutex),
	}
}

// Calculate accepts a name and password and returns the calculated UserID.
func (c *UserIDCalculator) Calculate(name, password string) UserID {

	input := fmt.Sprintf("%q:%q", name, password)

	hashB := argon2.IDKey(
		[]byte(input),
		c.Secret, // salt
		c.TimeCost, c.MemoryCost, c.Threads,
		c.HashLen,
	)

	return UserID{
		Name: name,
		Hash: hex.EncodeToString(hashB),
	}
}
