package cli_test

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

	"github.com/stretchr/testify/require"

	"github.com/kopia/kopia/cli"
	"github.com/kopia/kopia/internal/testutil"
	"github.com/kopia/kopia/tests/testenv"
)

func TestSnapshotList(t *testing.T) {
	t.Parallel()

	runner := testenv.NewInProcRunner(t)
	e := testenv.NewCLITest(t, testenv.RepoFormatNotImportant, runner)

	defer e.RunAndExpectSuccess(t, "repo", "disconnect")

	e.RunAndExpectSuccess(t, "repo", "create", "filesystem", "--path", e.RepoDir)

	srcdir := testutil.TempDirectory(t)
	require.NoError(t, os.WriteFile(filepath.Join(srcdir, "some-file2"), []byte{1, 2, 3}, 0o755))

	var man cli.SnapshotManifest

	e.RunAndExpectSuccess(t, "policy", "set", srcdir, "--keep-latest=4", "--keep-hourly=0", "--keep-daily=0", "--keep-monthly=0", "--keep-weekly=0", "--keep-annual=0")

	testutil.MustParseJSONLines(t, e.RunAndExpectSuccess(t, "snapshot", "create", srcdir, "--json"), &man)

	require.NoError(t, os.WriteFile(filepath.Join(srcdir, "some-file3"), []byte{1, 2, 3, 4}, 0o755))
	testutil.MustParseJSONLines(t, e.RunAndExpectSuccess(t, "snapshot", "create", srcdir, "--json"), &man)

	require.NoError(t, os.WriteFile(filepath.Join(srcdir, "some-file4"), []byte{4}, 0o755))
	e.RunAndExpectSuccess(t, "snapshot", "create", srcdir)
	e.RunAndExpectSuccess(t, "snapshot", "create", srcdir)

	var snapshots []*cli.SnapshotManifest

	testutil.MustParseJSONLines(t, e.RunAndExpectSuccess(t, "snapshot", "list",
		"--json"), &snapshots)

	require.Len(t, snapshots, 4)

	for _, s := range snapshots {
		require.NotEmpty(t, s.RetentionReasons, "expecting retention reason to be set")
	}

	lines := e.RunAndExpectSuccess(t, "snapshot", "list")
	require.Len(t, lines, 5)

	require.Contains(t, lines[1], " 3 B ")
	require.Contains(t, lines[1], " files:1 dirs:1 ")

	require.Contains(t, lines[2], " 7 B ")
	require.Contains(t, lines[2], " files:2 dirs:1 ")

	require.Contains(t, lines[3], " 8 B ")
	require.Contains(t, lines[3], " files:3 dirs:1 ")

	require.Contains(t, lines[4], "+ 1 identical snapshots until")

	lines = e.RunAndExpectSuccess(t, "snapshot", "list", "-l")
	require.Len(t, lines, 5)

	require.Contains(t, lines[1], " 3 B ")
	require.Contains(t, lines[1], " files:1 dirs:1 ")

	require.Contains(t, lines[2], " 7 B ")
	require.Contains(t, lines[2], " files:2 dirs:1 ")

	require.Contains(t, lines[3], " 8 B ")
	require.Contains(t, lines[3], " files:3 dirs:1 ")

	// identical snapshot is not coalesced
	require.Contains(t, lines[4], " 8 B ")
	require.Contains(t, lines[4], " files:3 dirs:1 ")
}

func TestSnapshotListWithSameFileInMultipleSnapshots(t *testing.T) {
	t.Parallel()

	runner := testenv.NewInProcRunner(t)
	e := testenv.NewCLITest(t, testenv.RepoFormatNotImportant, runner)

	defer e.RunAndExpectSuccess(t, "repo", "disconnect")

	e.RunAndExpectSuccess(t, "repo", "create", "filesystem", "--path", e.RepoDir)

	srcdir := testutil.TempDirectory(t)

	require.NoError(t, os.MkdirAll(filepath.Join(srcdir, "a", "b", "c", "d"), 0o755))
	require.NoError(t, os.WriteFile(filepath.Join(srcdir, "a", "b", "c", "d", "e.txt"), []byte{1, 2, 3}, 0o755))

	e.RunAndExpectSuccess(t, "snapshot", "create", srcdir)
	e.RunAndExpectSuccess(t, "snapshot", "create", filepath.Join(srcdir, "a"))
	e.RunAndExpectSuccess(t, "snapshot", "create", filepath.Join(srcdir, "a", "b"))
	e.RunAndExpectSuccess(t, "snapshot", "create", filepath.Join(srcdir, "a", "b", "c"))
	e.RunAndExpectSuccess(t, "snapshot", "create", filepath.Join(srcdir, "a", "b", "c", "d"))
	e.RunAndExpectSuccess(t, "snapshot", "create", filepath.Join(srcdir, "a", "b", "c", "d", "e.txt"))

	var snapshots []*cli.SnapshotManifest

	testutil.MustParseJSONLines(t, e.RunAndExpectSuccess(t, "snapshot", "list",
		filepath.Join(srcdir, "a", "b", "c", "d", "e.txt"), "--json"), &snapshots)

	require.Len(t, snapshots, 6)

	var sps []string

	for _, s := range snapshots {
		sps = append(sps, s.Source.Path)
	}

	require.Equal(t, []string{
		srcdir,
		filepath.Join(srcdir, "a"),
		filepath.Join(srcdir, "a", "b"),
		filepath.Join(srcdir, "a", "b", "c"),
		filepath.Join(srcdir, "a", "b", "c", "d"),
		filepath.Join(srcdir, "a", "b", "c", "d", "e.txt"),
	}, sps)
}
