package client

import (
	"context"
	"errors"
	"fmt"
	"net"
	"net/url"

	grpcstd "google.golang.org/grpc"
)

var errInvalidEndpoint = errors.New("invalid endpoint options")

func (c *Client) openGRPCConn(ctx context.Context, dialer func(ctx context.Context, cc grpcstd.ClientConnInterface) error) error {
	if c.conn != nil {
		return nil
	}

	if c.addr == "" {
		return errInvalidEndpoint
	}

	var err error

	c.conn, err = grpcstd.NewClient(c.addr, c.grpcDialOpts...)
	if err != nil {
		return fmt.Errorf("gRPC new client: %w", err)
	}

	if dialer != nil {
		ctx, cancel := context.WithTimeout(ctx, c.dialTimeout)
		defer cancel()

		if err := dialer(ctx, c.conn); err != nil {
			_ = c.conn.Close()
			return fmt.Errorf("gRPC dial: %w", err)
		}
	}

	return nil
}

// ParseURI parses s as address and returns a host and a flag
// indicating that TLS is enabled. If multi-address is provided
// the argument is returned unchanged.
func ParseURI(s string) (string, bool, error) {
	uri, err := url.ParseRequestURI(s)
	if err != nil {
		return s, false, nil
	}

	// check if passed string was parsed correctly
	// URIs that do not start with a slash after the scheme are interpreted as:
	// `scheme:opaque` => if `opaque` is not empty, then it is supposed that URI
	// is in `host:port` format
	if uri.Host == "" {
		uri.Host = uri.Scheme
		uri.Scheme = grpcScheme // assume GRPC by default
		if uri.Opaque != "" {
			uri.Host = net.JoinHostPort(uri.Host, uri.Opaque)
		}
	}

	switch uri.Scheme {
	case grpcTLSScheme, grpcScheme:
	default:
		return "", false, fmt.Errorf("unsupported scheme: %s", uri.Scheme)
	}

	return uri.Host, uri.Scheme == grpcTLSScheme, nil
}