package acme

import (
	"encoding/base64"
	"sync"
	"testing"
)

func TestNonceIssueAndConsume(t *testing.T) {
	store := NewNonceStore()
	nonce, err := store.Issue()
	if err != nil {
		t.Fatalf("Issue() error: %v", err)
	}
	if err := store.Consume(nonce); err != nil {
		t.Fatalf("Consume() error: %v", err)
	}
}

func TestNonceRejectUnknown(t *testing.T) {
	store := NewNonceStore()
	err := store.Consume("never-issued-nonce")
	if err == nil {
		t.Fatalf("expected error consuming unknown nonce, got nil")
	}
}

func TestNonceRejectReuse(t *testing.T) {
	store := NewNonceStore()
	nonce, err := store.Issue()
	if err != nil {
		t.Fatalf("Issue() error: %v", err)
	}
	if err := store.Consume(nonce); err != nil {
		t.Fatalf("first Consume() error: %v", err)
	}
	err = store.Consume(nonce)
	if err == nil {
		t.Fatalf("expected error on second Consume(), got nil")
	}
}

func TestNonceFormat(t *testing.T) {
	store := NewNonceStore()
	nonce, err := store.Issue()
	if err != nil {
		t.Fatalf("Issue() error: %v", err)
	}
	// 16 bytes base64url-encoded without padding = 22 characters.
	if len(nonce) != 22 {
		t.Fatalf("expected nonce length 22, got %d", len(nonce))
	}
	// Verify it is valid base64url (no padding).
	decoded, err := base64.RawURLEncoding.DecodeString(nonce)
	if err != nil {
		t.Fatalf("nonce is not valid base64url: %v", err)
	}
	if len(decoded) != 16 {
		t.Fatalf("expected 16 decoded bytes, got %d", len(decoded))
	}
}

func TestNonceConcurrentAccess(t *testing.T) {
	store := NewNonceStore()
	const goroutines = 50
	var wg sync.WaitGroup
	wg.Add(goroutines)
	errs := make(chan error, goroutines)
	for i := 0; i < goroutines; i++ {
		go func() {
			defer wg.Done()
			nonce, err := store.Issue()
			if err != nil {
				errs <- err
				return
			}
			if err := store.Consume(nonce); err != nil {
				errs <- err
				return
			}
		}()
	}
	wg.Wait()
	close(errs)
	for err := range errs {
		t.Fatalf("concurrent nonce operation failed: %v", err)
	}
}
