// Copyright Earl Warren <contact@earl-warren.org>
// Copyright Loïc Dachary <loic@dachary.org>
// SPDX-License-Identifier: MIT

package generic

import (
	"context"
	"testing"

	"code.forgejo.org/f3/gof3/v3/path"
	"code.forgejo.org/f3/gof3/v3/tree/generic"
	"code.forgejo.org/f3/gof3/v3/tree/memory"

	"github.com/stretchr/testify/assert"
)

type testReference struct {
	originPath           string
	originReference      string
	destinationPath      string
	destinationReference string
}

func TestMirror(t *testing.T) {
	ctx := context.Background()

	for _, testCase := range []struct {
		start      string
		references []testReference
		expected   []string
	}{
		{
			start:    "/",
			expected: []string{":ROOT:", "/D-A:content O-A:", "/D-A/D-B:content O-B:", "/D-A/D-B/D-C:content O-C:", "/D-A/D-B/D-D:content O-D:", "/D-A/D-B/D-E:content O-E:", "/D-A/D-F:content O-F:", "/D-A/D-F/D-G:content O-G:", "/D-A/D-F/D-H:content O-H:", "/D-A/D-F/D-I:content O-I:", "/D-A/D-J:content O-J:", "/D-A/D-J/D-K:content O-K:", "/D-A/D-J/D-L:content O-L:", "/D-A/D-J/D-M:content O-M:"},
		},
		{
			start: "/O-A/O-B",
			references: []testReference{
				{
					originPath:           "/O-A/O-B/O-C",
					originReference:      "/O-A/O-F",
					destinationPath:      "/D-A/D-B/D-D",
					destinationReference: "/D-A/D-C",
				},
			},
			expected: []string{":ROOT:", "/D-A:content O-A:", "/D-A/D-B:content O-B:", "/D-A/D-B/D-D:content O-C:/D-A/D-C", "/D-A/D-B/D-E:content O-D:", "/D-A/D-B/D-F:content O-E:", "/D-A/D-C:content O-F:"},
		},
		{
			start: "/O-A/O-F",
			references: []testReference{
				{
					originPath:           "/O-A/O-F/O-G",
					originReference:      "../../O-J",
					destinationPath:      "/D-A/D-B/D-D",
					destinationReference: "../../D-C",
				},
			},
			expected: []string{":ROOT:", "/D-A:content O-A:", "/D-A/D-B:content O-F:", "/D-A/D-B/D-D:content O-G:../../D-C", "/D-A/D-B/D-E:content O-H:", "/D-A/D-B/D-F:content O-I:", "/D-A/D-C:content O-J:"},
		},
	} {
		t.Run(" "+testCase.start, func(t *testing.T) {
			originTree := NewMemoryTree(ctx, "O")
			log := originTree.GetLogger()
			log.Trace("=========== build")
			testTreeBuild(t, originTree, 2)

			for _, c := range testCase.references {
				log.Trace("=========== inject reference %s", c.originReference)
				assert.True(t, originTree.Apply(ctx, generic.NewPathFromString(c.originPath), generic.NewApplyOptions(func(ctx context.Context, parent, path path.Path, node generic.NodeInterface) {
					memory.SetRef(node, c.originReference)
				})))
			}

			log.Trace("=========== mirror")
			destinationTree := NewMemoryTree(ctx, "D")

			generic.TreeMirror(ctx, originTree, destinationTree, generic.NewPathFromString(testCase.start), generic.NewMirrorOptions())
			log.Trace("=========== verify")
			collected := make([]string, 0, 10)
			collect := func(ctx context.Context, parent path.Path, node generic.NodeInterface) {
				collected = append(collected, node.GetCurrentPath().String()+":"+memory.GetContent(node)+":"+memory.GetRef(node))
			}
			destinationTree.Walk(ctx, generic.NewWalkOptions(collect))
			assert.EqualValues(t, testCase.expected, collected)

			for _, c := range testCase.references {
				log.Trace("=========== look for reference %s", c.destinationReference)
				var called bool
				assert.True(t, destinationTree.Apply(ctx, generic.NewPathFromString(c.destinationPath), generic.NewApplyOptions(func(ctx context.Context, parent, path path.Path, node generic.NodeInterface) {
					assert.EqualValues(t, c.destinationReference, memory.GetRef(node))
					called = true
				})))
				assert.True(t, called)
			}
		})
	}
}
