package overlay

import (
	"bufio"
	"encoding/json"
	"fmt"
	"os"
	"path"
	"strings"

	warewulfconf "github.com/warewulf/warewulf/internal/pkg/config"
	"github.com/warewulf/warewulf/internal/pkg/container"
	"github.com/warewulf/warewulf/internal/pkg/node"
	"github.com/warewulf/warewulf/internal/pkg/util"
	"github.com/warewulf/warewulf/internal/pkg/wwlog"
)

/*
Reads a file file from the host fs. If the file has nor '/' prefix
the path is relative to Paths.SysconfdirTemplates in the file are no evaluated.
*/
func templateFileInclude(inc string) string {
	conf := warewulfconf.Get()
	if !strings.HasPrefix(inc, "/") {
		inc = path.Join(conf.Paths.Sysconfdir, "warewulf", inc)
	}
	wwlog.Debug("Including file into template: %s", inc)
	content, err := os.ReadFile(inc)
	if err != nil {
		wwlog.Verbose("Could not include file into template: %s", err)
	}
	return strings.TrimSuffix(string(content), "\n")
}

/*
Reads a file into template the abort string is found in a line. First argument
is the file to read, the second the abort string
Templates in the file are no evaluated.
*/
func templateFileBlock(inc string, abortStr string) (string, error) {
	conf := warewulfconf.Get()
	if !strings.HasPrefix(inc, "/") {
		inc = path.Join(conf.Paths.Sysconfdir, "warewulf", inc)
	}
	wwlog.Debug("Including file block into template: %s", inc)
	readFile, err := os.Open(inc)
	if err != nil {
		return "", err
	}
	defer readFile.Close()
	var cont string
	fileScanner := bufio.NewScanner(readFile)
	fileScanner.Split(bufio.ScanLines)
	for fileScanner.Scan() {
		line := fileScanner.Text()
		if strings.Contains(line, abortStr) {
			break
		}
		cont += line + "\n"
	}

	// NOTE: the text originally contains N-1 newlines for N lines, but the above
	// loop will always add one at the end
	// Avoids adding a blank line that was not present in the original file
	// by adding 'abort' string to the end of the included block (without a newline)
	// instead of manually in the template
	cont += abortStr

	return cont, nil

}

/*
Reads a file relative to given container.
Templates in the file are no evaluated.
*/
func templateContainerFileInclude(containername string, filepath string) string {
	wwlog.Verbose("Including file from Container into template: %s:%s", containername, filepath)

	if containername == "" {
		wwlog.Warn("Container is not defined for node: %s", filepath)
		return ""
	}

	if !container.ValidSource(containername) {
		wwlog.Warn("Template requires file(s) from non-existant container: %s:%s", containername, filepath)
		return ""
	}

	containerDir := container.RootFsDir(containername)

	wwlog.Debug("Including file from container: %s:%s", containerDir, filepath)

	if !util.IsFile(path.Join(containerDir, filepath)) {
		wwlog.Warn("Requested file from container does not exist: %s:%s", containername, filepath)
		return ""
	}

	content, err := os.ReadFile(path.Join(containerDir, filepath))

	if err != nil {
		wwlog.Error("Template include failed: %s", err)
	}
	return strings.TrimSuffix(string(content), "\n")
}

func createIgnitionJson(node *node.NodeInfo) string {
	conf, rep, err := node.GetConfig()
	if len(conf.Storage.Disks) == 0 && len(conf.Storage.Filesystems) == 0 {
		wwlog.Debug("no disks or filesystems present, don't create a json object")
		return ""
	}
	if err != nil {
		wwlog.Error("disk, filesystem configuration has following error: ", fmt.Sprint(err))
		return fmt.Sprint(err)
	}
	if rep != "" {
		wwlog.Warn("%s storage configuration has following non fatal problems: %s", node.Id, rep)
	}
	tmpYaml, _ := json.Marshal(&conf)
	return string(tmpYaml)
}
