package promptcompiler

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

// CompilePrompt compiles and renders one runtime carrier in one compiler-owned
// operation. Callers receive a sealed CompiledPrompt rather than independently
// joining or mutating semantic prompt strings.
func CompilePrompt(ctx context.Context, desc CompileDescriptor, in CompileInput, runtime string) (CompiledPrompt, error) {
	compiler := New(DefaultPolicy())
	return compiler.CompilePrompt(ctx, desc, in, runtime)
}

// CompilePrompt compiles and renders one runtime carrier with this compiler's
// policy.
func (c *Compiler) CompilePrompt(ctx context.Context, desc CompileDescriptor, in CompileInput, runtime string) (CompiledPrompt, error) {
	envelope, err := c.CompileRuntimeInput(ctx, desc, in)
	if err != nil {
		return CompiledPrompt{}, err
	}
	rendered, err := c.RenderRuntimeInput(ctx, envelope, runtime, RenderOptions{})
	if err != nil {
		return CompiledPrompt{}, err
	}
	return CompiledPrompt{envelope: envelope, rendered: rendered}, nil
}

// CompileDeliveryPrompt compiles and renders a typed delivery envelope into
// the sealed runtime carrier used by live delivery.
func (c *Compiler) CompileDeliveryPrompt(ctx context.Context, eventType CompileEventType, eventID string, in CompileInput, runtime string) (CompiledPrompt, error) {
	return c.CompilePrompt(ctx, CompileDescriptor{
		EventID:   eventID,
		EventKind: EventDeliveryEnvelope,
		EventType: eventType,
	}, in, runtime)
}

// MarshalJSON is the explicit transport serialization for a compiled carrier.
// It serializes only compiler-produced envelope/rendered data; the sealed
// representation itself remains impossible to replace from another package.
func (p CompiledPrompt) MarshalJSON() ([]byte, error) {
	if !p.Valid() {
		return nil, fmt.Errorf("cannot serialize an invalid compiled prompt")
	}
	return json.Marshal(struct {
		Envelope RuntimeInputEnvelope  `json:"envelope"`
		Rendered RenderedPromptPayload `json:"rendered"`
		Launch   *launchPromptCarrier  `json:"launch,omitempty"`
	}{Envelope: p.envelope, Rendered: p.rendered, Launch: p.launch})
}

// UnmarshalJSON deliberately rejects all serialized carriers. A compiled
// prompt is an ephemeral compiler product; durable ownership records strip it
// and recompile from typed message facts. Accepting rendered or launch surfaces
// from JSON would let callers forge a valid semantic carrier.
func (*CompiledPrompt) UnmarshalJSON([]byte) error {
	const reason = "compiled prompt is sealed and cannot be unmarshaled"
	return fmt.Errorf("%s", reason)
}
