package ssr

import (
	"errors"
	"html/template"
	"io"
	"sync"

	"github.com/cespare/xxhash/v2"
)

type Writer struct {
	err   error
	w     io.Writer
	cache *TmplCache
}

func NewWriter(w io.Writer, cache *TmplCache) *Writer {
	return &Writer{w: w, cache: cache}
}

func (w *Writer) Raw(s string) error {
	_, err := w.w.Write([]byte(s))
	w.err = errors.Join(w.err, err)
	return w.err
}

func (w *Writer) Tmpl(data any, tmpl string) error {
	t, err := w.cache.GetOrCompile(tmpl)
	if err != nil {
		w.err = errors.Join(w.err, err)
		return w.err
	}

	err = t.Execute(w.w, data)
	w.err = errors.Join(w.err, err)
	return w.err
}

func (w *Writer) Error() error {
	return w.err
}

type TmplCache struct {
	funcs template.FuncMap
	cache sync.Map // uint64 -> *template.Template
}

func NewTmplCache(funcs template.FuncMap) *TmplCache {
	return &TmplCache{funcs: funcs}
}

func (t *TmplCache) GetOrCompile(content string) (*template.Template, error) {
	key := xxhash.Sum64String(content)

	if cached, ok := t.cache.Load(key); ok {
		return cached.(*template.Template), nil
	}

	tmpl, err := template.New("").Funcs(t.funcs).Parse(content)
	if err != nil {
		return nil, err
	}

	t.cache.Store(key, tmpl)
	return tmpl, nil
}

type Renderable interface {
	Render(sw *Writer) error
}
