package vfs

import (
	"io/fs"
	"os"
	"path/filepath"
)

type osPath struct {
	path string

	openFn     func(name string) (fs.File, error)
	statFn     func(name string) (fs.FileInfo, error)
	readDirFn  func(name string) ([]fs.DirEntry, error)
	readFileFn func(name string) ([]byte, error)
}

func New(name string) (Path, error) {
	abs, err := filepath.Abs(name)
	if err != nil {
		return nil, err
	}

	return newOsPath(abs), nil
}

func MustNew(name string) Path {
	p, err := New(name)
	if err != nil {
		panic(err)
	}

	return p
}

func newOsPath(name string) Path {
	if !filepath.IsAbs(name) {
		panic("vfs: abs path must be absolute")
	}

	// [os.DirFS] implements all required interfaces.
	// We used type assertion below to get the underlying types.
	dirfs := os.DirFS(name)

	return &osPath{
		path: name,

		openFn:     dirfs.Open,
		statFn:     dirfs.(fs.StatFS).Stat,
		readDirFn:  dirfs.(fs.ReadDirFS).ReadDir,
		readFileFn: dirfs.(fs.ReadFileFS).ReadFile,
	}
}

func (o osPath) Open(name string) (fs.File, error) {
	return o.openFn(name)
}

func (o osPath) Stat(name string) (fs.FileInfo, error) {
	return o.statFn(name)
}

func (o osPath) ReadDir(name string) ([]fs.DirEntry, error) {
	return o.readDirFn(name)
}

func (o osPath) ReadFile(name string) ([]byte, error) {
	return o.readFileFn(name)
}

func (o osPath) Parent() Path {
	dir := filepath.Dir(o.path)
	if dir == o.path {
		return nil
	}

	return newOsPath(dir)
}

func (o osPath) Native() string {
	return o.path
}