package backup_test

import (
	"testing"

	"github.com/owncloud/ocis/v2/ocis/pkg/backup"
	"github.com/test-go/testify/require"
)

func TestGatherData(t *testing.T) {
	testcases := []struct {
		Name     string
		Events   []interface{}
		Expected *backup.Consistency
	}{
		{
			Name: "no symlinks - no blobs",
			Events: []interface{}{
				nodeData("nodepath", "blobpath", true),
			},
			Expected: consistency(func(c *backup.Consistency) {
				node(c, "nodepath", backup.InconsistencySymlinkMissing)
				blobReference(c, "blobpath", backup.InconsistencyBlobMissing)
			}),
		},
		{
			Name: "symlink not required - no blobs",
			Events: []interface{}{
				nodeData("nodepath", "blobpath", false),
			},
			Expected: consistency(func(c *backup.Consistency) {
				blobReference(c, "blobpath", backup.InconsistencyBlobMissing)
			}),
		},
		{
			Name: "no inconsistencies",
			Events: []interface{}{
				nodeData("nodepath", "blobpath", true),
				linkData("linkpath", "nodepath"),
				blobData("blobpath"),
			},
			Expected: consistency(func(c *backup.Consistency) {
			}),
		},
		{
			Name: "orphaned blob",
			Events: []interface{}{
				nodeData("nodepath", "blobpath", true),
				linkData("linkpath", "nodepath"),
				blobData("blobpath"),
				blobData("anotherpath"),
			},
			Expected: consistency(func(c *backup.Consistency) {
				blob(c, "anotherpath", backup.InconsistencyBlobOrphaned)
			}),
		},
		{
			Name: "missing node",
			Events: []interface{}{
				linkData("linkpath", "nodepath"),
				blobData("blobpath"),
			},
			Expected: consistency(func(c *backup.Consistency) {
				linkedNode(c, "nodepath", backup.InconsistencyNodeMissing)
				blob(c, "blobpath", backup.InconsistencyBlobOrphaned)
			}),
		},
		{
			Name: "corrupt metadata",
			Events: []interface{}{
				nodeData("nodepath", "blobpath", true, backup.InconsistencyMetadataMissing),
				linkData("linkpath", "nodepath"),
				blobData("blobpath"),
			},
			Expected: consistency(func(c *backup.Consistency) {
				node(c, "nodepath", backup.InconsistencyMetadataMissing)
			}),
		},
		{
			Name: "corrupt metadata, no blob",
			Events: []interface{}{
				nodeData("nodepath", "blobpath", true, backup.InconsistencyMetadataMissing),
				linkData("linkpath", "nodepath"),
			},
			Expected: consistency(func(c *backup.Consistency) {
				node(c, "nodepath", backup.InconsistencyMetadataMissing)
				blobReference(c, "blobpath", backup.InconsistencyBlobMissing)
			}),
		},
	}

	for _, tc := range testcases {
		events := make(chan interface{})

		go func() {
			for _, ev := range tc.Events {
				switch e := ev.(type) {
				case backup.NodeData:
					events <- e
				case backup.LinkData:
					events <- e
				case backup.BlobData:
					events <- e
				}
			}
			close(events)
		}()

		c := backup.NewConsistency()
		c.GatherData(events)

		require.Equal(t, tc.Expected.Nodes, c.Nodes)
		require.Equal(t, tc.Expected.LinkedNodes, c.LinkedNodes)
		require.Equal(t, tc.Expected.Blobs, c.Blobs)
		require.Equal(t, tc.Expected.BlobReferences, c.BlobReferences)
	}

}

func nodeData(nodePath, blobPath string, requiresSymlink bool, incs ...backup.Inconsistency) backup.NodeData {
	return backup.NodeData{
		NodePath:        nodePath,
		BlobPath:        blobPath,
		RequiresSymlink: requiresSymlink,
		Inconsistencies: incs,
	}
}

func linkData(linkPath, nodePath string) backup.LinkData {
	return backup.LinkData{
		LinkPath: linkPath,
		NodePath: nodePath,
	}
}

func blobData(blobPath string) backup.BlobData {
	return backup.BlobData{
		BlobPath: blobPath,
	}
}

func consistency(f func(*backup.Consistency)) *backup.Consistency {
	c := backup.NewConsistency()
	f(c)
	return c
}

func node(c *backup.Consistency, path string, inc ...backup.Inconsistency) {
	c.Nodes[path] = inc
}

func linkedNode(c *backup.Consistency, path string, inc ...backup.Inconsistency) {
	c.LinkedNodes[path] = inc
}

func blob(c *backup.Consistency, path string, inc ...backup.Inconsistency) {
	c.Blobs[path] = inc
}

func blobReference(c *backup.Consistency, path string, inc ...backup.Inconsistency) {
	c.BlobReferences[path] = inc
}
