package zfs

import (
	"github.com/stretchr/testify/assert"
	"strconv"
	"strings"
	"testing"
	"time"
)

func fsvlist(fsv ...string) (r []FilesystemVersion) {

	r = make([]FilesystemVersion, len(fsv))
	for i, f := range fsv {

		// parse the id from fsvlist. it is used to derivce Guid,CreateTXG and Creation attrs
		split := strings.Split(f, ",")
		if len(split) != 2 {
			panic("invalid fsv spec")
		}
		id, err := strconv.Atoi(split[1])
		if err != nil {
			panic(err)
		}

		if strings.HasPrefix(f, "#") {
			r[i] = FilesystemVersion{
				Name:      strings.TrimPrefix(f, "#"),
				Type:      Bookmark,
				Guid:      uint64(id),
				CreateTXG: uint64(id),
				Creation:  time.Unix(0, 0).Add(time.Duration(id) * time.Second),
			}
		} else if strings.HasPrefix(f, "@") {
			r[i] = FilesystemVersion{
				Name:      strings.TrimPrefix(f, "@"),
				Type:      Snapshot,
				Guid:      uint64(id),
				CreateTXG: uint64(id),
				Creation:  time.Unix(0, 0).Add(time.Duration(id) * time.Second),
			}
		} else {
			panic("invalid character")
		}
	}
	return
}

func doTest(left, right []FilesystemVersion, validate func(d FilesystemDiff)) {
	var d FilesystemDiff
	d = MakeFilesystemDiff(left, right)
	validate(d)
}

func TestMakeFilesystemDiff_IncrementalSnapshots(t *testing.T) {

	l := fsvlist

	// basic functionality
	doTest(l("@a,1", "@b,2"), l("@a,1", "@b,2", "@c,3", "@d,4"), func(d FilesystemDiff) {
		assert.Equal(t, l("@b,2", "@c,3", "@d,4"), d.IncrementalPath)
	})

	// no common ancestor
	doTest(l(), l("@a,1"), func(d FilesystemDiff) {
		assert.Nil(t, d.IncrementalPath)
		assert.EqualValues(t, d.Conflict, ConflictNoCommonAncestor)
		assert.Equal(t, l("@a,1"), d.MRCAPathRight)
	})
	doTest(l("@a,1", "@b,2"), l("@c,3", "@d,4"), func(d FilesystemDiff) {
		assert.Nil(t, d.IncrementalPath)
		assert.EqualValues(t, d.Conflict, ConflictNoCommonAncestor)
		assert.Equal(t, l("@c,3", "@d,4"), d.MRCAPathRight)
	})

	// divergence is detected
	doTest(l("@a,1", "@b1,2"), l("@a,1", "@b2,3"), func(d FilesystemDiff) {
		assert.Nil(t, d.IncrementalPath)
		assert.EqualValues(t, d.Conflict, ConflictDiverged)
		assert.Equal(t, l("@a,1", "@b1,2"), d.MRCAPathLeft)
		assert.Equal(t, l("@a,1", "@b2,3"), d.MRCAPathRight)
	})

	// gaps before most recent common ancestor do not matter
	doTest(l("@a,1", "@b,2", "@c,3"), l("@a,1", "@c,3", "@d,4"), func(d FilesystemDiff) {
		assert.Equal(t, l("@c,3", "@d,4"), d.IncrementalPath)
	})

}

func TestMakeFilesystemDiff_BookmarksSupport(t *testing.T) {
	l := fsvlist

	// bookmarks are used
	doTest(l("@a,1"), l("#a,1", "@b,2"), func(d FilesystemDiff) {
		assert.Equal(t, l("#a,1", "@b,2"), d.IncrementalPath)
	})

	// boomarks are stripped from IncrementalPath (cannot send incrementally)
	doTest(l("@a,1"), l("#a,1", "#b,2", "@c,3"), func(d FilesystemDiff) {
		assert.Equal(t, l("#a,1", "@c,3"), d.IncrementalPath)
	})

	// test that snapshots are preferred over bookmarks in IncrementalPath
	doTest(l("@a,1"), l("#a,1", "@a,1", "@b,2"), func(d FilesystemDiff) {
		assert.Equal(t, l("@a,1", "@b,2"), d.IncrementalPath)
	})
	doTest(l("@a,1"), l("@a,1", "#a,1", "@b,2"), func(d FilesystemDiff) {
		assert.Equal(t, l("@a,1", "@b,2"), d.IncrementalPath)
	})

}