//go:build !windows
// +build !windows

package archiver

import (
	"os"
	"syscall"
	"testing"

	"github.com/restic/restic/internal/feature"
	"github.com/restic/restic/internal/fs"
	"github.com/restic/restic/internal/restic"
	rtest "github.com/restic/restic/internal/test"
)

type wrappedFileInfo struct {
	os.FileInfo
	sys  interface{}
	mode os.FileMode
}

func (fi wrappedFileInfo) Sys() interface{} {
	return fi.sys
}

func (fi wrappedFileInfo) Mode() os.FileMode {
	return fi.mode
}

// wrapFileInfo returns a new os.FileInfo with the mode, owner, and group fields changed.
func wrapFileInfo(fi os.FileInfo) os.FileInfo {
	// get the underlying stat_t and modify the values
	stat := fi.Sys().(*syscall.Stat_t)
	stat.Mode = mockFileInfoMode
	stat.Uid = mockFileInfoUID
	stat.Gid = mockFileInfoGID

	// wrap the os.FileInfo so we can return a modified stat_t
	res := wrappedFileInfo{
		FileInfo: fi,
		sys:      stat,
		mode:     mockFileInfoMode,
	}

	return res
}

// wrapIrregularFileInfo returns a new os.FileInfo with the mode changed to irregular file
func wrapIrregularFileInfo(fi os.FileInfo) os.FileInfo {
	// wrap the os.FileInfo so we can return a modified stat_t
	return wrappedFileInfo{
		FileInfo: fi,
		sys:      fi.Sys().(*syscall.Stat_t),
		mode:     (fi.Mode() &^ os.ModeType) | os.ModeIrregular,
	}
}

func statAndSnapshot(t *testing.T, repo archiverRepo, name string) (*restic.Node, *restic.Node) {
	fi := lstat(t, name)
	fs := &fs.Local{}
	want, err := fs.NodeFromFileInfo(name, fi, false)
	rtest.OK(t, err)

	_, node := snapshot(t, repo, fs, nil, name)
	return want, node
}

func TestHardlinkMetadata(t *testing.T) {
	defer feature.TestSetFlag(t, feature.Flag, feature.DeviceIDForHardlinks, true)()

	files := TestDir{
		"testfile": TestFile{
			Content: "foo bar test file",
		},
		"linktarget": TestFile{
			Content: "test file",
		},
		"testlink": TestHardlink{
			Target: "./linktarget",
		},
		"testdir": TestDir{},
	}

	tempdir, repo := prepareTempdirRepoSrc(t, files)

	back := rtest.Chdir(t, tempdir)
	defer back()

	want, node := statAndSnapshot(t, repo, "testlink")
	rtest.Assert(t, node.DeviceID == want.DeviceID, "device id mismatch expected %v got %v", want.DeviceID, node.DeviceID)
	rtest.Assert(t, node.Links == want.Links, "link count mismatch expected %v got %v", want.Links, node.Links)
	rtest.Assert(t, node.Inode == want.Inode, "inode mismatch expected %v got %v", want.Inode, node.Inode)

	_, node = statAndSnapshot(t, repo, "testfile")
	rtest.Assert(t, node.DeviceID == 0, "device id mismatch for testfile expected %v got %v", 0, node.DeviceID)

	_, node = statAndSnapshot(t, repo, "testdir")
	rtest.Assert(t, node.DeviceID == 0, "device id mismatch for testdir expected %v got %v", 0, node.DeviceID)
}