package promptcompiler

import (
	"strings"

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

// MessagePromptAttachment is the normalized attachment projection used by
// message templates. It is produced at the domain boundary, never by a
// template or runtime adapter.
type MessagePromptAttachment struct {
	ID          string
	Filename    string
	ContentType string
	SizeBytes   int64
}

// ReactionPromptData is the normalized reaction payload used by templates.
type ReactionPromptData struct {
	Emoji                string
	TargetMessageID      string
	TargetMessagePreview string
}

// MessagePromptData is the typed message authoring boundary. Raw metadata is
// decoded once by normalizeMessagePromptData; renderers consume this value.
type MessagePromptData struct {
	Target           string
	ChannelType      string
	ChannelName      string
	MessageID        string
	TargetSeq        int64
	Timestamp        string
	MessageType      string
	Content          string
	Author           types.AuthorIdentity
	Attachments      []MessagePromptAttachment
	ThreadRoot       *types.ReplyThreadRoot
	TeamContexts     []types.TeamContext
	WorkflowContext  types.WorkflowContext
	ActiveTaskTarget types.ActiveTaskTarget
	Task             TaskPromptMetadata
	HasTask          bool
	WorkflowEdit     *WorkflowEditPromptData
	Reaction         *ReactionPromptData
	HasMetadata      bool
}

func normalizeMessagePromptData(target string, msg types.InboxMessage) MessagePromptData {
	data := MessagePromptData{
		Target:           strings.TrimSpace(target),
		ChannelType:      strings.TrimSpace(msg.ChannelType),
		ChannelName:      strings.TrimSpace(msg.ChannelName),
		MessageID:        strings.TrimSpace(msg.MessageID),
		TargetSeq:        msg.TargetSeq,
		Timestamp:        strings.TrimSpace(msg.Timestamp),
		MessageType:      strings.TrimSpace(msg.MessageType),
		Content:          strings.TrimSpace(msg.Content),
		Author:           msg.AuthorIdentity(),
		TeamContexts:     append([]types.TeamContext(nil), msg.TeamContexts...),
		WorkflowContext:  msg.ResolvedWorkflowContext(),
		ActiveTaskTarget: msg.ActiveTaskTarget,
		HasMetadata:      len(msg.Metadata) > 0,
	}
	for _, attachment := range msg.Attachments() {
		data.Attachments = append(data.Attachments, MessagePromptAttachment{
			ID: attachment.ID, Filename: attachment.Filename, ContentType: attachment.ContentType, SizeBytes: attachment.SizeBytes,
		})
	}
	if root, ok := msg.ReplyThreadRoot(); ok {
		data.ThreadRoot = &root
	}
	if task, ok := decodeTaskPromptMetadata(data.MessageType, msg.Metadata); ok {
		data.Task, data.HasTask = task, true
		if !data.ActiveTaskTarget.Valid() && task.Task.HasTaskNumber && task.Task.ChannelRef != "" {
			taskScope := normalizeTaskScopeTarget(task.Task.TaskScope)
			if taskScope == "" {
				taskScope = normalizeTaskScopeTarget(data.Target)
			}
			data.ActiveTaskTarget = types.ActiveTaskTarget{
				Channel: task.Task.ChannelRef, TaskNumber: task.Task.TaskNumber,
				Source: "task_dispatch", TaskScope: taskScope,
				TaskTitle: task.Task.Title, RootTaskNumber: task.Task.RootTaskNumber, ParentTaskNumber: task.Task.ParentTaskNumber,
			}.Normalize()
			if !data.ActiveTaskTarget.Valid() {
				data.ActiveTaskTarget = types.ActiveTaskTarget{Channel: task.Task.ChannelRef, TaskNumber: task.Task.TaskNumber, Source: "task_dispatch", TaskTitle: task.Task.Title, RootTaskNumber: task.Task.RootTaskNumber, ParentTaskNumber: task.Task.ParentTaskNumber}.Normalize()
			}
		}
	}
	if workflow, ok := normalizeWorkflowAgentEditPromptData(msg.Metadata); ok {
		data.WorkflowEdit = &workflow
	}
	if reaction, ok := normalizeReactionPromptData(msg.MessageType, msg.Metadata); ok {
		data.Reaction = &reaction
	}
	return data
}

func (data MessagePromptData) initialized() bool {
	return data.MessageID != "" || data.Target != "" || data.Content != "" || data.MessageType != "" || data.ChannelType != "" || data.ChannelName != "" || data.HasMetadata || len(data.Attachments) > 0 || data.Author.ActorType != ""
}

func messagePromptHasExplicit(data MessagePromptData) bool {
	return data.Content != "" || data.MessageType != "" || len(data.Attachments) > 0 || data.HasMetadata
}

func messagePromptAttachments(data []MessagePromptAttachment) []types.MessageAttachment {
	out := make([]types.MessageAttachment, 0, len(data))
	for _, attachment := range data {
		out = append(out, types.MessageAttachment{ID: attachment.ID, Filename: attachment.Filename, ContentType: attachment.ContentType, SizeBytes: attachment.SizeBytes})
	}
	return out
}

func normalizeDeliveryPromptData(input DeliveryCompileInput) MessagePromptData {
	return normalizeMessagePromptData(input.Target, types.InboxMessage{
		MessageID: input.MessageID, TargetKey: input.TargetKey, TargetSeq: input.TargetSeq,
		Target: input.Target, ChannelType: input.ChannelType, ChannelName: input.ChannelName,
		AuthorProfile: types.AuthorProfile{ActorType: input.AuthorIdentity.ActorType, PrincipalID: input.AuthorIdentity.PrincipalID, MemberID: input.AuthorIdentity.MemberID, TargetID: input.AuthorIdentity.TargetID, DisplayName: input.AuthorIdentity.DisplayName},
		Content:       input.Content, Timestamp: input.Timestamp, MessageType: input.MessageType,
		Metadata: input.Metadata, AttachmentList: input.Attachments, TeamContexts: input.TeamContexts,
		WorkflowContext: input.WorkflowContext, ActiveTaskTarget: input.ActiveTaskTarget,
	})
}

func normalizeTaskScopeTarget(value string) string {
	value = strings.TrimSpace(value)
	if strings.HasPrefix(value, "tsk_") {
		value = "#" + value
	}
	return value
}

func normalizeReactionPromptData(messageType string, metadata map[string]any) (ReactionPromptData, bool) {
	if strings.TrimSpace(messageType) != "reaction" || len(metadata) == 0 {
		return ReactionPromptData{}, false
	}
	raw, ok := metadata["reaction"].(map[string]any)
	if !ok {
		return ReactionPromptData{}, false
	}
	data := ReactionPromptData{
		Emoji:                strings.TrimSpace(promptStringMapValue(raw, "emoji")),
		TargetMessageID:      strings.TrimSpace(promptStringMapValue(raw, "targetMessageId")),
		TargetMessagePreview: strings.TrimSpace(promptStringMapValue(raw, "targetMessagePreview")),
	}
	return data, data.Emoji != "" || data.TargetMessageID != "" || data.TargetMessagePreview != ""
}
