package client

import (
	"context"
	"net"

	"google.golang.org/grpc"
)

type Option func(*cfg)

type cfg struct {
	addr string

	conn net.Conn

	gRPC cfgGRPC
}

type cfgGRPC struct {
	dialCtx context.Context

	dialOpts []grpc.DialOption

	conn *grpc.ClientConn
}

func defaultCfg() *cfg {
	return &cfg{
		gRPC: cfgGRPC{
			dialCtx: context.Background(),
			dialOpts: []grpc.DialOption{
				grpc.WithInsecure(),
			},
		},
	}
}

func NewGRPCClientConn(opts ...Option) (*grpc.ClientConn, error) {
	cfg := defaultCfg()

	for i := range opts {
		opts[i](cfg)
	}

	var err error

	if cfg.gRPC.conn == nil {
		if cfg.conn != nil {
			if cfg.addr == "" {
				cfg.addr = cfg.conn.RemoteAddr().String()
			}

			cfg.gRPC.dialOpts = append(cfg.gRPC.dialOpts,
				grpc.WithContextDialer(func(context.Context, string) (net.Conn, error) {
					return cfg.conn, nil
				}),
			)
		}

		cfg.gRPC.conn, err = grpc.DialContext(cfg.gRPC.dialCtx, cfg.addr, cfg.gRPC.dialOpts...)
		if err != nil {
			return nil, err
		}
	}

	return cfg.gRPC.conn, err
}

func WithNetworkAddress(v string) Option {
	return func(c *cfg) {
		if v != "" {
			c.addr = v
		}
	}
}

func WithNetConn(v net.Conn) Option {
	return func(c *cfg) {
		if v != nil {
			c.conn = v
		}
	}
}

func WithGRPCDialContext(v context.Context) Option {
	return func(c *cfg) {
		if v != nil {
			c.gRPC.dialCtx = v
		}
	}
}

func WithGRPCDialOpts(v []grpc.DialOption) Option {
	return func(c *cfg) {
		if len(v) > 0 {
			c.gRPC.dialOpts = v
		}
	}
}

func WithGRPCConn(v *grpc.ClientConn) Option {
	return func(c *cfg) {
		if v != nil {
			c.gRPC.conn = v
		}
	}
}