// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1

package etcd

import (
	"context"
	"fmt"
	"net/url"
	"os"
	"testing"
	"time"

	"github.com/hashicorp/vault/sdk/helper/docker"
	clientv3 "go.etcd.io/etcd/client/v3"
)

type Config struct {
	docker.ServiceURL
}

// PrepareTestContainer creates etcd docker container.  If environment variabe
// ETCD_ADDR is set, the tests are executed against specified address and etcd
// container is not launched.
func PrepareTestContainer(t *testing.T, version string) (func(), *Config) {
	if addr := os.Getenv("ETCD_ADDR"); addr != "" {
		url, err := docker.NewServiceURLParse(addr)
		if err != nil {
			t.Fatal(err)
		}
		return func() {}, &Config{ServiceURL: *url}
	}

	// Check https://github.com/etcd-io/etcd/releases for latest releases.
	runner, err := docker.NewServiceRunner(docker.RunOptions{
		ContainerName: "etcd",
		ImageRepo:     "gcr.io/etcd-development/etcd",
		ImageTag:      version,
		Cmd: []string{
			"/usr/local/bin/etcd",
			"--name", "s1",
			"--listen-client-urls", "http://0.0.0.0:2379",
			"--advertise-client-urls", "http://0.0.0.0:2379",
			"--listen-peer-urls", "http://0.0.0.0:2380",
			"--initial-advertise-peer-urls", "http://0.0.0.0:2380",
			"--initial-cluster", "s1=http://0.0.0.0:2380",
			"--initial-cluster-token", "tkn",
			"--initial-cluster-state", "new",
			"--log-level", "info",
			"--logger", "zap",
			"--log-outputs", "stderr",
		},
		Ports: []string{"2379/tcp"},
	})
	if err != nil {
		t.Fatalf("Could not start docker etcd container: %s", err)
	}

	svc, err := runner.StartService(context.Background(), func(ctx context.Context, host string, port int) (docker.ServiceConfig, error) {
		address := fmt.Sprintf("%s:%d", host, port)
		s := docker.NewServiceURL(url.URL{
			Scheme: "http",
			Host:   address,
		})

		client, err := clientv3.New(clientv3.Config{
			Endpoints:   []string{address},
			DialTimeout: 2 * time.Minute,
		})
		if err != nil {
			return nil, fmt.Errorf("could not connect to etcd container: %w", err)
		}

		// Enable authentication for the tests.
		client.RoleAdd(ctx, "root")
		client.UserAdd(ctx, "root", "insecure")
		client.UserGrantRole(ctx, "root", "root")
		client.AuthEnable(ctx)
		client.Close()

		return &Config{
			ServiceURL: *s,
		}, nil
	})
	if err != nil {
		t.Fatalf("Could not start docker etcd container: %s", err)
	}

	return svc.Cleanup, svc.Config.(*Config)
}
