package staticfs

import (
	"io"
	iofs "io/fs"
	"os"
	"testing"

	"github.com/stretchr/testify/require"
	"github.com/tonistiigi/fsutil/types"
)

func TestStatic(t *testing.T) {
	fs := NewFS()
	fs.Add("foo", &types.Stat{Mode: 0644}, []byte("foofoo"))
	fs.Add("bar", &types.Stat{Mode: 0444}, []byte("barbarbar"))

	rc, err := fs.Open("bar")
	require.NoError(t, err)
	require.NoError(t, rc.Close())

	data, err := io.ReadAll(rc)
	require.NoError(t, err)
	require.Equal(t, []byte("barbarbar"), data)

	_, err = fs.Open("abc")
	require.Error(t, err)
	require.True(t, os.IsNotExist(err))

	var files []string
	err = fs.Walk(t.Context(), "", func(path string, entry iofs.DirEntry, err error) error {
		require.NoError(t, err)
		info, err := entry.Info()
		require.NoError(t, err)
		switch path {
		case "foo":
			require.Equal(t, int64(6), info.Size())
			require.Equal(t, os.FileMode(0644), info.Mode())
		case "bar":
			require.Equal(t, int64(9), info.Size())
			require.Equal(t, os.FileMode(0444), info.Mode())
		default:
			require.Fail(t, "unexpected path", path)
		}
		files = append(files, path)
		return nil
	})
	require.NoError(t, err)

	require.Equal(t, []string{"bar", "foo"}, files)

	fs.Add("abc", &types.Stat{Mode: 0444}, []byte("abcabcabc"))

	rc, err = fs.Open("abc")
	require.NoError(t, err)

	data, err = io.ReadAll(rc)
	require.NoError(t, err)
	require.Equal(t, []byte("abcabcabc"), data)
	require.NoError(t, rc.Close())

	rc, err = fs.Open("foo")
	require.NoError(t, err)

	data, err = io.ReadAll(rc)
	require.NoError(t, err)
	require.Equal(t, []byte("foofoo"), data)
	require.NoError(t, rc.Close())
}

func TestWalkTarget(t *testing.T) {
	fs := NewFS()
	fs.Add("foo", &types.Stat{Mode: uint32(os.ModeDir | 0755)}, nil)
	fs.Add("foo/bar", &types.Stat{}, nil)
	fs.Add("foobar", &types.Stat{Mode: uint32(os.ModeDir | 0755)}, nil)
	fs.Add("foobar/baz", &types.Stat{}, nil)
	for _, tc := range []struct {
		target string
		want   []string
	}{
		{"", []string{"foo", "foo/bar", "foobar", "foobar/baz"}},
		{"/", []string{"foo", "foo/bar", "foobar", "foobar/baz"}},
		{"foo", []string{"foo", "foo/bar"}},
		{"/foo", []string{"foo", "foo/bar"}},
		{"foo/", []string{"foo", "foo/bar"}},
		{"foo/bar", []string{"foo/bar"}},
		{"./foo", []string{"foo", "foo/bar"}},
		{"foo//bar", []string{"foo/bar"}},
		{"../foo", []string{"foo", "foo/bar"}},
		{"missing", nil},
	} {
		t.Run(tc.target, func(t *testing.T) {
			var paths []string
			err := fs.Walk(t.Context(), tc.target, func(path string, _ iofs.DirEntry, err error) error {
				require.NoError(t, err)
				paths = append(paths, path)
				return nil
			})
			require.NoError(t, err)
			require.Equal(t, tc.want, paths)
		})
	}
}
