// Package encoders contains functions for rendering template.Block to a string.
package encoders

import (
	"bytes"
	"fmt"
	"strings"

	"git.max-richter.dev/max/marka/renderer/utils"
	"git.max-richter.dev/max/marka/template"
)

const emptyBlock = "\uE000"

func fixRenderedBlock(input string) string {
	input = strings.ReplaceAll(input, "'_type':", "_type:")
	if len(input) == 0 {
		return emptyBlock
	}
	return input
}

// RenderBlock renders a single template block using the provided data.
func RenderBlock(block template.Block, data map[string]any) (string, error) {
	if block.Type == template.MatchingBlock {
		return block.GetContent(), nil
	}

	value, found := utils.GetValueFromPath(data, block.Path)
	if !found {
		return "", nil // If not found and not required, return empty string
	}

	switch block.Codec {
	case template.CodecText:
		return fmt.Sprintf("%v", value), nil
	case template.CodecYaml:
		return RenderYaml(data, block)
	case template.CodecList:
		return RenderList(value, block)
	case template.CodecConst:
		return fmt.Sprintf("%v", block.Value), nil
	default:
		return "", fmt.Errorf("unknown codec: %s for path '%s'", block.Codec, block.Path)
	}
}

func Render(data map[string]any, blocks []template.Block) (string, error) {
	var buffer bytes.Buffer
	for _, block := range blocks {
		renderedContent, err := RenderBlock(block, data)
		if err != nil {
			return "", fmt.Errorf("failed to render block for path '%s': %w", block.Path, err)
		}
		renderedContent = fixRenderedBlock(renderedContent)
		buffer.WriteString(renderedContent)
	}

	outputString := buffer.String()
	outputString = strings.ReplaceAll(outputString, "\n\n"+emptyBlock+"\n\n", "\n\n")
	outputString = strings.ReplaceAll(outputString, "\n"+emptyBlock+"\n", "\n")
	outputString = strings.ReplaceAll(outputString, emptyBlock, "")
	if !strings.HasSuffix(outputString, "\n") {
		outputString += "\n"
	}

	return outputString, nil
}
