package template

import (
	"fmt"
	"strings"

	"go.yaml.in/yaml/v4"
)

type yamlBlock struct {
	Path         string        `yaml:"path"`
	Codec        string        `yaml:"codec"`
	Value        any           `yaml:"value,omitempty"`
	Fields       []yamlField   `yaml:"fields"`
	ListTemplate string        `yaml:"listTemplate,omitempty"`
	Hidden       bool          `yaml:"hidden,omitempty"`
	PathAlias    StringOrSlice `yaml:"pathAlias,omitempty"`
}

type yamlField struct {
	Path      string        `yaml:"path"`
	Value     any           `yaml:"value,omitempty"`
	Codec     string        `yaml:"codec"`
	Hidden    bool          `yaml:"hidden,omitempty"`
	PathAlias StringOrSlice `yaml:"pathAlias,omitempty"`
}

func parseYamlTemplate(input Slice) (block Block, err error) {
	var blk yamlBlock

	cleaned := cleanTemplate(input)

	dec := yaml.NewDecoder(strings.NewReader(cleaned))
	dec.KnownFields(true)

	if err := dec.Decode(&blk); err != nil {
		return block, NewErrorf("failed to parse yaml -> %w", err).WithPosition(input.start, input.end)
	}

	if blk.Path == "" {
		return block, fmt.Errorf("missing top-level 'path'")
	}

	if blk.Codec == "" {
		blk.Codec = "text"
	}

	codec, err := parseCodecType(blk.Codec)
	if err != nil {
		return block, fmt.Errorf("failed to parse codec -> %w", err)
	}

	var fields []BlockField

	for _, field := range blk.Fields {

		if field.Path == "" {
			return block, fmt.Errorf("failed to parse field: %v", field)
		}

		if field.Codec == "" {
			field.Codec = "text"
		}

		fieldCodec, err := parseCodecType(field.Codec)
		if err != nil {
			return block, fmt.Errorf("failed to parse codec -> %w", err)
		}

		block := BlockField{
			Path:        field.Path,
			PathAliases: field.PathAlias,
			CodecType:   fieldCodec,
			Value:       field.Value,
			Hidden:      field.Hidden,
		}

		fields = append(fields, block)

	}

	return Block{
		Type:         DataBlock,
		Path:         blk.Path,
		PathAliases:  blk.PathAlias,
		Codec:        codec,
		Fields:       fields,
		ListTemplate: blk.ListTemplate,
		content:      input,
	}, nil
}
