package spec

import (
	"bytes"
	"fmt"
	"sort"

	"gopkg.in/yaml.v3"
)

// EncodeDefinition renders a Package back to its deterministic on-disk file set
// keyed by package-relative path ("meta.yaml" and "actions/<name>.yaml"). The
// output is the normative Package format, so load and encode round-trip without
// changing the Definition digest.
func EncodeDefinition(pkg Package) (map[string][]byte, error) {
	meta := metaFile{
		SchemaVersion: pkg.Metadata.SchemaVersion,
		Digest:        pkg.Metadata.Digest,
		Domain:        pkg.Metadata.Domain,
		Version:       pkg.Metadata.Version,
		Name:          pkg.Metadata.Name,
		Description:   pkg.Metadata.Description,
		Source:        pkg.Metadata.Source,
		Endpoint:      pkg.Metadata.Endpoint,
		Auth:          pkg.Metadata.Auth,
	}
	for _, a := range pkg.Actions {
		meta.Actions = append(meta.Actions, ActionRef{Name: a.Name, File: a.File})
	}
	// meta.yaml actions follow Package order (already sorted by name on load),
	// but encoding sorts defensively so hand-built packages are stable.
	sort.Slice(meta.Actions, func(i, j int) bool { return meta.Actions[i].Name < meta.Actions[j].Name })

	metaBytes, err := encodeYAML(meta)
	if err != nil {
		return nil, fmt.Errorf("encode meta.yaml: %w", err)
	}
	files := map[string][]byte{"meta.yaml": metaBytes}

	actions := append([]Action(nil), pkg.Actions...)
	sort.Slice(actions, func(i, j int) bool { return actions[i].Name < actions[j].Name })
	for _, a := range actions {
		raw, err := encodeYAML(a)
		if err != nil {
			return nil, fmt.Errorf("encode action %q: %w", a.Name, err)
		}
		files[a.File] = raw
	}
	return files, nil
}

func encodeYAML(v any) ([]byte, error) {
	var buf bytes.Buffer
	enc := yaml.NewEncoder(&buf)
	enc.SetIndent(2)
	if err := enc.Encode(v); err != nil {
		return nil, err
	}
	if err := enc.Close(); err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}
