package pkg_test

import (
	"bytes"
	"io/fs"
	"reflect"
	"testing"
	"testing/fstest"

	"hakurei.app/internal/pkg"
)

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

	testCases := []struct {
		name    string
		fsys    fs.FS
		entries []pkg.FlatEntry
		sum     pkg.Checksum
		err     error
	}{
		{"bad type", fstest.MapFS{
			".":       {Mode: fs.ModeDir | 0700},
			"invalid": {Mode: fs.ModeCharDevice | 0400},
		}, nil, pkg.Checksum{}, pkg.InvalidFileModeError(
			fs.ModeCharDevice | 0400,
		)},

		{"coldboot", fstest.MapFS{
			".": {Mode: fs.ModeDir | 0700},

			"devices":        {Mode: fs.ModeDir | 0700},
			"devices/uevent": {Mode: 0600, Data: []byte("add")},
			"devices/empty":  {Mode: fs.ModeDir | 0700},

			"devices/sub":        {Mode: fs.ModeDir | 0700},
			"devices/sub/uevent": {Mode: 0600, Data: []byte("add")},

			"block":        {Mode: fs.ModeDir | 0700},
			"block/uevent": {Mode: 0600, Data: []byte{}},
		}, []pkg.FlatEntry{
			{Mode: fs.ModeDir | 0700, Path: "."},

			{Mode: fs.ModeDir | 0700, Path: "block"},
			{Mode: 0600, Path: "block/uevent", Data: []byte{}},

			{Mode: fs.ModeDir | 0700, Path: "devices"},
			{Mode: fs.ModeDir | 0700, Path: "devices/empty"},
			{Mode: fs.ModeDir | 0700, Path: "devices/sub"},
			{Mode: 0600, Path: "devices/sub/uevent", Data: []byte("add")},
			{Mode: 0600, Path: "devices/uevent", Data: []byte("add")},
		}, pkg.MustDecode("mEy_Lf5KotThm7OwMx7yTKZh5HCCyaB41pVAvI9uDMgVQFM91iosBLYsRm8bDsX8"), nil},

		{"empty", fstest.MapFS{
			".":          {Mode: fs.ModeDir | 0700},
			"checksum":   {Mode: fs.ModeDir | 0700},
			"identifier": {Mode: fs.ModeDir | 0700},
			"work":       {Mode: fs.ModeDir | 0700},
		}, []pkg.FlatEntry{
			{Mode: fs.ModeDir | 0700, Path: "."},
			{Mode: fs.ModeDir | 0700, Path: "checksum"},
			{Mode: fs.ModeDir | 0700, Path: "identifier"},
			{Mode: fs.ModeDir | 0700, Path: "work"},
		}, pkg.MustDecode("E4vEZKhCcL2gPZ2Tt59FS3lDng-d_2SKa2i5G_RbDfwGn6EemptFaGLPUDiOa94C"), nil},

		{"sample directory step garbage", fstest.MapFS{
			".": {Mode: fs.ModeDir | 0500},

			"lib":       {Mode: fs.ModeDir | 0500},
			"lib/check": {Mode: 0400, Data: []byte{}},

			"lib/pkgconfig": {Mode: fs.ModeDir | 0500},
		}, []pkg.FlatEntry{
			{Mode: fs.ModeDir | 0500, Path: "."},

			{Mode: fs.ModeDir | 0500, Path: "lib"},
			{Mode: 0400, Path: "lib/check", Data: []byte{}},

			{Mode: fs.ModeDir | 0500, Path: "lib/pkgconfig"},
		}, pkg.MustDecode("CUx-3hSbTWPsbMfDhgalG4Ni_GmR9TnVX8F99tY_P5GtkYvczg9RrF5zO0jX9XYT"), nil},
	}
	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			t.Parallel()

			t.Run("roundtrip", func(t *testing.T) {
				t.Parallel()

				var buf bytes.Buffer
				if _, err := pkg.Flatten(
					tc.fsys,
					".",
					&buf,
				); !reflect.DeepEqual(err, tc.err) {
					t.Fatalf("Flatten: error = %v, want %v", err, tc.err)
				} else if tc.err != nil {
					return
				}

				s := pkg.NewDirScanner(bytes.NewReader(buf.Bytes()), true)
				var got []pkg.FlatEntry
				for s.Scan() {
					got = append(got, *s.Entry())
				}
				if err := s.Err(); err != nil {
					t.Fatalf("Err: error = %v", err)
				}

				if !reflect.DeepEqual(got, tc.entries) {
					t.Fatalf("Scan: %#v, want %#v", got, tc.entries)
				}
			})

			if tc.err != nil {
				return
			}

			t.Run("hash", func(t *testing.T) {
				t.Parallel()

				var got pkg.Checksum
				if err := pkg.HashFS(&got, tc.fsys, "."); err != nil {
					t.Fatalf("HashFS: error = %v", err)
				} else if got != tc.sum {
					t.Fatalf("HashFS: %v", &pkg.ChecksumMismatchError{
						Got:  got,
						Want: tc.sum,
					})
				}
			})
		})
	}
}
