package renderizacao

import (
	"bytes"
	"fmt"
	"os"
	"path/filepath"
	"sync"
	"text/template"
)

type Motor struct {
	modelosDir string
	cache      sync.Map
}

func NovoMotor(modelosDir string) *Motor {
	return &Motor{modelosDir: modelosDir}
}

func (m *Motor) Renderizar(nomeModelo string, dados any) ([]byte, error) {
	tplPath := filepath.Join(m.modelosDir, "portais", nomeModelo+".xml.tmpl")
	tpl, err := m.carregarTemplate(tplPath)
	if err != nil {
		return nil, err
	}
	var buf bytes.Buffer
	if err := tpl.Execute(&buf, dados); err != nil {
		return nil, fmt.Errorf("render %s: %w", nomeModelo, err)
	}
	return buf.Bytes(), nil
}

func (m *Motor) carregarTemplate(path string) (*template.Template, error) {
	if v, ok := m.cache.Load(path); ok {
		return v.(*template.Template), nil
	}
	funcMap := template.FuncMap{
		"raw": func(s string) string { return s },
	}
	tpl, err := template.New(filepath.Base(path)).Funcs(funcMap).ParseFiles(path)
	if err != nil {
		return nil, err
	}
	m.cache.Store(path, tpl)
	return tpl, nil
}

func GravarAtomico(dir, hash string, conteudo []byte) (string, error) {
	dest := filepath.Join(dir, hash+".xml")
	tmp := dest + ".tmp"
	if err := os.WriteFile(tmp, conteudo, 0o644); err != nil {
		return "", err
	}
	if err := os.Rename(tmp, dest); err != nil {
		_ = os.Remove(tmp)
		return "", err
	}
	return dest, nil
}
