package client

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

	"github.com/nspcc-dev/neofs-api-go/v2/rpc/grpc"
	grpcstd "google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
)

func (c *Client) createGRPCClient() (err error) {
	c.gRPCClientOnce.Do(func() {
		if err = c.openGRPCConn(); err != nil {
			return
		}

		c.gRPCClient = grpc.New(
			grpc.WithClientConnection(c.conn),
			grpc.WithRWTimeout(c.rwTimeout),
		)
	})

	return
}

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

func (c *Client) openGRPCConn() error {
	if c.conn != nil {
		return nil
	}

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

	var err error

	var credOpt grpcstd.DialOption

	if c.tlsCfg != nil {
		creds := credentials.NewTLS(c.tlsCfg)
		credOpt = grpcstd.WithTransportCredentials(creds)
	} else {
		credOpt = grpcstd.WithInsecure()
	}

	dialCtx, cancel := context.WithTimeout(context.Background(), c.dialTimeout)
	c.conn, err = grpcstd.DialContext(dialCtx, c.addr, credOpt)
	cancel()

	return err
}

// 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
}