package mtls

import (
	"context"
	"crypto/rand"
	"encoding/hex"
	"fmt"
	"time"

	"github.com/redis/go-redis/v9"
)

const (
	bootstrapTokenPrefix = "enroll:token:"
	bootstrapTokenTTL    = 15 * time.Minute
)

// BootstrapTokenManager issues and validates one-time enrollment tokens.
// Tokens are generated before a node is provisioned (called by task #14 during
// cloud-init preparation) and consumed exactly once during the Enroll RPC.
type BootstrapTokenManager struct {
	redis redis.Cmdable
}

// NewBootstrapTokenManager creates a manager backed by the given Redis client.
func NewBootstrapTokenManager(r redis.Cmdable) *BootstrapTokenManager {
	return &BootstrapTokenManager{redis: r}
}

// IssueToken allocates a cryptographically random 32-byte token for a node.
// The mapping enroll:token:{token} → nodeUUID is stored in Redis with a 15-minute TTL.
//
// Callers (task #14) invoke this pre-flight when a new node record is created;
// the token is then injected into the node's cloud-init user-data.
func (m *BootstrapTokenManager) IssueToken(ctx context.Context, nodeUUID string) (string, error) {
	raw := make([]byte, 32)
	if _, err := rand.Read(raw); err != nil {
		return "", fmt.Errorf("mtls: generate token entropy: %w", err)
	}
	token := hex.EncodeToString(raw) // 64-char hex string

	key := bootstrapTokenPrefix + token
	if err := m.redis.Set(ctx, key, nodeUUID, bootstrapTokenTTL).Err(); err != nil {
		return "", fmt.Errorf("mtls: store bootstrap token: %w", err)
	}
	return token, nil
}

// ConsumeToken atomically retrieves and deletes the token (GETDEL).
// Returns the bound nodeUUID on the first and only successful call.
// A second call—or any call after the 15-minute TTL—returns an error.
func (m *BootstrapTokenManager) ConsumeToken(ctx context.Context, token string) (string, error) {
	key := bootstrapTokenPrefix + token
	nodeUUID, err := m.redis.GetDel(ctx, key).Result()
	if err == redis.Nil {
		return "", fmt.Errorf("mtls: bootstrap token not found, already used, or expired")
	}
	if err != nil {
		return "", fmt.Errorf("mtls: consume bootstrap token: %w", err)
	}
	return nodeUUID, nil
}
