package client

import (
	"context"
	"net"
	"net/url"

	commandconnPkg "coopcloud.tech/abra/pkg/client/commandconn"
	"github.com/docker/cli/cli/connhelper"
	"github.com/docker/cli/cli/connhelper/ssh"
	"github.com/docker/cli/cli/context/docker"
	dCliContextStore "github.com/docker/cli/cli/context/store"
	dClient "github.com/docker/docker/client"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
)

// GetConnectionHelper returns Docker-specific connection helper for the given URL.
// GetConnectionHelper returns nil without error when no helper is registered for the scheme.
//
// ssh://<user>@<host> URL requires Docker 18.09 or later on the remote host.
func GetConnectionHelper(daemonURL string) (*connhelper.ConnectionHelper, error) {
	return getConnectionHelper(daemonURL, nil)
}

func getConnectionHelper(daemonURL string, sshFlags []string) (*connhelper.ConnectionHelper, error) {
	u, err := url.Parse(daemonURL)
	if err != nil {
		return nil, err
	}
	switch scheme := u.Scheme; scheme {
	case "ssh":
		sp, err := ssh.ParseURL(daemonURL)
		if err != nil {
			return nil, errors.Wrap(err, "ssh host connection is not valid")
		}
		return &connhelper.ConnectionHelper{
			Dialer: func(ctx context.Context, network, addr string) (net.Conn, error) {
				return commandconnPkg.New(ctx, "ssh", append(sshFlags, sp.Args("docker", "system", "dial-stdio")...)...)
			},
			Host: daemonURL,
		}, nil
	}
	// Future version may support plugins via ~/.docker/config.json. e.g. "dind"
	// See docker/cli#889 for the previous discussion.
	return nil, err
}

func newConnectionHelper(daemonURL string) *connhelper.ConnectionHelper {
	helper, err := GetConnectionHelper(daemonURL)
	if err != nil {
		logrus.Fatal(err)
	}

	return helper
}

func getDockerEndpoint(host string) (docker.Endpoint, error) {
	skipTLSVerify := false
	ep := docker.Endpoint{
		EndpointMeta: docker.EndpointMeta{
			Host:          host,
			SkipTLSVerify: skipTLSVerify,
		},
	}
	// try to resolve a docker client, validating the configuration
	opts, err := ep.ClientOpts()
	if err != nil {
		return docker.Endpoint{}, err
	}
	if _, err := dClient.NewClientWithOpts(opts...); err != nil {
		return docker.Endpoint{}, err
	}
	return ep, nil
}

func getDockerEndpointMetadataAndTLS(host string) (docker.EndpointMeta, *dCliContextStore.EndpointTLSData, error) {
	ep, err := getDockerEndpoint(host)
	if err != nil {
		return docker.EndpointMeta{}, nil, err
	}
	return ep.EndpointMeta, ep.TLSData.ToStoreTLSData(), nil
}