package crypto

import (
	"crypto/rand"
	"errors"
	"io"
	"slices"
)

type Nonce struct {
	val []byte
}

func NewNonce(size int) *Nonce {
	return &Nonce{val: make([]byte, size)}
}

// NewRandomNonce generates a new initial nonce with the lower byte set to a random value
// This ensures there are plenty of nonce values available before rolling over
// Based on ideas from the Secure Programming Cookbook for C and C++ by John Viega, Matt Messier
// https://www.oreilly.com/library/view/secure-programming-cookbook/0596003943/ch04s09.html
func NewRandomNonce(size int) (*Nonce, error) {
	randomBytes := 1
	if size <= randomBytes {
		return nil, errors.New("nonce size must be greater than the number of random bytes")
	}

	randomPart := make([]byte, randomBytes)
	if _, err := rand.Read(randomPart); err != nil {
		return nil, err
	}

	zeroPart := make([]byte, size-randomBytes)
	nonceVal := append(randomPart, zeroPart...)
	return &Nonce{val: nonceVal}, nil
}

func (n *Nonce) Read(stream io.Reader) error {
	_, err := io.ReadFull(stream, n.val)
	return err
}

func (n *Nonce) Value() []byte {
	return n.val
}

func (n *Nonce) Increment() error {
	// Start incrementing from the least significant byte
	for i := range slices.Backward(n.val) {
		// Increment the current byte
		n.val[i]++

		// Check for overflow
		if n.val[i] != 0 {
			// No overflow, nonce is successfully incremented
			return nil
		}
	}

	// If we reach here, it means the nonce has overflowed
	return errors.New("nonce overflow")
}
