package promptcompiler

import (
	"fmt"
	"net/url"
	"path/filepath"
	"strings"

	"github.com/tidbcloud/AgentTeam/desktop/internal/types"
)

// RenderAttachmentResolution renders the compiler-owned attachment guidance
// for a prepared or failed resolution. It is safe to call for an empty result.
func RenderAttachmentResolution(resolution AttachmentResolution) string {
	id := ""
	data := AttachmentTemplateData{}
	if len(resolution.Prepared) > 0 {
		id = "event.attachment_manifest"
		data.Attachments = preparedAttachmentTemplateItems(resolution.Prepared)
	} else if resolution.Failure != nil {
		id = "event.attachment_preparation_failure"
		data.Attachments = failedAttachmentTemplateItems(resolution.Failure.Attachments)
		data.Error = strings.TrimSpace(resolution.Failure.Error)
	}
	if id == "" {
		return ""
	}
	return strings.TrimSpace(renderRegisteredPromptText(id, newAttachmentPromptInput(data)))
}

// AppendAttachmentResolution is a compatibility bridge for direct delivery
// callers. Normal launch/delivery paths compile the resolution before rendering;
// this function is idempotent so a legacy fallback cannot duplicate guidance.
func AppendAttachmentResolution(text string, resolution AttachmentResolution) string {
	guidance := RenderAttachmentResolution(resolution)
	if guidance == "" {
		return text
	}
	if strings.Contains(text, guidance) {
		return text
	}
	text = strings.TrimRight(text, "\n")
	if strings.TrimSpace(text) == "" {
		return guidance
	}
	return text + "\n\n" + guidance
}

func attachmentPromptBlockInput(resolution AttachmentResolution) (string, PromptBlockInput, bool) {
	id := ""
	data := AttachmentTemplateData{}
	if len(resolution.Prepared) > 0 {
		id = "event.attachment_manifest"
		data.Attachments = preparedAttachmentTemplateItems(resolution.Prepared)
	} else if resolution.Failure != nil {
		id = "event.attachment_preparation_failure"
		data.Attachments = failedAttachmentTemplateItems(resolution.Failure.Attachments)
		data.Error = strings.TrimSpace(resolution.Failure.Error)
	}
	if id == "" {
		return "", nil, false
	}
	return id, newAttachmentPromptInput(data), true
}

// AttachmentPromptBlock returns a registered turn block for attachment
// guidance. The compiler is the owner of the semantic text and block metadata.
func AttachmentPromptBlock(resolution AttachmentResolution) PromptBlock {
	id, input, ok := attachmentPromptBlockInput(resolution)
	if !ok {
		var empty PromptBlock
		return empty
	}
	block, err := DefaultPromptBlockRegistry().Construct(id, input)
	if err != nil {
		panic(err)
	}
	return block
}

func preparedAttachmentTemplateItems(attachments []types.RuntimeAttachment) []AttachmentTemplateItem {
	items := make([]AttachmentTemplateItem, 0, len(attachments))
	for _, attachment := range attachments {
		label := strings.TrimSpace(attachment.Filename)
		path := strings.TrimSpace(attachment.Path)
		uri := strings.TrimSpace(attachment.URI)
		if label == "" && path != "" {
			label = filepath.Base(path)
		}
		if label == "" && uri != "" {
			if parsed, err := url.Parse(uri); err == nil && parsed.Scheme == "file" {
				label = filepath.Base(parsed.Path)
			}
		}
		if label == "" {
			label = strings.TrimSpace(attachment.ID)
		}
		items = append(items, AttachmentTemplateItem{
			Number: len(items) + 1,
			Label:  label,
			ID:     strings.TrimSpace(attachment.ID),
			Path:   path,
			URI:    uri,
			MIME:   strings.TrimSpace(attachment.MIME),
			Size:   attachment.Size,
		})
	}
	return items
}

func failedAttachmentTemplateItems(attachments []types.MessageAttachment) []AttachmentTemplateItem {
	items := make([]AttachmentTemplateItem, 0, len(attachments))
	for _, attachment := range types.NormalizeMessageAttachments(attachments) {
		label := strings.TrimSpace(attachment.Filename)
		items = append(items, AttachmentTemplateItem{
			Number:      len(items) + 1,
			Label:       label,
			ID:          strings.TrimSpace(attachment.ID),
			ContentType: strings.TrimSpace(attachment.ContentType),
			Size:        attachment.SizeBytes,
		})
	}
	return items
}

// AttachmentGuidanceBlockForMetrics creates a registered block projection for
// an already compiled attachment result. It is used only for attribution when
// a legacy caller needs to record a delivery boundary.
func AttachmentGuidanceBlockForMetrics(resolution AttachmentResolution) PromptBlock {
	_, input, ok := attachmentPromptBlockInput(resolution)
	if !ok {
		var empty PromptBlock
		return empty
	}
	id := "runtime_attachment_manifest"
	if resolution.Failure != nil {
		id = "runtime_attachment_preparation_failure"
	}
	block, err := DefaultPromptBlockRegistry().Construct(id, input)
	if err != nil {
		panic(err)
	}
	return block
}

// AttachmentResolutionError builds the typed failure payload without exposing
// an error object to template data.
func AttachmentResolutionError(attachments []types.MessageAttachment, err error) AttachmentResolution {
	failure := &AttachmentPreparationFailure{Attachments: append([]types.MessageAttachment(nil), attachments...)}
	if err != nil {
		failure.Error = strings.TrimSpace(fmt.Sprint(err))
		if len(failure.Error) > 1024 {
			failure.Error = failure.Error[:1024] + "..."
		}
	}
	return AttachmentResolution{Failure: failure}
}
