package promptcompiler

import (
	"context"
	"fmt"
	"strings"
)

// GeneratedPromptKind is the closed set of compiler-owned lifecycle turns.
type GeneratedPromptKind string

const (
	GeneratedContextCompaction GeneratedPromptKind = "context_compaction_continuation"
	GeneratedRecoveryRestart   GeneratedPromptKind = "context_recovery_restart"
	GeneratedOutputTruncation  GeneratedPromptKind = "output_truncation_continuation"
	GeneratedRuntimeRetry      GeneratedPromptKind = "runtime_retry"
	GeneratedFinalReplyCheck   GeneratedPromptKind = "final_visible_reply_check"
)

// GeneratedPromptInput is the typed domain input for one registered lifecycle
// turn. There is deliberately no arbitrary PromptBlock.Text field.
type GeneratedPromptInput struct {
	Kind       GeneratedPromptKind
	Retry      RuntimeRetryTemplateData
	FinalReply FinalVisibleReplyCheckTemplateData
}

// GeneratedPromptBlockID maps a closed lifecycle kind to its registered block.
func GeneratedPromptBlockID(kind GeneratedPromptKind) string {
	switch kind {
	case GeneratedContextCompaction:
		return "agentteam.context_compaction_continuation"
	case GeneratedRecoveryRestart:
		return "event.context_recovery_restart"
	case GeneratedOutputTruncation:
		return "agentteam.output_truncation_continuation"
	case GeneratedRuntimeRetry:
		return "event.runtime_retry"
	case GeneratedFinalReplyCheck:
		return "agentteam.final_visible_reply_check"
	default:
		return ""
	}
}

// CompileGeneratedPrompt compiles a closed typed lifecycle value into a sealed
// carrier. Registry lookup, template rendering, metadata, and validation all
// happen inside promptcompiler.
func (c *Compiler) CompileGeneratedPrompt(ctx context.Context, eventType CompileEventType, eventID string, input GeneratedPromptInput, runtime string) (CompiledPrompt, error) {
	envelope, err := c.CompileGeneratedTurn(ctx, eventType, eventID, input)
	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
}

// CompileGeneratedTurn compiles a registered lifecycle value into an envelope.
func (c *Compiler) CompileGeneratedTurn(ctx context.Context, eventType CompileEventType, eventID string, input GeneratedPromptInput) (RuntimeInputEnvelope, error) {
	if err := contextError(ctx); err != nil {
		return RuntimeInputEnvelope{}, err
	}
	id := GeneratedPromptBlockID(input.Kind)
	if id == "" {
		return RuntimeInputEnvelope{}, &PromptCompileError{Kind: ErrUnregisteredPromptBlock, BlockID: id, EventID: eventID, EventType: eventType, Reason: "generated lifecycle kind is not registered"}
	}
	if _, ok := DefaultPromptBlockRegistry().Lookup(id); !ok {
		return RuntimeInputEnvelope{}, &PromptCompileError{Kind: ErrUnregisteredPromptBlock, BlockID: id, EventID: eventID, EventType: eventType, Reason: "generated lifecycle kind is not registered"}
	}
	desc := CompileDescriptor{EventID: eventID, EventKind: EventTurnContext, EventType: eventType}
	var block PromptBlock
	switch input.Kind {
	case GeneratedContextCompaction, GeneratedRecoveryRestart, GeneratedOutputTruncation:
		block = c.mustConstructBlock(desc, id, newRecoveryPromptInput(RecoveryTemplateData{}))
	case GeneratedRuntimeRetry:
		block = c.mustConstructBlock(desc, id, newRuntimeRetryPromptInput(input.Retry))
	case GeneratedFinalReplyCheck:
		block = c.mustConstructBlock(desc, id, newFinalReplyCheckPromptInput(input.FinalReply))
	default:
		return RuntimeInputEnvelope{}, fmt.Errorf("unsupported generated prompt kind %q", input.Kind)
	}
	bundle := PromptBundle{SchemaVersion: SchemaVersion, EventID: strings.TrimSpace(eventID), EventType: eventType, Blocks: []PromptBlock{block}}
	bundle.Fingerprint = fingerprintBundle(bundle, EventTurnContext)
	return c.validatedEnvelope(RuntimeInputEnvelope{SchemaVersion: SchemaVersion, EventID: strings.TrimSpace(eventID), EventType: eventType, EventKind: EventTurnContext, Bundle: &bundle, Fingerprint: fingerprintEnvelope(EventTurnContext, eventType, bundle.Fingerprint)})
}
