package containerd

import (
	"io"
	"path/filepath"
	"testing"

	"github.com/containerd/containerd/v2/pkg/namespaces"
	cerrdefs "github.com/containerd/errdefs"
	"github.com/containerd/platforms"
	"github.com/moby/moby/v2/internal/testutil/specialimage"
	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
	"gotest.tools/v3/assert"
	is "gotest.tools/v3/assert/cmp"
)

func TestImageMultiplatformSaveShallowWithNative(t *testing.T) {
	ctx := namespaces.WithNamespace(t.Context(), "testing-"+t.Name())

	contentDir := t.TempDir()
	store := &blobsDirContentStore{blobs: filepath.Join(contentDir, "blobs/sha256")}

	native := platforms.Platform{
		OS:           "linux",
		Architecture: "amd64",
	}

	arm64 := platforms.Platform{
		OS:           "linux",
		Architecture: "arm64",
	}

	riscv64 := platforms.Platform{
		OS:           "linux",
		Architecture: "riscv64",
	}

	imgSvc := fakeImageService(t, ctx, store)
	// Mock the native platform.
	imgSvc.defaultPlatformOverride = &native

	idx, _, err := specialimage.PartialMultiPlatform(contentDir, "partial-with-native:latest", specialimage.PartialOpts{
		Stored:  []ocispec.Platform{native, riscv64},
		Missing: []ocispec.Platform{arm64},
	})
	assert.NilError(t, err)

	img, err := imgSvc.images.Create(ctx, imagesFromIndex(idx)[0])
	assert.NilError(t, err)

	t.Run("export without specific platform", func(t *testing.T) {
		err = imgSvc.ExportImage(ctx, []string{img.Name}, nil, io.Discard)
		assert.NilError(t, err)
	})
	t.Run("export native", func(t *testing.T) {
		err = imgSvc.ExportImage(ctx, []string{img.Name}, []ocispec.Platform{native}, io.Discard)
		assert.NilError(t, err)
	})
	t.Run("export multiple platforms", func(t *testing.T) {
		err = imgSvc.ExportImage(ctx, []string{img.Name}, []ocispec.Platform{native, riscv64}, io.Discard)
		assert.NilError(t, err)
	})
	t.Run("export missing", func(t *testing.T) {
		err = imgSvc.ExportImage(ctx, []string{img.Name}, []ocispec.Platform{arm64}, io.Discard)
		assert.Check(t, is.ErrorType(err, cerrdefs.IsNotFound))
	})
	t.Run("export multiple platforms with some missing", func(t *testing.T) {
		err = imgSvc.ExportImage(ctx, []string{img.Name}, []ocispec.Platform{arm64, riscv64}, io.Discard)
		assert.NilError(t, err)
	})
}

func TestImageMultiplatformSaveShallowWithoutNative(t *testing.T) {
	ctx := namespaces.WithNamespace(t.Context(), "testing-"+t.Name())

	contentDir := t.TempDir()
	store := &blobsDirContentStore{blobs: filepath.Join(contentDir, "blobs/sha256")}

	native := platforms.Platform{
		OS:           "linux",
		Architecture: "amd64",
	}

	arm64 := platforms.Platform{
		OS:           "linux",
		Architecture: "arm64",
	}

	riscv64 := platforms.Platform{
		OS:           "linux",
		Architecture: "riscv64",
	}

	s390x := platforms.Platform{
		OS:           "linux",
		Architecture: "s390x",
	}

	imgSvc := fakeImageService(t, ctx, store)
	// Mock the native platform.
	imgSvc.defaultPlatformOverride = &native

	idx, _, err := specialimage.PartialMultiPlatform(contentDir, "partial-without-native:latest", specialimage.PartialOpts{
		Stored:  []ocispec.Platform{arm64, riscv64},
		Missing: []ocispec.Platform{native},
	})
	assert.NilError(t, err)

	img, err := imgSvc.images.Create(ctx, imagesFromIndex(idx)[0])
	assert.NilError(t, err)

	t.Run("export without specific platform", func(t *testing.T) {
		t.Skip("TODO(vvoland): https://github.com/docker/cli/issues/5476")
		err = imgSvc.ExportImage(ctx, []string{img.Name}, nil, io.Discard)
		assert.NilError(t, err)
	})
	t.Run("export native", func(t *testing.T) {
		err = imgSvc.ExportImage(ctx, []string{img.Name}, []ocispec.Platform{native}, io.Discard)
		assert.Check(t, is.ErrorType(err, cerrdefs.IsNotFound))
	})
	t.Run("export arm64", func(t *testing.T) {
		err = imgSvc.ExportImage(ctx, []string{img.Name}, []ocispec.Platform{arm64}, io.Discard)
		assert.NilError(t, err)
	})
	t.Run("export multiple platforms", func(t *testing.T) {
		err = imgSvc.ExportImage(ctx, []string{img.Name}, []ocispec.Platform{arm64, riscv64}, io.Discard)
		assert.NilError(t, err)
	})
	t.Run("export multiple platforms with some missing", func(t *testing.T) {
		err = imgSvc.ExportImage(ctx, []string{img.Name}, []ocispec.Platform{arm64, native}, io.Discard)
		assert.Check(t, is.ErrorType(err, cerrdefs.IsNotFound))
	})
	t.Run("export non existing platform", func(t *testing.T) {
		err = imgSvc.ExportImage(ctx, []string{img.Name}, []ocispec.Platform{s390x}, io.Discard)
		assert.Check(t, is.ErrorType(err, cerrdefs.IsNotFound))
	})
}
