package filesystem

import (
	"fmt"
	"html"
	"io/fs"
	"path"
	"path/filepath"
	"sort"
	"strings"

	"github.com/gofiber/fiber/v3"
)

func getFileExtension(p string) string {
	n := strings.LastIndexByte(p, '.')
	if n < 0 {
		return ""
	}
	return p[n:]
}

func dirList(c fiber.Ctx, f fs.File) error {
	ff, ok := f.(fs.ReadDirFile)
	if !ok {
		return fmt.Errorf("failed to type-assert to fs.ReadDirFile")
	}
	fileinfos, err := ff.ReadDir(-1)
	if err != nil {
		return fmt.Errorf("failed to read dir: %w", err)
	}

	fm := make(map[string]fs.FileInfo, len(fileinfos))
	filenames := make([]string, 0, len(fileinfos))
	for _, fi := range fileinfos {
		name := fi.Name()
		info, err := fi.Info()
		if err != nil {
			return fmt.Errorf("failed to get file info: %w", err)
		}

		fm[name] = info
		filenames = append(filenames, name)
	}

	basePathEscaped := html.EscapeString(c.Path())
	_, _ = fmt.Fprintf(c, "<html><head><title>%s</title><style>.dir { font-weight: bold }</style></head><body>", basePathEscaped)
	_, _ = fmt.Fprintf(c, "<h1>%s</h1>", basePathEscaped)
	_, _ = fmt.Fprint(c, "<ul>")

	if len(basePathEscaped) > 1 {
		parentPathEscaped := html.EscapeString(strings.TrimRight(c.Path(), "/") + "/..")
		_, _ = fmt.Fprintf(c, `<li><a href="%s" class="dir">..</a></li>`, parentPathEscaped)
	}

	sort.Strings(filenames)
	for _, name := range filenames {
		pathEscaped := html.EscapeString(path.Join(c.Path() + "/" + name))
		fi := fm[name]
		auxStr := "dir"
		className := "dir"
		if !fi.IsDir() {
			auxStr = fmt.Sprintf("file, %d bytes", fi.Size())
			className = "file"
		}
		_, _ = fmt.Fprintf(c, `<li><a href="%s" class="%s">%s</a>, %s, last modified %s</li>`,
			pathEscaped, className, html.EscapeString(name), auxStr, fi.ModTime())
	}
	_, _ = fmt.Fprint(c, "</ul></body></html>")

	c.Type("html")

	return nil
}

func openFile(filesystem fs.FS, name string) (fs.File, error) {
	name = filepath.ToSlash(name)

	file, err := filesystem.Open(name)
	if err != nil {
		return nil, fmt.Errorf("failed to open file: %w", err)
	}

	return file, nil
}