package fileutils

import (
	"os"
	"path"
	"runtime"
	"syscall"
	"testing"

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

func TestExist(t *testing.T) {
	tempDir := t.TempDir()

	symlinkPath := path.Join(tempDir, "sl-working")
	danglingSymlinkPath := path.Join(tempDir, "sl-broken")

	err := os.Symlink(tempDir, symlinkPath)
	require.NoError(t, err)

	err = os.Symlink("fooobar123", danglingSymlinkPath)
	require.NoError(t, err)

	assertSameError := func(err1, err2 error, description string) {
		assert.Equal(t, err1 == nil, err2 == nil, description+": only one error is set")
		if err1 == nil {
			return
		}

		var pathErr1 *os.PathError
		var pathErr2 *os.PathError
		assert.ErrorAs(t, err1, &pathErr1, description+": wrong error type")
		assert.ErrorAs(t, err2, &pathErr2, description+": wrong error type")
		assert.Equal(t, pathErr1.Path, pathErr1.Path, description+": different file path")

		// on Linux validates that the syscall error is the same
		if runtime.GOOS == "linux" {
			var syscallErr1 syscall.Errno
			var syscallErr2 syscall.Errno
			assert.ErrorAs(t, err1, &syscallErr1, description+": wrong error type")
			assert.ErrorAs(t, err2, &syscallErr2, description+": wrong error type")
			assert.Equal(t, syscallErr1, syscallErr2, description+": same error for existing path (follow=false)")
		}
	}

	err = Lexists(tempDir)
	_, err2 := os.Lstat(tempDir)
	assertSameError(err, err2, "same error for existing path (follow=false)")

	err = Lexists("foo123shouldnotexist")
	_, err2 = os.Lstat("foo123shouldnotexist")
	assertSameError(err, err2, "same error for not existing path (follow=false)")

	err = Lexists(symlinkPath)
	_, err2 = os.Lstat(symlinkPath)
	assertSameError(err, err2, "same error for existing symlink (follow=false)")

	err = Exists(symlinkPath)
	_, err2 = os.Stat(symlinkPath)
	assertSameError(err, err2, "same error for existing symlink (follow=true)")

	err = Lexists(danglingSymlinkPath)
	_, err2 = os.Lstat(danglingSymlinkPath)
	assertSameError(err, err2, "same error for not existing symlink (follow=false)")

	err = Exists(danglingSymlinkPath)
	_, err2 = os.Stat(danglingSymlinkPath)
	assertSameError(err, err2, "same error for not existing symlink (follow=true)")
}

func BenchmarkExists(b *testing.B) {
	tempDir := b.TempDir()
	for range b.N {
		_ = Exists(tempDir)
		_ = Lexists(tempDir)
	}
}

func BenchmarkStat(b *testing.B) {
	tempDir := b.TempDir()
	for range b.N {
		_, _ = os.Stat(tempDir)
		_, _ = os.Lstat(tempDir)
	}
}
