package engine

import (
	"bytes"
	"embed"
	"fmt"
	"os"
	"path/filepath"
	"text/template"

	"github.com/sirupsen/logrus"
)

// Engine handles template rendering
type Engine struct {
	logger        *logrus.Logger
	templates     embed.FS
	templateCache map[string]*template.Template
}

// NewEngine creates a new template engine
func NewEngine(logger *logrus.Logger, templates embed.FS) *Engine {
	return &Engine{
		logger:        logger,
		templates:     templates,
		templateCache: make(map[string]*template.Template),
	}
}

// RenderFile renders a template file with the given data and writes to output path
func (e *Engine) RenderFile(templateName string, data interface{}, outputPath string) error {
	// Render template to string
	content, err := e.Render(templateName, data)
	if err != nil {
		return fmt.Errorf("failed to render template %s: %w", templateName, err)
	}

	// Ensure output directory exists
	outputDir := filepath.Dir(outputPath)
	if err := os.MkdirAll(outputDir, 0755); err != nil {
		return fmt.Errorf("failed to create output directory: %w", err)
	}

	// Write to file
	if err := os.WriteFile(outputPath, []byte(content), 0644); err != nil {
		return fmt.Errorf("failed to write file %s: %w", outputPath, err)
	}

	e.logger.WithFields(logrus.Fields{
		"template": templateName,
		"output":   outputPath,
	}).Debug("Rendered template file")

	return nil
}

// Render renders a template with the given data and returns the result as a string
func (e *Engine) Render(templateName string, data interface{}) (string, error) {
	tmpl, err := e.getTemplate(templateName)
	if err != nil {
		return "", err
	}

	var buf bytes.Buffer
	if err := tmpl.Execute(&buf, data); err != nil {
		return "", fmt.Errorf("failed to execute template: %w", err)
	}

	return buf.String(), nil
}

// RenderString renders a template string with the given data
func (e *Engine) RenderString(templateContent string, data interface{}) (string, error) {
	tmpl, err := template.New("inline").Funcs(GetTemplateFuncs()).Parse(templateContent)
	if err != nil {
		return "", fmt.Errorf("failed to parse template: %w", err)
	}

	var buf bytes.Buffer
	if err := tmpl.Execute(&buf, data); err != nil {
		return "", fmt.Errorf("failed to execute template: %w", err)
	}

	return buf.String(), nil
}

// getTemplate loads and caches a template
func (e *Engine) getTemplate(templateName string) (*template.Template, error) {
	// Check cache
	if tmpl, ok := e.templateCache[templateName]; ok {
		return tmpl, nil
	}

	// Load template from embedded filesystem
	content, err := e.templates.ReadFile(templateName)
	if err != nil {
		return nil, fmt.Errorf("failed to read template %s: %w", templateName, err)
	}

	// Parse template
	tmpl, err := template.New(templateName).Funcs(GetTemplateFuncs()).Parse(string(content))
	if err != nil {
		return nil, fmt.Errorf("failed to parse template %s: %w", templateName, err)
	}

	// Cache it
	e.templateCache[templateName] = tmpl

	return tmpl, nil
}

// ClearCache clears the template cache (useful for development)
func (e *Engine) ClearCache() {
	e.templateCache = make(map[string]*template.Template)
}
