package httputils

import (
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"
	"text/template"
	"time"
)

type TemplateSet struct {
	templates *template.Template
	paths     []string
	loadTimes map[string]time.Time
}

func newTemplateSet(partials *TemplateSet, paths ...string) TemplateSet {
	var partialPaths []string
	if partials == nil {
		partialPaths = make([]string, 0)
	} else {
		partialPaths = partials.paths
	}
	allPaths := append(partialPaths, paths...)
	modTimes := make(map[string]time.Time)
	for _, path := range allPaths {
		fileInfo, _ := os.Stat(path)
		modTimes[path] = fileInfo.ModTime()
	}
	templates := template.Template{}
	templates.Funcs(DefaultTemplateFuncs)
	templates.ParseFiles(allPaths...)
	return TemplateSet{
		templates: &templates,
		paths:     allPaths,
		loadTimes: modTimes,
	}
}

func NewTemplateSet(paths ...string) TemplateSet {
	for i, path := range paths {
		paths[i] = fmt.Sprintf("%s/%s", templateFolder, path)
	}
	return newTemplateSet(&partials, paths...)
}

func (templateSet *TemplateSet) ExecuteTemplate(wr io.Writer, name string, data any) error {
	templateSet.reloadTemplatesIfModified()
	templateSet.templates.Funcs(TemplateFuncs)
	err := templateSet.templates.ExecuteTemplate(wr, name, data)
	TemplateFuncs = DefaultTemplateFuncs
	return err
}

func (templateSet *TemplateSet) reloadTemplatesIfModified() {
	for path, loadTime := range templateSet.loadTimes {
		fileInfo, _ := os.Stat(path)
		modTime := fileInfo.ModTime()
		if modTime.After(loadTime) {
			fmt.Printf("%s updated: reloading templates...\n", path)
			goto update
		}
	}
	return
update:
	now := time.Now()
	for _, path := range templateSet.paths {
		templateSet.templates.ParseFiles(path)
		templateSet.loadTimes[path] = now
	}
}

func getTemplatePathsInDirectory(directory string) (paths []string, err error) {
	paths = make([]string, 0)
	err = filepath.Walk(directory, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if !info.IsDir() && strings.HasSuffix(path, ".html") {
			paths = append(paths, path)
		}
		return nil
	})
	return
}

const templateFolder = "templates"
const partialsFolder = templateFolder + "/partials"

var paths, _ = getTemplatePathsInDirectory(partialsFolder)
var partials = newTemplateSet(nil, paths...)
var DefaultTemplateFuncs template.FuncMap
var TemplateFuncs template.FuncMap