package exec

import (
	"fmt"
	"regexp"
	"strings"

	portainer "github.com/portainer/portainer/api"
	"github.com/portainer/portainer/api/http/proxy"
	"github.com/portainer/portainer/api/http/proxy/factory"
	"github.com/portainer/portainer/api/internal/registryutils"

	"github.com/docker/cli/cli/config/types"
	"github.com/rs/zerolog/log"
)

var stackNameNormalizeRegex = regexp.MustCompile("[^-_a-z0-9]+")

func normalizeStackName(name string) string {
	return stackNameNormalizeRegex.ReplaceAllString(strings.ToLower(name), "")
}

// fetchEndpointProxy returns the Docker host URL for the given endpoint.
// For remote endpoints it creates a local proxy that handles TLS termination and
// Portainer agent header injection; for local unix/npipe sockets no proxy is needed.
func fetchEndpointProxy(proxyManager *proxy.Manager, endpoint *portainer.Endpoint) (string, *factory.ProxyServer, error) {
	if strings.HasPrefix(endpoint.URL, "unix://") || strings.HasPrefix(endpoint.URL, "npipe://") {
		return "", nil, nil
	}

	proxy, err := proxyManager.CreateAgentProxyServer(endpoint)
	if err != nil {
		return "", nil, err
	}

	return fmt.Sprintf("tcp://127.0.0.1:%d", proxy.Port), proxy, nil
}

// portainerRegistriesToAuthConfigs converts registries to Docker auth configs.
// Callers must ensure ECR tokens are valid before calling this function (e.g. via
// registryutils.RefreshAndPersistECRTokens with a real DataStoreTx). This function
// intentionally performs no DB writes to avoid write-lock contention when called inside
// an active BoltDB write transaction.
func portainerRegistriesToAuthConfigs(registries []portainer.Registry) []types.AuthConfig {
	var authConfigs []types.AuthConfig

	for _, r := range registries {
		ac := types.AuthConfig{
			Username:      r.Username,
			Password:      r.Password,
			ServerAddress: r.URL,
		}

		if r.Authentication {
			var err error

			ac.Username, ac.Password, err = getEffectiveRegUsernamePassword(&r)
			if err != nil {
				continue
			}
		}

		authConfigs = append(authConfigs, ac)
	}

	return authConfigs
}

func getEffectiveRegUsernamePassword(registry *portainer.Registry) (string, string, error) {
	username, password, err := registryutils.GetRegEffectiveCredential(registry)
	if err != nil {
		log.Warn().
			Err(err).
			Str("RegistryName", registry.Name).
			Msg("Failed to get effective credential. Skip logging with this registry.")
	}

	return username, password, err
}
