package mtls

import (
	"context"
	"strings"
	"testing"
	"time"

	"github.com/alicebob/miniredis/v2"
	"github.com/redis/go-redis/v9"
)

func newTestBootstrapManager(t *testing.T) (*BootstrapTokenManager, *miniredis.Miniredis) {
	t.Helper()
	mr := miniredis.RunT(t)
	rdb := redis.NewClient(&redis.Options{Addr: mr.Addr()})
	t.Cleanup(func() { rdb.Close() })
	return NewBootstrapTokenManager(rdb), mr
}

func TestBootstrap_IssueToken_Format(t *testing.T) {
	mgr, _ := newTestBootstrapManager(t)

	token, err := mgr.IssueToken(context.Background(), "node-abc")
	if err != nil {
		t.Fatalf("IssueToken: %v", err)
	}
	if len(token) != 64 {
		t.Errorf("token length = %d; want 64 hex chars", len(token))
	}
	if strings.ContainsAny(token, "ghijklmnopqrstuvwxyz !@#") {
		t.Errorf("token contains non-hex chars: %q", token)
	}
}

func TestBootstrap_ConsumeToken_OnceOnly(t *testing.T) {
	mgr, _ := newTestBootstrapManager(t)
	ctx := context.Background()

	const nodeUUID = "node-11111111-2222-3333-4444-555555555555"

	token, err := mgr.IssueToken(ctx, nodeUUID)
	if err != nil {
		t.Fatalf("IssueToken: %v", err)
	}

	// First consume: must succeed.
	got, err := mgr.ConsumeToken(ctx, token)
	if err != nil {
		t.Fatalf("ConsumeToken (first): %v", err)
	}
	if got != nodeUUID {
		t.Errorf("ConsumeToken returned %q; want %q", got, nodeUUID)
	}

	// Second consume: must fail (one-time guarantee).
	_, err = mgr.ConsumeToken(ctx, token)
	if err == nil {
		t.Fatal("ConsumeToken (second): expected error for already-consumed token, got nil")
	}
}

func TestBootstrap_ConsumeToken_NeverIssued(t *testing.T) {
	mgr, _ := newTestBootstrapManager(t)

	_, err := mgr.ConsumeToken(context.Background(), "deadbeefdeadbeef")
	if err == nil {
		t.Fatal("expected error for non-existent token, got nil")
	}
}

func TestBootstrap_ConsumeToken_ExpiredTTL(t *testing.T) {
	mgr, mr := newTestBootstrapManager(t)
	ctx := context.Background()

	token, err := mgr.IssueToken(ctx, "node-expired")
	if err != nil {
		t.Fatalf("IssueToken: %v", err)
	}

	// Advance miniredis clock past the 15-minute TTL.
	mr.FastForward(bootstrapTokenTTL + time.Second)

	_, err = mgr.ConsumeToken(ctx, token)
	if err == nil {
		t.Fatal("expected error after TTL expiry, got nil")
	}
}

func TestBootstrap_IssueToken_Uniqueness(t *testing.T) {
	mgr, _ := newTestBootstrapManager(t)
	ctx := context.Background()

	t1, _ := mgr.IssueToken(ctx, "node-A")
	t2, _ := mgr.IssueToken(ctx, "node-B")
	if t1 == t2 {
		t.Error("IssueToken returned duplicate tokens for different nodes")
	}
}
