package testcontainers_test

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

	"github.com/stretchr/testify/require"

	"github.com/testcontainers/testcontainers-go"
	"github.com/testcontainers/testcontainers-go/wait"
)

const testBashImage string = "bash:5.2.26"

func TestCopyFileToContainer(t *testing.T) {
	ctx, cnl := context.WithTimeout(context.Background(), 30*time.Second)
	defer cnl()

	// copyFileOnCreate {
	absPath, err := filepath.Abs(filepath.Join(".", "testdata", "hello.sh"))
	require.NoError(t, err)

	r, err := os.Open(absPath)
	require.NoError(t, err)

	ctr, err := testcontainers.Run(ctx, testBashImage,
		testcontainers.WithFiles(testcontainers.ContainerFile{
			Reader:            r,
			HostFilePath:      absPath, // will be discarded internally
			ContainerFilePath: "/hello.sh",
			FileMode:          0o700,
		}),
		testcontainers.WithCmd("bash", "/hello.sh"),
		testcontainers.WithWaitStrategy(wait.ForLog("done")),
	)
	// }
	testcontainers.CleanupContainer(t, ctr)
	require.NoError(t, err)
}

func TestCopyFileToRunningContainer(t *testing.T) {
	ctx, cnl := context.WithTimeout(context.Background(), 30*time.Second)
	defer cnl()

	// Not using the assertations here to avoid leaking the library into the example
	// copyFileAfterCreate {
	waitForPath, err := filepath.Abs(filepath.Join(".", "testdata", "waitForHello.sh"))
	require.NoError(t, err)
	helloPath, err := filepath.Abs(filepath.Join(".", "testdata", "hello.sh"))
	require.NoError(t, err)

	ctr, err := testcontainers.Run(ctx, testBashImage,
		testcontainers.WithFiles(testcontainers.ContainerFile{
			HostFilePath:      waitForPath,
			ContainerFilePath: "/waitForHello.sh",
			FileMode:          0o700,
		}),
		testcontainers.WithCmd("bash", "/waitForHello.sh"),
	)
	testcontainers.CleanupContainer(t, ctr)
	require.NoError(t, err)

	err = ctr.CopyFileToContainer(ctx, helloPath, "/scripts/hello.sh", 0o700)
	// }

	require.NoError(t, err)

	// Give some time to the wait script to catch the hello script being created
	err = wait.ForLog("done").WithStartupTimeout(2*time.Second).WaitUntilReady(ctx, ctr)
	require.NoError(t, err)
}

func TestCopyDirectoryToContainer(t *testing.T) {
	ctx, cnl := context.WithTimeout(context.Background(), 30*time.Second)
	defer cnl()

	// Not using the assertations here to avoid leaking the library into the example
	// copyDirectoryToContainer {
	dataDirectory, err := filepath.Abs(filepath.Join(".", "testdata"))
	require.NoError(t, err)

	ctr, err := testcontainers.Run(ctx, testBashImage,
		testcontainers.WithFiles(testcontainers.ContainerFile{
			HostFilePath: dataDirectory,
			// ContainerFile cannot create the parent directory, so we copy the scripts
			// to the root of the container instead. Make sure to create the container directory
			// before you copy a host directory on create.
			ContainerFilePath: "/",
			FileMode:          0o700,
		}),
		testcontainers.WithCmd("bash", "/testdata/hello.sh"),
		testcontainers.WithWaitStrategy(wait.ForLog("done")),
	)
	// }
	testcontainers.CleanupContainer(t, ctr)
	require.NoError(t, err)
}

func TestCopyDirectoryToRunningContainerAsFile(t *testing.T) {
	ctx, cnl := context.WithTimeout(context.Background(), 30*time.Second)
	defer cnl()

	// copyDirectoryToRunningContainerAsFile {
	dataDirectory, err := filepath.Abs(filepath.Join(".", "testdata"))
	require.NoError(t, err)
	waitForPath, err := filepath.Abs(filepath.Join(dataDirectory, "waitForHello.sh"))
	require.NoError(t, err)

	ctr, err := testcontainers.Run(ctx, testBashImage,
		testcontainers.WithFiles(testcontainers.ContainerFile{
			HostFilePath:      waitForPath,
			ContainerFilePath: "/waitForHello.sh",
			FileMode:          0o700,
		}),
		testcontainers.WithCmd("bash", "/waitForHello.sh"),
	)
	testcontainers.CleanupContainer(t, ctr)
	require.NoError(t, err)

	// as the container is started, we can create the directory first
	_, _, err = ctr.Exec(ctx, []string{"mkdir", "-p", "/scripts"})
	require.NoError(t, err)

	// because the container path is a directory, it will use the copy dir method as fallback
	err = ctr.CopyFileToContainer(ctx, dataDirectory, "/scripts", 0o700)
	require.NoError(t, err)
	// }
}

func TestCopyDirectoryToRunningContainerAsDir(t *testing.T) {
	ctx, cnl := context.WithTimeout(context.Background(), 30*time.Second)
	defer cnl()

	// Not using the assertations here to avoid leaking the library into the example
	// copyDirectoryToRunningContainerAsDir {
	waitForPath, err := filepath.Abs(filepath.Join(".", "testdata", "waitForHello.sh"))
	require.NoError(t, err)
	dataDirectory, err := filepath.Abs(filepath.Join(".", "testdata"))
	require.NoError(t, err)

	ctr, err := testcontainers.Run(ctx, testBashImage,
		testcontainers.WithFiles(testcontainers.ContainerFile{
			HostFilePath:      waitForPath,
			ContainerFilePath: "/waitForHello.sh",
			FileMode:          0o700,
		}),
		testcontainers.WithCmd("bash", "/waitForHello.sh"),
	)
	testcontainers.CleanupContainer(t, ctr)
	require.NoError(t, err)

	// as the container is started, we can create the directory first
	_, _, err = ctr.Exec(ctx, []string{"mkdir", "-p", "/scripts"})
	require.NoError(t, err)

	err = ctr.CopyDirToContainer(ctx, dataDirectory, "/scripts", 0o700)
	require.NoError(t, err)
	// }
}
