package mount

import (
	"testing"
	"time"

	"github.com/seaweedfs/seaweedfs/weed/util"
)

// The two indexes hold the same states, at the path the inode entry carries.
func checkDirIndexes(t *testing.T, itp *InodeToPath) {
	t.Helper()
	itp.RLock()
	defer itp.RUnlock()
	for path, d := range itp.dirPaths {
		if d.path != path {
			t.Errorf("dirPaths[%s] holds a state for %s", path, d.path)
		}
	}
	for inode, d := range itp.dirStates {
		e := itp.inode2path[inode]
		if e == nil || e.path == "" {
			continue
		}
		if e.path != d.path {
			t.Errorf("inode %d: entry at %s, directory state at %s", inode, e.path, d.path)
		}
		if itp.dirPaths[d.path] != d {
			t.Errorf("inode %d: directory state at %s is missing from dirPaths", inode, d.path)
		}
	}
}

func TestDirStateIndexesStayInStep(t *testing.T) {
	itp := NewInodeToPath(util.FullPath("/"), 0)
	now := time.Now().Unix()

	dirInode := itp.Lookup("/a", now, true, false, 0, true)
	subInode := itp.Lookup("/a/sub", now, true, false, 0, true)
	itp.Lookup("/a/sub/f.txt", now, false, false, 0, true)
	checkDirIndexes(t, itp)

	if itp.dirStateOf("/a/sub") == nil {
		t.Fatal("/a/sub not indexed as a directory")
	}
	if itp.dirStateOf("/a/sub/f.txt") != nil {
		t.Error("a file landed in the directory index")
	}

	itp.MovePath("/a/sub", "/a/moved")
	checkDirIndexes(t, itp)
	if itp.dirStateOf("/a/sub") != nil {
		t.Error("pre-rename directory still indexed")
	}
	if itp.dirStateOf("/a/moved") == nil {
		t.Error("renamed directory not indexed")
	}

	itp.RemovePath("/a/moved", nil)
	if itp.dirStateOf("/a/moved") != nil {
		t.Error("removed directory still indexed")
	}
	checkDirIndexes(t, itp)

	itp.Forget(subInode, 1, nil, nil)
	itp.Forget(dirInode, 1, nil, nil)
	checkDirIndexes(t, itp)
	if itp.dirStateOf("/a") != nil {
		t.Error("forgotten directory still indexed")
	}
}

// A released directory's state keeps its old path. Forgetting it must not take
// the index entry of whatever holds that name now.
func TestForgetDoesNotDropAReusedDirPath(t *testing.T) {
	itp := NewInodeToPath(util.FullPath("/"), 0)
	now := time.Now().Unix()

	oldInode := itp.Lookup("/a", now, true, false, 0, true)
	itp.RemovePath("/a", nil)
	newInode := itp.Lookup("/a", now+1, true, false, 0, true)
	if newInode == oldInode {
		t.Fatalf("recreated directory reused inode %d", newInode)
	}

	itp.Forget(oldInode, 1, nil, nil)

	if itp.dirStateOf("/a") == nil {
		t.Error("live directory lost its index when the released one was forgotten")
	}
	checkDirIndexes(t, itp)
}
