package etcd

import (
	"net/url"
	"path"
	"strings"

	"google.golang.org/grpc/resolver"
	"google.golang.org/grpc/resolver/manual"
)

const scheme = "etcd-endpoint"

type SimpleResolver struct {
	*manual.Resolver
	endpoint string
}

// Cribbed from https://github.com/etcd-io/etcd/blob/v3.6.4/client/v3/internal/resolver/resolver.go
// but only supports a single fixed endpoint. We use this instead of the internal etcd client resolver
// because the agent loadbalancer handles failover and we don't want etcd or grpc's special behavior.
func NewSimpleResolver(endpoint string) *SimpleResolver {
	r := manual.NewBuilderWithScheme(scheme)
	return &SimpleResolver{Resolver: r, endpoint: endpoint}
}

func (r *SimpleResolver) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions) (resolver.Resolver, error) {
	res, err := r.Resolver.Build(target, cc, opts)
	if err != nil {
		return nil, err
	}
	if cc != nil {
		addr, serverName := interpret(r.endpoint)
		a := resolver.Address{Addr: addr, ServerName: serverName}
		r.UpdateState(resolver.State{
			Endpoints: []resolver.Endpoint{
				{Addresses: []resolver.Address{a}},
			},
		})
	}

	return res, nil
}

func interpret(ep string) (string, string) {
	if strings.HasPrefix(ep, "unix:") || strings.HasPrefix(ep, "unixs:") {
		if strings.HasPrefix(ep, "unix:///") || strings.HasPrefix(ep, "unixs:///") {
			_, absolutePath, _ := strings.Cut(ep, "://")
			return "unix://" + absolutePath, path.Base(absolutePath)
		}
		if strings.HasPrefix(ep, "unix://") || strings.HasPrefix(ep, "unixs://") {
			_, localPath, _ := strings.Cut(ep, "://")
			return "unix:" + localPath, path.Base(localPath)
		}
		_, localPath, _ := strings.Cut(ep, ":")
		return "unix:" + localPath, path.Base(localPath)
	}
	if strings.Contains(ep, "://") {
		url, err := url.Parse(ep)
		if err != nil {
			return ep, ep
		}
		if url.Scheme == "http" || url.Scheme == "https" {
			return url.Host, url.Host
		}
		return ep, url.Host
	}
	return ep, ep
}

func authority(ep string) string {
	if _, authority, ok := strings.Cut(ep, "://"); ok {
		return authority
	}
	if suff, ok := strings.CutPrefix(ep, "unix:"); ok {
		return suff
	}
	if suff, ok := strings.CutPrefix(ep, "unixs:"); ok {
		return suff
	}
	return ep
}
