package vfstest

import (
	"os"
	"time"
)

// Oser defines the things that the "os" package can do
//
// This covers what the VFS can do also
type Oser interface {
	Chtimes(name string, atime time.Time, mtime time.Time) error
	Create(name string) (OsFiler, error)
	Mkdir(name string, perm os.FileMode) error
	Open(name string) (OsFiler, error)
	OpenFile(name string, flags int, perm os.FileMode) (fd OsFiler, err error)
	ReadDir(dirname string) ([]os.FileInfo, error)
	ReadFile(filename string) (b []byte, err error)
	Remove(name string) error
	Rename(oldName, newName string) error
	Stat(path string) (os.FileInfo, error)
}

// realOs is an implementation of Oser backed by the "os" package
type realOs struct {
}

// realOsFile is an implementation of vfs.Handle
type realOsFile struct {
	*os.File
}

// Flush
func (f realOsFile) Flush() error {
	return nil
}

// Release
func (f realOsFile) Release() error {
	return f.File.Close()
}

// Chtimes
func (r realOs) Chtimes(name string, atime time.Time, mtime time.Time) error {
	return os.Chtimes(name, atime, mtime)
}

// Create
func (r realOs) Create(name string) (OsFiler, error) {
	fd, err := os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
	if err != nil {
		return nil, err
	}
	return realOsFile{File: fd}, err
}

// Mkdir
func (r realOs) Mkdir(name string, perm os.FileMode) error {
	return os.Mkdir(name, perm)
}

// Open
func (r realOs) Open(name string) (OsFiler, error) {
	fd, err := os.Open(name)
	if err != nil {
		return nil, err
	}
	return realOsFile{File: fd}, err
}

// OpenFile
func (r realOs) OpenFile(name string, flags int, perm os.FileMode) (OsFiler, error) {
	fd, err := os.OpenFile(name, flags, perm)
	if err != nil {
		return nil, err
	}
	return realOsFile{File: fd}, err
}

// ReadDir
func (r realOs) ReadDir(dirname string) ([]os.FileInfo, error) {
	entries, err := os.ReadDir(dirname)
	if err != nil {
		return nil, err
	}
	infos := make([]os.FileInfo, 0, len(entries))
	for _, entry := range entries {
		info, err := entry.Info()
		if err != nil {
			return nil, err
		}
		infos = append(infos, info)
	}
	return infos, nil
}

// ReadFile
func (r realOs) ReadFile(filename string) (b []byte, err error) {
	return os.ReadFile(filename)
}

// Remove
func (r realOs) Remove(name string) error {
	return os.Remove(name)
}

// Rename
func (r realOs) Rename(oldName, newName string) error {
	return os.Rename(oldName, newName)
}

// Stat
func (r realOs) Stat(path string) (os.FileInfo, error) {
	return os.Stat(path)
}

// Check interfaces
var _ Oser = &realOs{}

// OsFiler is the methods on *os.File
type OsFiler interface {
	Chdir() error
	Chmod(mode os.FileMode) error
	Chown(uid, gid int) error
	Close() error
	Fd() uintptr
	Name() string
	Read(b []byte) (n int, err error)
	ReadAt(b []byte, off int64) (n int, err error)
	Readdir(n int) ([]os.FileInfo, error)
	Readdirnames(n int) (names []string, err error)
	Seek(offset int64, whence int) (ret int64, err error)
	Stat() (os.FileInfo, error)
	Sync() error
	Truncate(size int64) error
	Write(b []byte) (n int, err error)
	WriteAt(b []byte, off int64) (n int, err error)
	WriteString(s string) (n int, err error)
}
