package web

import (
	"embed"
	"html/template"
	"io"
	"io/fs"
	"path/filepath"
	"strings"
)

//go:embed templates/*
var templateFS embed.FS

// Templates holds the parsed templates
type Templates struct {
	templates map[string]*template.Template
}

// Template helper functions
var templateFuncs = template.FuncMap{
	"divFloat": func(a int64, b int) float64 {
		return float64(a) / float64(b)
	},
	"add": func(a, b int) int {
		return a + b
	},
	"sub": func(a, b int) int {
		return a - b
	},
	"mul": func(a, b int) int {
		return a * b
	},
	"min": func(a, b int) int {
		if a < b {
			return a
		}
		return b
	},
}

// NewTemplates creates and parses all templates
func NewTemplates() (*Templates, error) {
	t := &Templates{
		templates: make(map[string]*template.Template),
	}

	// Load all layout and partial templates first
	layoutFiles, err := fs.Glob(templateFS, "templates/layouts/*.html")
	if err != nil {
		return nil, err
	}
	partialFiles, err := fs.Glob(templateFS, "templates/partials/*.html")
	if err != nil {
		return nil, err
	}

	// Load page templates
	pageFiles, err := fs.Glob(templateFS, "templates/pages/*.html")
	if err != nil {
		return nil, err
	}

	// Combine layouts and partials
	baseFiles := append(layoutFiles, partialFiles...)

	// Parse each page template with layouts and partials
	for _, pageFile := range pageFiles {
		files := append([]string{pageFile}, baseFiles...)

		// Read and parse all files
		tmpl := template.New(filepath.Base(pageFile)).Funcs(templateFuncs)
		for _, file := range files {
			content, err := templateFS.ReadFile(file)
			if err != nil {
				return nil, err
			}
			_, err = tmpl.Parse(string(content))
			if err != nil {
				return nil, err
			}
		}

		// Extract page name (e.g., "login" from "templates/pages/login.html")
		name := strings.TrimSuffix(filepath.Base(pageFile), ".html")
		t.templates[name] = tmpl
	}

	// Also parse partials standalone for htmx partial responses
	for _, partialFile := range partialFiles {
		content, err := templateFS.ReadFile(partialFile)
		if err != nil {
			return nil, err
		}
		tmpl, err := template.New(filepath.Base(partialFile)).Funcs(templateFuncs).Parse(string(content))
		if err != nil {
			return nil, err
		}
		name := "partial:" + strings.TrimSuffix(filepath.Base(partialFile), ".html")
		t.templates[name] = tmpl
	}

	return t, nil
}

// RenderPage renders a full page template
func (t *Templates) RenderPage(w io.Writer, name string, data interface{}) error {
	tmpl, ok := t.templates[name]
	if !ok {
		return &ErrTemplateNotFound{Name: name}
	}
	return tmpl.ExecuteTemplate(w, "base", data)
}

// RenderPartial renders a partial template (for htmx responses)
func (t *Templates) RenderPartial(w io.Writer, name string, data interface{}) error {
	tmpl, ok := t.templates["partial:"+name]
	if !ok {
		return &ErrTemplateNotFound{Name: name}
	}
	return tmpl.ExecuteTemplate(w, name, data)
}

// ErrTemplateNotFound is returned when a template is not found
type ErrTemplateNotFound struct {
	Name string
}

func (e *ErrTemplateNotFound) Error() string {
	return "template not found: " + e.Name
}
