package vfs

import (
	"io/fs"
	"time"
)

// ModTimeFS wraps an fs.FS and overrides all file mtimes with a fixed time.
type ModTimeFS struct {
	fs.FS
	Time time.Time
}

// Open overrides the FS.Open method to wrap returned files.
func (m ModTimeFS) Open(name string) (fs.File, error) {
	f, err := m.FS.Open(name)
	if err != nil {
		return nil, err
	}
	return &modTimeFile{File: f, Time: m.Time}, nil
}

// ReadDir implements fs.ReadDirFS if the underlying FS supports it.
func (m ModTimeFS) ReadDir(name string) ([]fs.DirEntry, error) {
	readDirFS, ok := m.FS.(fs.ReadDirFS)
	if !ok {
		return nil, &fs.PathError{Op: "ReadDir", Path: name, Err: fs.ErrInvalid}
	}

	entries, err := readDirFS.ReadDir(name)
	if err != nil {
		return nil, err
	}

	wrapped := make([]fs.DirEntry, len(entries))
	for i, entry := range entries {
		wrapped[i] = modTimeDirEntry{DirEntry: entry, Time: m.Time}
	}
	return wrapped, nil
}

// modTimeFile wraps fs.File to override Stat().ModTime().
type modTimeFile struct {
	fs.File
	Time time.Time
}

func (f *modTimeFile) Stat() (fs.FileInfo, error) {
	info, err := f.File.Stat()
	if err != nil {
		return nil, err
	}
	return modTimeFileInfo{FileInfo: info, Time: f.Time}, nil
}

// modTimeFileInfo overrides ModTime to return a fixed time.
type modTimeFileInfo struct {
	fs.FileInfo
	Time time.Time
}

func (fi modTimeFileInfo) ModTime() time.Time {
	return fi.Time
}

func (fi modTimeFileInfo) Uname() (string, error) {
	return "root", nil
}

func (fi modTimeFileInfo) Gname() (string, error) {
	return "root", nil
}

func (fi modTimeFileInfo) Sys() any {
	return nil
}

// modTimeDirEntry wraps fs.DirEntry to override Info().ModTime().
type modTimeDirEntry struct {
	fs.DirEntry
	Time time.Time
}

func (d modTimeDirEntry) Info() (fs.FileInfo, error) {
	info, err := d.DirEntry.Info()
	if err != nil {
		return nil, err
	}
	return modTimeFileInfo{FileInfo: info, Time: d.Time}, nil
}
