package template

import (
	"bytes"
	"git.sxidc.com/go-tools/utils/fileutils"
	"github.com/Masterminds/sprig/v3"
	"os"
	"path/filepath"
	"strings"
	"text/template"
)

type ParseTemplateOption func(tpl *template.Template)

func WithDelim(leftDelim string, rightDelim string) ParseTemplateOption {
	return func(tpl *template.Template) {
		tpl.Delims(leftDelim, rightDelim)
	}
}

func ParseTemplatesDir(templateDir string, outputDir string, params any, opts ...ParseTemplateOption) error {
	files, err := fileutils.GetDirFiles(templateDir)
	if err != nil {
		return err
	}

	for _, file := range files {
		subFileName := strings.ReplaceAll(file, templateDir, "")
		subFileName = strings.TrimSuffix(subFileName, ".tpl")

		outputFile := filepath.Join(outputDir, subFileName)
		err := ParseTemplateFileToFile(file, outputFile, params, opts...)
		if err != nil {
			return err
		}
	}

	return nil
}

func ParseTemplateFileToFile(inputFilePath string, outputFilePath string, params any, opts ...ParseTemplateOption) error {
	parsedBytes, err := ParseTemplateFileToBytes(inputFilePath, params, opts...)
	if err != nil {
		return err
	}

	outputDir := filepath.Dir(outputFilePath)
	if !fileutils.PathExists(outputDir) {
		err := os.MkdirAll(outputDir, os.ModeDir|os.ModePerm)
		if err != nil {
			return err
		}
	}

	err = os.WriteFile(outputFilePath, parsedBytes, os.ModePerm)
	if err != nil {
		return err
	}

	return nil
}

func ParseTemplateFileToString(inputFilePath string, params any, opts ...ParseTemplateOption) (string, error) {
	inputBytes, err := os.ReadFile(inputFilePath)
	if err != nil {
		return "", err
	}

	parsedBytes, err := parseTemplateStringToBytes(newTemplate(inputFilePath, opts...), string(inputBytes), params)
	if err != nil {
		return "", err
	}

	return string(parsedBytes), nil
}

func ParseTemplateFileToBytes(inputFilePath string, params any, opts ...ParseTemplateOption) ([]byte, error) {
	inputBytes, err := os.ReadFile(inputFilePath)
	if err != nil {
		return nil, err
	}

	return parseTemplateStringToBytes(newTemplate(inputFilePath, opts...), string(inputBytes), params)
}

func ParseTemplateStringToString(inputStr string, params any, opts ...ParseTemplateOption) (string, error) {
	parsedBytes, err := parseTemplateStringToBytes(newTemplate(inputStr, opts...), inputStr, params)
	if err != nil {
		return "", err
	}

	return string(parsedBytes), nil
}

func ParseTemplateStringToBytes(inputStr string, params any, opts ...ParseTemplateOption) ([]byte, error) {
	return parseTemplateStringToBytes(newTemplate(inputStr, opts...), inputStr, params)
}

func newTemplate(name string, opts ...ParseTemplateOption) *template.Template {
	tpl := template.New(name)

	for _, opt := range opts {
		opt(tpl)
	}

	tpl.Funcs(sprig.FuncMap())
	tpl.Funcs(templateFuncMap)

	return tpl
}

func parseTemplateStringToBytes(tpl *template.Template, inputStr string, params any) ([]byte, error) {
	tpl, err := tpl.Parse(inputStr)
	if err != nil {
		return nil, err
	}

	buffer := &bytes.Buffer{}
	err = tpl.Execute(buffer, params)
	if err != nil {
		return nil, err
	}

	return buffer.Bytes(), nil
}