package pkg_test

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

	"hakurei.app/check"
	"hakurei.app/internal/pkg"
)

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

	testCases := []struct {
		name string
		a    pkg.Artifact
	}{
		{"http get aligned", pkg.NewHTTPGet(
			nil, "file:///testdata",
			pkg.Checksum(bytes.Repeat([]byte{0xfd}, len(pkg.Checksum{}))),
		)},
		{"http get unaligned", pkg.NewHTTPGet(
			nil, "https://hakurei.app",
			pkg.Checksum(bytes.Repeat([]byte{0xfc}, len(pkg.Checksum{}))),
		)},

		{"http get tar", pkg.NewTar(pkg.NewDecompress(pkg.NewHTTPGet(
			nil, "file:///testdata",
			pkg.Checksum(bytes.Repeat([]byte{0xff}, len(pkg.Checksum{}))),
		), pkg.Bzip2))},
		{"http get tar unaligned", pkg.NewTar(pkg.NewHTTPGet(
			nil, "https://hakurei.app",
			pkg.Checksum(bytes.Repeat([]byte{0xfe}, len(pkg.Checksum{}))),
		))},

		{"exec offline", pkg.NewExec(
			"exec-offline", "", nil, 0, false, false,
			pkg.AbsWork,
			[]string{"HAKUREI_TEST=1"},
			check.MustAbs("/opt/bin/testtool"),
			[]string{"testtool"},

			pkg.MustPath("/file", false, pkg.NewFile("file", []byte(
				"stub file",
			))), pkg.MustPath("/.hakurei", false, pkg.NewTar(pkg.NewHTTPGet(
				nil, "file:///hakurei.tar",
				pkg.Checksum(bytes.Repeat([]byte{0xfc}, len(pkg.Checksum{}))),
			))), pkg.MustPath("/opt", false, pkg.NewTar(pkg.NewDecompress(pkg.NewHTTPGet(
				nil, "file:///testtool.tar.gz",
				pkg.Checksum(bytes.Repeat([]byte{0xfc}, len(pkg.Checksum{}))),
			), pkg.Gzip))),
		)},

		{"exec net", pkg.NewExec(
			"exec-net", "",
			(*pkg.Checksum)(bytes.Repeat([]byte{0xfc}, len(pkg.Checksum{}))),
			0, false, false,
			pkg.AbsWork,
			[]string{"HAKUREI_TEST=1"},
			check.MustAbs("/opt/bin/testtool"),
			[]string{"testtool", "net"},

			pkg.MustPath("/file", false, pkg.NewFile("file", []byte(
				"stub file",
			))), pkg.MustPath("/.hakurei", false, pkg.NewTar(pkg.NewHTTPGet(
				nil, "file:///hakurei.tar",
				pkg.Checksum(bytes.Repeat([]byte{0xfc}, len(pkg.Checksum{}))),
			))), pkg.MustPath("/opt", false, pkg.NewTar(pkg.NewDecompress(pkg.NewHTTPGet(
				nil, "file:///testtool.tar.gz",
				pkg.Checksum(bytes.Repeat([]byte{0xfc}, len(pkg.Checksum{}))),
			), pkg.Gzip))),
		)},

		{"exec measured", pkg.NewExec(
			"exec-measured", "",
			(*pkg.Checksum)(bytes.Repeat([]byte{0xfd}, len(pkg.Checksum{}))),
			0, false, false,
			pkg.AbsWork,
			[]string{"HAKUREI_TEST=1"},
			check.MustAbs("/opt/bin/testtool"),
			[]string{"testtool", "measured"},

			pkg.MustPath("/file", false, pkg.NewFile("file", []byte(
				"stub file",
			))), pkg.MustPath("/.hakurei", false, pkg.NewTar(pkg.NewHTTPGet(
				nil, "file:///hakurei.tar",
				pkg.Checksum(bytes.Repeat([]byte{0xfd}, len(pkg.Checksum{}))),
			))), pkg.MustPath("/opt", false, pkg.NewTar(pkg.NewDecompress(pkg.NewHTTPGet(
				nil, "file:///testtool.tar.gz",
				pkg.Checksum(bytes.Repeat([]byte{0xfd}, len(pkg.Checksum{}))),
			), pkg.Gzip))),
		)},

		{"file anonymous", pkg.NewFile("", []byte{0})},
		{"file", pkg.NewFile("stub", []byte("stub"))},

		{"decompress", pkg.NewDecompress(pkg.NewFile("", []byte{0}), pkg.Bzip2)},

		{"archive", pkg.NewArchive(pkg.NewFile("", []byte{0}))},
	}
	testCasesCache := make([]cacheTestCase, len(testCases))
	for i, tc := range testCases {
		want := tc.a
		testCasesCache[i] = cacheTestCase{tc.name, 0, nil,
			func(t *testing.T, base *check.Absolute, c *pkg.Cache) {
				r, w := io.Pipe()

				done := make(chan error, 1)
				go func() {
					t.Helper()
					done <- c.EncodeAll(w, want)
					_ = w.Close()
				}()

				if got, err := c.NewDecoder(r).Decode(); err != nil {
					t.Fatalf("Decode: error = %v", err)
				} else if !reflect.DeepEqual(got, want) {
					t.Fatalf("Decode: %#v, want %#v", got, want)
				}

				if err := <-done; err != nil {
					t.Fatalf("EncodeAll: error = %v", err)
				}
			}, expectsFS{
				".":          {Mode: fs.ModeDir | 0700},
				"checksum":   {Mode: fs.ModeDir | 0700},
				"identifier": {Mode: fs.ModeDir | 0700},
				"substitute": {Mode: fs.ModeDir | 0700},
				"work":       {Mode: fs.ModeDir | 0700},
			},
		}
	}
	checkWithCache(t, testCasesCache)
}
