//go:build !remote

package libimage

import (
	"context"
	"os"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"go.podman.io/common/pkg/config"
	"go.podman.io/image/v5/types"
	"go.podman.io/storage"
	"go.podman.io/storage/pkg/reexec"
)

func TestMain(m *testing.M) {
	if reexec.Init() {
		return
	}
	os.Exit(m.Run())
}

type testNewRuntimeOptions struct {
	registriesConfPath string
	dirForceDecompress bool
}

// Create a new Runtime that can be used for testing.
func testNewRuntime(t *testing.T, options ...testNewRuntimeOptions) *Runtime {
	workdir := t.TempDir()
	storeOptions := &storage.StoreOptions{
		RunRoot:         workdir,
		GraphRoot:       workdir,
		GraphDriverName: "vfs",
	}

	// Make sure that the tests do not use the host's registries.conf.
	systemContext := &types.SystemContext{
		SystemRegistriesConfPath:    "testdata/registries.conf",
		SystemRegistriesConfDirPath: t.TempDir(),
	}

	if len(options) == 1 {
		if options[0].registriesConfPath != "" {
			systemContext.SystemRegistriesConfPath = options[0].registriesConfPath
		}
		if options[0].dirForceDecompress {
			systemContext.DirForceDecompress = true
		}
	}

	runtime, err := RuntimeFromStoreOptions(&RuntimeOptions{SystemContext: systemContext}, storeOptions)
	require.NoError(t, err)
	tmpd, err := tmpdir()
	require.NoError(t, err)
	require.Equal(t, runtime.systemContext.BigFilesTemporaryDir, tmpd)

	t.Cleanup(func() { _ = runtime.Shutdown(true) })

	sys := runtime.SystemContext()
	require.NotNil(t, sys)
	return runtime
}

func testRuntimePullImage(t *testing.T, r *Runtime, ctx context.Context, imageName string) {
	pullOptions := &PullOptions{}
	pullOptions.Writer = os.Stdout
	_, err := r.Pull(ctx, imageName, config.PullPolicyMissing, pullOptions)
	require.NoError(t, err)
}

func TestTmpdir(t *testing.T) {
	tmpStr := "TMPDIR"
	confStr := "CONTAINERS_CONF"

	t.Run(confStr, func(t *testing.T) {
		// Note, Cleanup() must be called before Setenv() so it only
		// runs after the Setenv() cleanup reset the env again.
		t.Cleanup(func() {
			_, err := config.Reload()
			require.NoError(t, err)
		})
		t.Setenv(confStr, "testdata/containers.conf")

		_, err := config.Reload()
		require.NoError(t, err)

		tmpd, err := tmpdir()
		require.NoError(t, err)
		require.Equal(t, "/tmp/from/containers.conf", tmpd)
	})

	t.Run(tmpStr, func(t *testing.T) {
		t.Setenv(tmpStr, "/tmp/test")
		tmpd, err := tmpdir()
		require.NoError(t, err)
		require.Equal(t, "/tmp/test", tmpd)

		os.Unsetenv(tmpStr)
		tmpd, err = tmpdir()
		require.NoError(t, err)
		require.Equal(t, "/var/tmp", tmpd)
	})
}

func TestRuntimeListImagesAllImages(t *testing.T) {
	runtime := testNewRuntime(t)
	ctx := context.Background()

	// Prefetch alpine, busybox.
	testRuntimePullImage(t, runtime, ctx, "quay.io/libpod/alpine:latest")
	testRuntimePullImage(t, runtime, ctx, "quay.io/libpod/busybox:latest")

	images, err := runtime.ListImages(ctx, nil)
	require.NoError(t, err)

	require.Len(t, images, 2)
	imageNames := make([]string, 0, len(images))
	for _, i := range images {
		imageNames = append(imageNames, i.Names()...)
	}
	assert.ElementsMatch(t,
		imageNames,
		[]string{"quay.io/libpod/alpine:latest", "quay.io/libpod/busybox:latest"},
	)
}

func TestRuntimeListImagesByNames(t *testing.T) {
	runtime := testNewRuntime(t)
	ctx := context.Background()

	// Prefetch alpine, busybox.
	testRuntimePullImage(t, runtime, ctx, "quay.io/libpod/alpine:latest")
	testRuntimePullImage(t, runtime, ctx, "quay.io/libpod/busybox:latest")

	for _, test := range []struct {
		name     string
		fullName string
	}{
		{"alpine", "quay.io/libpod/alpine:latest"},
		{"busybox", "quay.io/libpod/busybox:latest"},
	} {
		images, err := runtime.ListImagesByNames([]string{test.name})
		require.NoError(t, err)
		require.Len(t, images, 1)
		require.Contains(t, images[0].Names(), test.fullName)
	}
	_, err := runtime.ListImagesByNames([]string{""})
	require.Error(t, err)
}
