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

package generic

import (
	"context"
	"fmt"
	"testing"

	"code.forgejo.org/f3/gof3/v3/id"
	"code.forgejo.org/f3/gof3/v3/kind"

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

func TestNodeInit(t *testing.T) {
	node := NewNode()
	assert.NotNil(t, node)
}

func TestNewNodeFromID(t *testing.T) {
	{
		i := "nodeID"
		node := NewNodeFromID(i)
		assert.Equal(t, id.NewNodeID(i), node.GetID())
	}
	{
		i := 123
		node := NewNodeFromID(i)
		assert.Equal(t, id.NewNodeID(fmt.Sprintf("%d", i)), node.GetID())
	}
}

func TestNodeIsNil(t *testing.T) {
	node := NewNode()
	assert.True(t, node.GetIsNil())

	node.SetIsNil(false)
	assert.False(t, node.GetIsNil())

	node.SetIsNil(true)
	assert.True(t, node.GetIsNil())

	var nilNode *Node
	assert.True(t, nilNode.GetIsNil())
}

func TestNodeEquals(t *testing.T) {
	ctx := context.Background()
	tree := newTestTree()
	one := tree.Factory(ctx, kindTestNodeLevelOne)
	one.(*testNodeLevelOne).v = 1
	assert.True(t, one.Equals(ctx, one))
	two := tree.Factory(ctx, kindTestNodeLevelOne)
	two.(*testNodeLevelOne).v = 2
	assert.False(t, one.Equals(ctx, two))
}

func TestNodeParent(t *testing.T) {
	node := NewNode()
	assert.True(t, node.GetParent().GetIsNil())
	parent := NewNode()
	node.SetParent(parent)
	assert.True(t, parent == node.GetParent())
}

func TestNodeKind(t *testing.T) {
	node := NewNode()
	assert.EqualValues(t, kind.KindNil, node.GetKind())
	kind := kind.Kind("something")
	node.SetKind(kind)
	assert.True(t, kind == node.GetKind())
}

func TestNodeMappedID(t *testing.T) {
	node := NewNode()
	node.SetDriver(NewNullDriver())
	assert.EqualValues(t, id.NilID, node.GetMappedID())
	mapped := id.NewNodeID("mapped")
	node.SetMappedID(mapped)
	assert.True(t, mapped == node.GetMappedID())
}

func TestNodeNewFormat(t *testing.T) {
	node := NewNode()
	node.SetDriver(NewNullDriver())

	assert.Panics(t, func() { node.NewFormat() })
}

func TestChildren(t *testing.T) {
	parent := NewNode()
	assert.Empty(t, parent.GetChildren())

	id1 := id.NewNodeID("one")
	child1 := NewNode()
	child1.SetID(id1)
	parent.SetChild(child1)

	id2 := id.NewNodeID("two")
	child2 := NewNode()
	child2.SetID(id2)
	parent.SetChild(child2)

	children := parent.GetChildren()
	assert.Len(t, children, 2)
	assert.Equal(t, children[0].GetID(), id1)
	assert.Equal(t, children[1].GetID(), id2)

	nodeChildren := parent.GetNodeChildren()
	assert.Len(t, nodeChildren, 2)
	delete(nodeChildren, id1)
	parent.SetChildren(nodeChildren)
	nodeChildren = parent.GetNodeChildren()
	assert.Len(t, nodeChildren, 1)
}

func TestNodeID(t *testing.T) {
	node := NewNode()
	assert.EqualValues(t, id.NilID, node.GetID())
	i := id.NewNodeID("1234")
	node.SetID(i)
	assert.True(t, i == node.GetID())
}

func TestNodeTree(t *testing.T) {
	node := NewNode()
	assert.Nil(t, node.GetTree())
	tree := NewTree(newTestOptions())
	node.SetTree(tree)
	assert.True(t, tree == node.GetTree())
}

type driverChildren struct {
	NullDriver
}

func (o *driverChildren) ListPage(cxt context.Context, page int) ChildrenSlice {
	node := o.GetNode()
	tree := node.GetTree()
	count := tree.GetPageSize()
	if page > 1 {
		count = 1
	}

	offset := (page - 1) * tree.GetPageSize()
	children := NewChildrenSlice(0)
	for i := 0; i < count; i++ {
		child := tree.GetSelf().Factory(cxt, kind.KindNil)
		child.SetID(id.NewNodeID(i + offset))
		children = append(children, child)
	}

	return children
}

type driverTreeChildren struct {
	NullTreeDriver
}

func (o *driverTreeChildren) Factory(ctx context.Context, kind kind.Kind) NodeDriverInterface {
	return &driverChildren{}
}

type treeChildren struct {
	Tree
}

func newTreeChildren() TreeInterface {
	tree := &treeChildren{}
	tree.Init(tree, newTestOptions())
	treeDriver := &driverTreeChildren{}
	treeDriver.Init()
	tree.SetDriver(treeDriver)
	tree.Register(kind.KindNil, func(ctx context.Context, kind kind.Kind) NodeInterface {
		node := NewNode()
		node.SetIsNil(false)
		return node
	})
	return tree
}

func TestNodeListPage(t *testing.T) {
	tree := newTreeChildren()

	ctx := context.Background()
	node := tree.Factory(ctx, kind.KindNil)

	children := node.ListPage(context.Background(), int(1))
	assert.NotNil(t, children)
	assert.EqualValues(t, tree.GetPageSize(), len(children))

	children = node.ListPage(context.Background(), int(2))
	assert.NotNil(t, children)
	assert.EqualValues(t, 1, len(children))
}

func TestNodeList(t *testing.T) {
	tree := newTreeChildren()

	ctx := context.Background()
	node := tree.Factory(ctx, kind.KindNil)
	children := node.List(ctx)
	assert.EqualValues(t, tree.GetPageSize()+1, len(children))

	idThree := id.NewNodeID("3")
	childThree := node.GetChild(idThree)
	assert.False(t, childThree.GetIsNil())
	assert.EqualValues(t, idThree, childThree.GetID())
	node.DeleteChild(idThree)
	childThree = node.GetChild(idThree)
	assert.True(t, childThree.GetIsNil())
}

type nodeGetIDFromName struct {
	Node
	name string
}

func (o *nodeGetIDFromName) GetIDFromName(ctx context.Context, name string) id.NodeID {
	for _, child := range o.GetChildren() {
		if child.(*nodeGetIDFromName).name == name {
			return child.GetID()
		}
	}
	return id.NilID
}

func newNodeGetIDFromName(i, name string) NodeInterface {
	node := &nodeGetIDFromName{}
	node.Init(node)
	node.SetIsNil(false)
	node.SetID(id.NewNodeID(i))
	node.name = name
	return node
}

func TestNodeGetIDFromName(t *testing.T) {
	i := "1243"
	name := "NAME"
	node := newNodeGetIDFromName(i, name)

	ctx := context.Background()
	parent := newNodeGetIDFromName("parent", "PARENT")
	parent.SetChild(node)

	r := parent.GetIDFromName(ctx, "OTHERNAME")
	assert.EqualValues(t, id.NilID, r)

	r = parent.GetIDFromName(ctx, name)
	assert.True(t, r == id.NewNodeID(i))
}

func TestNodePath(t *testing.T) {
	{
		path := NilNode.GetCurrentPath()
		assert.True(t, path.Empty())
		pathString := path.PathString()
		assert.True(t, pathString.Empty())
		assert.EqualValues(t, "", pathString.Join())
	}

	root := NewNode()
	root.SetIsNil(false)
	root.SetKind(kind.KindRoot)

	level1 := NewNode()
	level1.SetIsNil(false)
	level1.SetParent(root)
	id1 := id.NewNodeID("1")
	level1.SetID(id1)

	level2 := NewNode()
	level2.SetIsNil(false)
	level2.SetParent(level1)
	id2 := id.NewNodeID("2")
	level2.SetID(id2)

	{
		p := level2.GetCurrentPath()
		assert.False(t, p.Empty())
		if assert.EqualValues(t, 3, p.Length()) {
			assert.True(t, root == p.First(), p.First().(NodeInterface).GetID())
			rest := p.RemoveFirst()
			assert.True(t, level1 == rest.First(), p.First().(NodeInterface).GetID())
			rest = rest.RemoveFirst()
			assert.True(t, level2 == rest.First(), p.First().(NodeInterface).GetID())
		}
		pathString := p.PathString()
		assert.False(t, pathString.Empty())
		assert.EqualValues(t, 3, len(pathString.Elements()))
		assert.EqualValues(t, "/1/2", pathString.Join())
	}
}
