// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1

package proxy

import (
	"fmt"
	"strconv"
	"strings"

	"github.com/hashicorp/consul/api"
	"github.com/hashicorp/consul/connect/proxy"
)

// FlagUpstreams implements the flag.Value interface and allows specifying
// the -upstream flag multiple times and keeping track of the name of the
// upstream and the local port.
//
// The syntax of the value is "name:addr" where addr can be "port" or
// "host:port". Examples: "db:8181", "db:127.0.0.10:8282", etc.
//
// This would need to be extended for Unix Domain Sockets; how does this get handled
// addr: path & path:mode is ambigous; maybe path alone could be sorted by checking for a numeric port
// but iffy
type FlagUpstreams map[string]proxy.UpstreamConfig

func (f *FlagUpstreams) String() string {
	return fmt.Sprintf("%v", *f)
}

func (f *FlagUpstreams) Set(value string) error {
	idx := strings.Index(value, ":")
	if idx == -1 {
		return fmt.Errorf("Upstream value should be name:addr in %q", value)
	}

	addr := ""
	name := value[:idx]
	portRaw := value[idx+1:]
	if idx := strings.Index(portRaw, ":"); idx != -1 {
		addr = portRaw[:idx]
		portRaw = portRaw[idx+1:]
	}

	destinationType := "service"
	if idx := strings.Index(name, "."); idx != -1 {
		typ := name[idx+1:]
		name = name[:idx]
		switch typ {
		case "", "service":
			destinationType = "service"

		case "query":
			destinationType = "prepared_query"

		default:
			return fmt.Errorf(
				"Upstream type must be blank, 'service', or 'query'. Got: %q", typ)
		}
	}

	port, err := strconv.ParseInt(portRaw, 0, 0)
	if err != nil {
		return err
	}

	if *f == nil {
		*f = make(map[string]proxy.UpstreamConfig)
	}

	(*f)[name] = proxy.UpstreamConfig{
		LocalBindAddress: addr,
		LocalBindPort:    int(port),
		DestinationName:  name,
		DestinationType:  api.UpstreamDestType(destinationType),
	}

	return nil
}
