package main

import (
	"fmt"
	"io/fs"
	"os"
	"strings"
	"text/template"
)
import "embed"
import sprig "github.com/Masterminds/sprig/v3" // This provides most Helm functions
import "github.com/goccy/go-yaml"

// This provides most Helm functions

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

func NewTemplate() *template.Template {
	tmpl := template.New("")

	// Combine sprig functions with custom functions
	funcMap := template.FuncMap{}

	// Add all sprig functions
	for name, fn := range sprig.FuncMap() {
		funcMap[name] = fn
	}

	// Add any custom functions you want
	customFuncs := template.FuncMap{
		"toYaml": func(v interface{}) string {
			data, err := yaml.Marshal(v)
			if err != nil {
				return ""
			}
			return string(data)
		},
	}

	// Merge custom functions
	for name, fn := range customFuncs {
		funcMap[name] = fn
	}

	// Add the function map to the template
	tmpl = tmpl.Funcs(funcMap)

	return tmpl
}

func showContents(files fs.FS) {
	entries, err := fs.ReadDir(files, ".")
	if err != nil {
		panic(err)
	}
	for _, entry := range entries {
		fmt.Fprintf(os.Stderr, "entry %s %s\n", entry.Name(), entry.Type())
		if entry.Type().IsDir() {
			subdir, err := fs.Sub(files, entry.Name())
			if err != nil {
				panic(err)
			}
			showContents(subdir)
		}
	}
}

type PolicyTemplates struct {
	templates *template.Template
}

func NewPolicyTemplates() (*PolicyTemplates, error) {
	showContents(templateFS)

	// Parse all templates at once from the embedded FS
	tmpl := NewTemplate()

	err := loadTemplatesAll(tmpl)
	if err != nil {
		return nil, err
	}
	return &PolicyTemplates{templates: tmpl}, err
}

func loadTemplatesAll(tmpl *template.Template) error {
	return fs.WalkDir(templateFS, ".", func(path string, d os.DirEntry, err error) error {
		if strings.HasSuffix(path, ".yaml") {
			data, err := fs.ReadFile(templateFS, path)
			if err != nil {
				return err
			}
			fmt.Fprintf(os.Stderr, "Loading template %s\n", path)
			_, err = tmpl.New(path).Option("missingkey=error").Parse(string(data))
			if err != nil {
				return err
			}
		}
		return nil
	})
}

func (t *PolicyTemplates) NamespaceTemplates(policyType string, capabilities []string) []*template.Template {
	res := make([]*template.Template, 0)
	tmpl := t.templates.Lookup(fmt.Sprintf("templates/%s/namespace/namespace.yaml", policyType))
	if tmpl != nil {
		res = append(res, tmpl)
	}
	for _, capability := range capabilities {
		tmpl := t.templates.Lookup(fmt.Sprintf("templates/%s/namespace/%s.yaml", policyType, capability))
		if tmpl != nil {
			res = append(res, tmpl)
		}
	}
	return res
}

func (t *PolicyTemplates) ApplicationTemplate(policyType string) *template.Template {
	tmpl := t.templates.Lookup(fmt.Sprintf("templates/%s/pod/pod.yaml", policyType))
	return tmpl
}

func (t *PolicyTemplates) PredefineApplicationPolicyTemplate(policyType string, predefined string) *template.Template {
	tmpl := t.templates.Lookup(fmt.Sprintf("templates/pod/%s/%s.yaml", policyType, predefined))
	return tmpl
}