package templates

import (
	"embed"
	"encoding/json"
	"path/filepath"
	"strings"
	"text/template"
)

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

// TemplateEngine provides template rendering functionality for Dockerfiles
type TemplateEngine struct {
	templates map[string]*template.Template
}

// NewTemplateEngine creates a new template engine with all built-in templates
func NewTemplateEngine() (*TemplateEngine, error) {
	te := &TemplateEngine{
		templates: make(map[string]*template.Template),
	}

	// Create template functions
	funcMap := template.FuncMap{
		"join": func(sep string, items []string) string {
			return strings.Join(items, sep)
		},
		"toJSON": func(v interface{}) string {
			b, _ := json.Marshal(v)
			return string(b)
		},
		"hasPrefix": func(prefix, str string) bool {
			return strings.HasPrefix(str, prefix)
		},
		"hasSuffix": func(suffix, str string) bool {
			return strings.HasSuffix(str, suffix)
		},
		"contains": func(substr, str string) bool {
			return strings.Contains(str, substr)
		},
		"lower": strings.ToLower,
		"upper": strings.ToUpper,
		"trim":  strings.TrimSpace,
	}

	// Load all template files
	entries, err := templateFS.ReadDir(".")
	if err != nil {
		return nil, err
	}

	for _, entry := range entries {
		if !entry.IsDir() && strings.HasSuffix(entry.Name(), ".tmpl") {
			name := strings.TrimSuffix(entry.Name(), ".tmpl")
			content, err := templateFS.ReadFile(entry.Name())
			if err != nil {
				return nil, err
			}

			tmpl, err := template.New(name).Funcs(funcMap).Parse(string(content))
			if err != nil {
				return nil, err
			}

			te.templates[name] = tmpl
		}
	}

	return te, nil
}

// Render renders a template with the given data
func (te *TemplateEngine) Render(templateName string, data interface{}) (string, error) {
	tmpl, exists := te.templates[templateName]
	if !exists {
		return "", &template.ExecError{
			Name: "template not found: " + templateName,
		}
	}

	var buf strings.Builder
	err := tmpl.Execute(&buf, data)
	if err != nil {
		return "", err
	}

	return buf.String(), nil
}

// GetTemplateNames returns all available template names
func (te *TemplateEngine) GetTemplateNames() []string {
	names := make([]string, 0, len(te.templates))
	for name := range te.templates {
		names = append(names, name)
	}
	return names
}

// RenderFromFile renders a template from an external file (for custom templates)
func (te *TemplateEngine) RenderFromFile(templatePath string, data interface{}) (string, error) {
	funcMap := template.FuncMap{
		"join": func(sep string, items []string) string {
			return strings.Join(items, sep)
		},
		"toJSON": func(v interface{}) string {
			b, _ := json.Marshal(v)
			return string(b)
		},
		"hasPrefix": func(prefix, str string) bool {
			return strings.HasPrefix(str, prefix)
		},
		"hasSuffix": func(suffix, str string) bool {
			return strings.HasSuffix(str, suffix)
		},
		"contains": func(substr, str string) bool {
			return strings.Contains(str, substr)
		},
		"lower": strings.ToLower,
		"upper": strings.ToUpper,
		"trim":  strings.TrimSpace,
	}

	name := filepath.Base(templatePath)
	tmpl, err := template.New(name).Funcs(funcMap).ParseFiles(templatePath)
	if err != nil {
		return "", err
	}

	var buf strings.Builder
	err = tmpl.Execute(&buf, data)
	if err != nil {
		return "", err
	}

	return buf.String(), nil
}
