package auth

import (
	"crypto/rand"
	"fmt"
	"strings"
	"sync"
	"time"
)

// DynamicClient holds a dynamically registered OAuth client (RFC 7591).
type DynamicClient struct {
	ClientID     string
	ClientName   string
	RedirectURIs []string
	CreatedAt    time.Time
}

// HasRedirectURI reports whether uri is registered for this client.
func (c *DynamicClient) HasRedirectURI(uri string) bool {
	for _, u := range c.RedirectURIs {
		if u == uri {
			return true
		}
	}
	return false
}

// DynamicClientStore holds dynamically registered OAuth clients in memory.
type DynamicClientStore struct {
	mu      sync.RWMutex
	clients map[string]DynamicClient
}

func NewDynamicClientStore() *DynamicClientStore {
	return &DynamicClientStore{clients: make(map[string]DynamicClient)}
}

// Register creates a new client and returns it.
func (s *DynamicClientStore) Register(name string, redirectURIs []string) (DynamicClient, error) {
	b := make([]byte, 16)
	if _, err := rand.Read(b); err != nil {
		return DynamicClient{}, err
	}
	client := DynamicClient{
		ClientID:     fmt.Sprintf("%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:]),
		ClientName:   name,
		RedirectURIs: append([]string(nil), redirectURIs...),
		CreatedAt:    time.Now(),
	}
	s.mu.Lock()
	s.clients[normalizeClientID(client.ClientID)] = client
	s.mu.Unlock()
	return client, nil
}

// Lookup returns the client for the given client_id.
// Accepts UUIDs with or without dashes.
func (s *DynamicClientStore) Lookup(clientID string) (DynamicClient, bool) {
	s.mu.RLock()
	client, ok := s.clients[normalizeClientID(clientID)]
	s.mu.RUnlock()
	return client, ok
}

func normalizeClientID(id string) string {
	return strings.ReplaceAll(id, "-", "")
}
