package fs

import (
	"bytes"
	"io"
	"os"
	"sort"
	"strings"
	"text/template"

	sr "tildegit.org/tjp/sliderule"
)

// RenderDirectoryListing provides an io.Reader with the output of a directory listing template.
//
// The template is provided the following namespace:
//   - .FullPath: the complete path to the listed directory
//   - .DirName: the name of the directory itself
//   - .Entries: the []fs.DirEntry of the directory contents
//   - .Hostname: the hostname of the server hosting the file
//   - .Port: the port on which the server is listening
//
// Each entry in .Entries has the following fields:
//   - .Name the string name of the item within the directory
//   - .IsDir is a boolean
//   - .Type is the FileMode bits
//   - .Info is a method returning (fs.FileInfo, error)
func RenderDirectoryListing(
	path string,
	requestpath string,
	template *template.Template,
	server sr.Server,
) (io.Reader, error) {
	buf := &bytes.Buffer{}

	environ, err := dirlistNamespace(path, requestpath, server)
	if err != nil {
		return nil, err
	}

	if err := template.Execute(buf, environ); err != nil {
		return nil, err
	}

	return buf, nil
}

func dirlistNamespace(path, requestpath string, server sr.Server) (map[string]any, error) {
	entries, err := os.ReadDir(path)
	if err != nil {
		return nil, err
	}

	for i := len(entries) - 1; i >= 0; i-- {
		if strings.HasPrefix(entries[i].Name(), ".") {
			copy(entries[i:], entries[i+1:])
			entries = entries[:len(entries)-1]
		}
	}

	sort.Slice(entries, func(i, j int) bool {
		return entries[i].Name() < entries[j].Name()
	})

	var dirname string
	if requestpath == "" {
		dirname = "(root)"
	} else {
		dirname = path[strings.LastIndex(path, "/")+1:]
	}

	hostname := "none"
	port := "0"
	if server != nil {
		hostname = server.Hostname()
		port = server.Port()
	}

	m := map[string]any{
		"FullPath": path,
		"DirName":  dirname,
		"Entries":  entries,
		"Hostname": hostname,
		"Port":     port,
	}

	return m, nil
}