package remotecontext

import (
	"os"
	"path/filepath"
	"testing"

	"github.com/moby/go-archive"
	"github.com/moby/go-archive/compression"
	"github.com/moby/moby/v2/daemon/builder"
	"github.com/moby/sys/reexec"
	"github.com/pkg/errors"
	"gotest.tools/v3/skip"
)

const (
	filename = "test"
	contents = "contents test"
)

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

func TestCloseRootDirectory(t *testing.T) {
	src := makeTestArchiveContext(t, t.TempDir())
	err := src.Close()
	if err != nil {
		t.Fatalf("Error while executing Close: %s", err)
	}

	_, err = os.Stat(src.Root())
	if !errors.Is(err, os.ErrNotExist) {
		t.Fatal("Directory should not exist at this point")
	}
}

func TestHashFile(t *testing.T) {
	contextDir := t.TempDir()

	createTestTempFile(t, contextDir, filename, contents, 0o755)

	tarSum := makeTestArchiveContext(t, contextDir)

	sum, err := tarSum.Hash(filename)
	if err != nil {
		t.Fatalf("Error when executing Stat: %s", err)
	}

	if sum == "" {
		t.Fatalf("Hash returned empty sum")
	}

	expected := "55dfeb344351ab27f59aa60ebb0ed12025a2f2f4677bf77d26ea7a671274a9ca"

	if actual := sum; expected != actual {
		t.Fatalf("invalid checksum. expected %s, got %s", expected, actual)
	}
}

func TestHashSubdir(t *testing.T) {
	contextDir := t.TempDir()

	contextSubdir := filepath.Join(contextDir, "builder-tarsum-test-subdir")
	err := os.Mkdir(contextSubdir, 0o755)
	if err != nil {
		t.Fatalf("Failed to make directory: %s", contextSubdir)
	}

	testFilename := createTestTempFile(t, contextSubdir, filename, contents, 0o755)

	tarSum := makeTestArchiveContext(t, contextDir)

	relativePath, err := filepath.Rel(contextDir, testFilename)
	if err != nil {
		t.Fatalf("Error when getting relative path: %s", err)
	}

	sum, err := tarSum.Hash(relativePath)
	if err != nil {
		t.Fatalf("Error when executing Stat: %s", err)
	}

	if sum == "" {
		t.Fatalf("Hash returned empty sum")
	}

	expected := "74a3326b8e766ce63a8e5232f22e9dd895be647fb3ca7d337e5e0a9b3da8ef28"

	if actual := sum; expected != actual {
		t.Fatalf("invalid checksum. expected %s, got %s", expected, actual)
	}
}

func TestRemoveDirectory(t *testing.T) {
	contextDir := t.TempDir()

	contextSubdir, err := os.MkdirTemp(contextDir, "builder-tarsum-test-subdir")
	if err != nil {
		t.Fatalf("Error when creating directory %s with prefix %s: %s", contextDir, "builder-tarsum-test-subdir", err)
	}

	relativePath, err := filepath.Rel(contextDir, contextSubdir)
	if err != nil {
		t.Fatalf("Error when getting relative path: %s", err)
	}

	src := makeTestArchiveContext(t, contextDir)

	_, err = os.Stat(filepath.Join(src.Root(), relativePath))
	if err != nil {
		t.Fatalf("Statting %s shouldn't fail: %+v", relativePath, err)
	}

	tarSum := src.(modifiableContext)
	err = tarSum.Remove(relativePath)
	if err != nil {
		t.Fatalf("Error when executing Remove: %s", err)
	}

	_, err = os.Stat(filepath.Join(src.Root(), relativePath))
	if !errors.Is(err, os.ErrNotExist) {
		t.Fatalf("Directory should not exist at this point: %+v ", err)
	}
}

func makeTestArchiveContext(t *testing.T, dir string) builder.Source {
	skip.If(t, os.Getuid() != 0, "skipping test that requires root")
	tarStream, err := archive.Tar(dir, compression.None)
	if err != nil {
		t.Fatalf("error: %s", err)
	}
	defer tarStream.Close()
	tarSum, err := FromArchive(tarStream)
	if err != nil {
		t.Fatalf("Error when executing FromArchive: %s", err)
	}
	return tarSum
}
