package filesystem

import (
	"encoding/json"
	"os"
	"time"
)

type FS interface {
	ReadDir(path string) ([]os.FileInfo, error)
	Read(path string) ([]byte, error)
	Stat(path string) (os.FileInfo, error)
}

type FileInfo interface {
	os.FileInfo
	ContentType() string
	Path() string
}

// File implements FileInfo
type File struct {
	name        string
	size        int64
	mode        os.FileMode
	modTime     time.Time
	isDir       bool
	contentType string
	path        string
}

func (f File) Name() string        { return f.name }
func (f File) Size() int64         { return f.size }
func (f File) Mode() os.FileMode   { return f.mode }
func (f File) ModTime() time.Time  { return f.modTime }
func (f File) IsDir() bool         { return f.isDir }
func (f File) Sys() any            { return nil }
func (f File) ContentType() string { return f.contentType }
func (f File) Path() string        { return f.path }

type serializableFile struct {
	Name        string      `json:"name"`
	Size        int64       `json:"size"`
	Mode        os.FileMode `json:"mode"`
	ModTime     time.Time   `json:"modTime"`
	IsDir       bool        `json:"isDir"`
	ContentType string      `json:"contentType"`
	Path        string      `json:"path"`
}

// MarshalJSON implements json.Marshaler
func (f File) MarshalJSON() ([]byte, error) {
	// Create a local shadow struct with exported fields for the JSON package
	return json.Marshal(serializableFile{
		Name:        f.name,
		Size:        f.size,
		Mode:        f.mode,
		ModTime:     f.modTime,
		IsDir:       f.isDir,
		ContentType: f.contentType,
		Path:        f.path,
	})
}

// UnmarshalJSON implements json.Unmarshaler
func (f *File) UnmarshalJSON(data []byte) error {
	// Define a shadow struct to capture the incoming JSON
	shadow := serializableFile{}

	if err := json.Unmarshal(data, &shadow); err != nil {
		return err
	}

	// Transfer values to the actual struct
	f.name = shadow.Name
	f.size = shadow.Size
	f.mode = shadow.Mode
	f.modTime = shadow.ModTime
	f.isDir = shadow.IsDir
	f.contentType = shadow.ContentType
	f.path = shadow.Path
	return nil
}

func ParseToFile(file os.FileInfo) File {
	if fi, ok := file.(FileInfo); ok {
		return File{
			name:        fi.Name(),
			size:        fi.Size(),
			mode:        fi.Mode(),
			modTime:     fi.ModTime(),
			isDir:       fi.IsDir(),
			contentType: fi.ContentType(),
			path:        fi.Path(),
		}
	} else {
		// Fallback if underlying impl returns generic os.FileInfo
		return File{
			name:    file.Name(),
			size:    file.Size(),
			mode:    file.Mode(),
			modTime: file.ModTime(),
			isDir:   file.IsDir(),
		}
	}
}
