package signerpool

import (
	"fmt"
	"strings"
	"sync"

	"github.com/projectdiscovery/nuclei/v3/pkg/protocols/http/signer"

	"github.com/projectdiscovery/nuclei/v3/pkg/types"
)

var (
	poolMutex  sync.RWMutex
	clientPool map[string]signer.Signer
)

// Init initializes the clientpool implementation
func Init(options *types.Options) error {
	poolMutex.Lock()
	defer poolMutex.Unlock()
	if clientPool != nil {
		return nil // already initialized
	}
	clientPool = make(map[string]signer.Signer)
	return nil
}

// Configuration contains the custom configuration options for a client
type Configuration struct {
	SignerArgs signer.SignerArgs
}

// Hash returns the hash of the configuration to allow client pooling
func (c *Configuration) Hash() string {
	builder := &strings.Builder{}
	_, _ = fmt.Fprintf(builder, "%v", c.SignerArgs)
	hash := builder.String()
	return hash
}

// Get creates or gets a client for the protocol based on custom configuration
func Get(options *types.Options, configuration *Configuration) (signer.Signer, error) {
	hash := configuration.Hash()
	poolMutex.RLock()
	if client, ok := clientPool[hash]; ok {
		poolMutex.RUnlock()
		return client, nil
	}
	poolMutex.RUnlock()

	client, err := signer.NewSigner(configuration.SignerArgs)
	if err != nil {
		return nil, err
	}

	poolMutex.Lock()
	clientPool[hash] = client
	poolMutex.Unlock()
	return client, nil
}
