package templateutil

import (
	"html/template"
	"io/fs"

	"source.toby3d.me/toby3d/home/internal/domain"
	"source.toby3d.me/toby3d/home/internal/templateutil/collections"
	"source.toby3d.me/toby3d/home/internal/templateutil/partials"
	"source.toby3d.me/toby3d/home/internal/templateutil/safe"
	"source.toby3d.me/toby3d/home/internal/templateutil/strings"
	"source.toby3d.me/toby3d/home/internal/templateutil/transform"
	"source.toby3d.me/toby3d/home/internal/templateutil/urls"
)

type Function struct {
	Handler func(v ...any) any
	Methods template.FuncMap
	Name    string
}

func New(partialsDir fs.FS, site *domain.Site) template.FuncMap {
	funcMap := make(template.FuncMap)
	funcs := make([]Function, 0)
	stringsNamespace := strings.New()
	funcs = append(funcs, Function{
		Name:    "strings",
		Handler: func(v ...any) any { return stringsNamespace },
		Methods: template.FuncMap{
			"hasPrefix": stringsNamespace.HasPrefix,
			"hasSuffix": stringsNamespace.HasSuffix,
		},
	})
	safeNamespace := safe.New()
	funcs = append(funcs, Function{
		Name:    "safe",
		Handler: func(v ...any) any { return safeNamespace },
		Methods: template.FuncMap{
			"safeHTML": safeNamespace.HTML,
		},
	})
	partialsNamespace := partials.New(partialsDir, funcMap)
	funcs = append(funcs, Function{
		Name:    "partials",
		Handler: func(v ...any) any { return partialsNamespace },
		Methods: template.FuncMap{
			"partial": partialsNamespace.Include,
		},
	})
	urlsNamespace := urls.New(site)
	funcs = append(funcs, Function{
		Name:    "urls",
		Handler: func(v ...any) any { return urlsNamespace },
		Methods: template.FuncMap{
			"absLangURL": urlsNamespace.AbsLangURL,
			"absURL":     urlsNamespace.AbsURL,
			"relLangURL": urlsNamespace.RelLangURL,
			"relURL":     urlsNamespace.RelURL,
		},
	})
	transformNamespace := transform.New()
	funcs = append(funcs, Function{
		Name:    "transform",
		Handler: func(v ...any) any { return transformNamespace },
		Methods: template.FuncMap{
			"markdownify": transformNamespace.Markdownify,
		},
	})
	collectionsNamespace := collections.New()
	funcs = append(funcs, Function{
		Name:    "collections",
		Handler: func(v ...any) any { return collectionsNamespace },
		Methods: template.FuncMap{
			"index": collectionsNamespace.Index,
			"seq":   collectionsNamespace.Seq,
			"slice": collectionsNamespace.Slice,
		},
	})

	for _, f := range funcs {
		funcMap[f.Name] = f.Handler

		for name, m := range f.Methods {
			funcMap[name] = m
		}
	}

	return funcMap
}
