package fsx

import (
	"errors"
	"io/fs"
	"os"
	"syscall"
	"testing"

	"github.com/ooni/probe-cli/v3/internal/atomicx"
)

// baseDir is the base directory we use for testing.
var baseDir = "./testdata/"

// failingStatFS is a fs.FS returning a file where stat() fails.
type failingStatFS struct {
	CloseCount *atomicx.Int64
}

// failingStatFile is a fs.File where stat() fails.
type failingStatFile struct {
	CloseCount *atomicx.Int64
}

// errStatFailed is the internal error indicating that stat() failed.
var errStatFailed = errors.New("stat failed")

// Stat is a stat implementation that fails.
func (failingStatFile) Stat() (os.FileInfo, error) {
	return nil, errStatFailed
}

// Open opens a fake file whose Stat fails.
func (f failingStatFS) Open(pathname string) (fs.File, error) {
	return failingStatFile(f), nil
}

// Close closes the failingStatFile.
func (fs failingStatFile) Close() error {
	if fs.CloseCount != nil {
		fs.CloseCount.Add(1)
	}
	return nil
}

// Read implements fs.File.Read.
func (failingStatFile) Read([]byte) (int, error) {
	return 0, errors.New("shouldn't be called")
}

func TestOpenWithFailingStat(t *testing.T) {
	count := &atomicx.Int64{}
	_, err := openWithFS(
		failingStatFS{CloseCount: count}, baseDir+"testfile.txt")
	if !errors.Is(err, errStatFailed) {
		t.Error("expected error with invalid FS", err)
	}
	if count.Load() != 1 {
		t.Error("expected close counter to be equal to 1")
	}
}

func TestOpenNonexistentFile(t *testing.T) {
	_, err := OpenFile(baseDir + "invalidtestfile.txt")
	if !errors.Is(err, syscall.ENOENT) {
		t.Errorf("not the error we expected")
	}
}

func TestOpenDirectoryShouldFail(t *testing.T) {
	_, err := OpenFile(baseDir)
	if !errors.Is(err, syscall.EISDIR) {
		t.Fatalf("not the error we expected: %+v", err)
	}
}

func TestOpeningExistingFileShouldWork(t *testing.T) {
	file, err := OpenFile(baseDir + "testfile.txt")
	if err != nil {
		t.Fatal(err)
	}
	defer file.Close()
}
