// Package emit defines the render-format contract between Go-side emitters
// and bash-side appliers (grill C-16). Every rendered artifact is a JSON
// object with a versioned schema; both sides validate against it to prevent
// emitter/applier drift.
package emit

import (
	"encoding/json"
	"errors"
	"fmt"
)

// SchemaVersion is the canonical versioned schema identifier for render
// contracts. Bump the suffix when the contract shape changes.
const SchemaVersion = "orca.emit/v1"

// Kind enumerates the rendered-artifact kinds. Each maps to an emitter
// implementation and a matching bash-side applier.
type Kind string

const (
	KindSystemd    Kind = "systemd"
	KindTraefik    Kind = "traefik"
	KindSyncthing  Kind = "syncthing"
	KindSudoers    Kind = "sudoers"
	KindSSHD       Kind = "sshd"
	KindEnvFile    Kind = "envfile"
	KindCredential Kind = "credential"
)

// Artifact is a single rendered file destined for a peer. The bash-side
// applier reads this JSON and writes Content to Path with the given Mode.
type Artifact struct {
	SchemaVersion string `json:"schema_version"`
	Kind          Kind   `json:"kind"`
	Path          string `json:"path"`
	Content       string `json:"content"`
	Mode          string `json:"mode"`
}

// Validate checks that an Artifact conforms to the render contract.
// Returns a structured error if any field is missing or invalid.
func (a *Artifact) Validate() error {
	if a.SchemaVersion != SchemaVersion {
		return fmt.Errorf("emit: schema_version mismatch: got %q want %q", a.SchemaVersion, SchemaVersion)
	}
	if a.Kind == "" {
		return errors.New("emit: kind is required")
	}
	if a.Path == "" {
		return errors.New("emit: path is required")
	}
	if a.Mode == "" {
		return errors.New("emit: mode is required")
	}
	return nil
}

// Marshal serializes an Artifact to JSON for transport to the bash applier.
func (a *Artifact) Marshal() ([]byte, error) {
	if err := a.Validate(); err != nil {
		return nil, err
	}
	return json.Marshal(a)
}

// UnmarshalArtifact parses a JSON byte slice into an Artifact and validates
// it against the contract. The bash-side applier (via orca-verify-render.sh)
// uses this same validation; the bash side rejects unparseable input with a
// structured error, never silently (grill C-16).
func UnmarshalArtifact(data []byte) (*Artifact, error) {
	var a Artifact
	if err := json.Unmarshal(data, &a); err != nil {
		return nil, fmt.Errorf("emit: unmarshal: %w", err)
	}
	if err := a.Validate(); err != nil {
		return nil, err
	}
	return &a, nil
}
