package httpserver

import (
	"bytes"
	"fmt"
	"html/template"
	"net/http"
	"path/filepath"
	"strings"
)

type Renderer struct {
	templates map[string]*template.Template
}

func NewRenderer(templatesDir string) (*Renderer, error) {
	layouts, err := filepath.Glob(filepath.Join(templatesDir, "layouts", "*.gohtml"))
	if err != nil {
		return nil, fmt.Errorf("find layouts: %w", err)
	}

	if len(layouts) == 0 {
		return nil, fmt.Errorf("no layout templates found in %s", filepath.Join(templatesDir, "layouts"))
	}

	partials, err := filepath.Glob(filepath.Join(templatesDir, "partials", "*.gohtml"))
	if err != nil {
		return nil, fmt.Errorf("find partials: %w", err)
	}

	pages, err := filepath.Glob(filepath.Join(templatesDir, "pages", "*.gohtml"))
	if err != nil {
		return nil, fmt.Errorf("find pages: %w", err)
	}

	if len(pages) == 0 {
		return nil, fmt.Errorf("no page templates found in %s", filepath.Join(templatesDir, "pages"))
	}

	renderer := &Renderer{templates: make(map[string]*template.Template, len(pages))}

	for _, page := range pages {
		files := append([]string{}, layouts...)
		files = append(files, partials...)
		files = append(files, page)

		tmpl, err := template.ParseFiles(files...)
		if err != nil {
			return nil, fmt.Errorf("parse template set for %s: %w", page, err)
		}

		name := strings.TrimSuffix(filepath.Base(page), filepath.Ext(page))
		renderer.templates[name] = tmpl
	}

	return renderer, nil
}

func (r *Renderer) Render(w http.ResponseWriter, name string, status int, data any) error {
	tmpl, ok := r.templates[name]
	if !ok {
		return fmt.Errorf("unknown template %q", name)
	}

	var output bytes.Buffer
	if err := tmpl.ExecuteTemplate(&output, "base", data); err != nil {
		return err
	}

	w.Header().Set("Content-Type", "text/html; charset=utf-8")
	if status <= 0 {
		status = http.StatusOK
	}
	w.WriteHeader(status)

	_, err := output.WriteTo(w)
	return err
}
