package promptcompiler

import (
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"strings"

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

const controlledTaskPayloadVersion = "controlled-task-payload/v1"

type controlledTaskPayload struct {
	Version            string                      `json:"version"`
	Message            string                      `json:"message"`
	ActiveTaskContext  controlledActiveTaskContext `json:"activeTaskContext,omitempty,omitzero"`
	WorkflowContextSet bool                        `json:"workflowContextSet,omitempty"`
}

type controlledActiveTaskContext struct {
	Channel   string `json:"channel,omitempty"`
	Source    string `json:"source,omitempty"`
	TaskTitle string `json:"taskTitle,omitempty"`
	Role      string `json:"role,omitempty"`
}

func (context controlledActiveTaskContext) IsZero() bool {
	return context == (controlledActiveTaskContext{})
}

// controlledTaskPayloadIdentity identifies the task and routing payload without
// retaining the body after compilation. The boolean distinguishes no task
// payload from a payload whose identity cannot be proven. Delivery-attempt
// fields such as message ID, timestamp, wake token, and transport IDs are
// intentionally excluded so the same controlled payload is stable
// across daemon lifetimes and reruns.
func controlledTaskPayloadIdentity(binding ConversationBinding, msg types.InboxMessage) (string, bool) {
	promptFacts := normalizeMessagePromptData(strings.TrimSpace(msg.Target), msg)
	if !messagePromptHasExplicit(promptFacts) || promptFacts.Author.ActorType == "system" {
		return "", false
	}
	if len(msg.Attachments()) > 0 {
		return "", true
	}
	if root, ok := msg.ReplyThreadRoot(); ok && len(root.Attachments) > 0 {
		return "", true
	}
	target := strings.TrimSpace(msg.Target)
	if target == "" {
		target = strings.TrimSpace(binding.TargetID)
	}
	if target == "" {
		target = strings.TrimSpace(binding.ChannelID)
	}
	activeTaskTarget := SelectPromptActiveTaskTarget(binding.ActiveTaskTarget, msg)
	msg = controlledPayloadMessage(msg)
	material := controlledTaskPayload{
		Version: controlledTaskPayloadVersion,
		Message: formatPromptMessage(target, msg, "message_send"),
		ActiveTaskContext: controlledActiveTaskContext{
			Channel:   activeTaskTarget.Channel,
			Source:    activeTaskTarget.Source,
			TaskTitle: activeTaskTarget.TaskTitle,
			Role:      activeTaskTarget.Role,
		},
		WorkflowContextSet: msg.ResolvedWorkflowContext().Valid(),
	}
	data, err := json.Marshal(material)
	if err != nil {
		return "", true
	}
	sum := sha256.Sum256(data)
	return hex.EncodeToString(sum[:16]), true
}

func controlledPayloadMessage(msg types.InboxMessage) types.InboxMessage {
	msg.MessageID = ""
	msg.TargetKey = ""
	msg.TargetSeq = 0
	msg.Timestamp = ""
	msg.InterruptPolicy = ""
	msg.Source = types.ContributionSource{}
	msg.AttachmentList = nil
	msg.AttachmentIDs = ""
	msg.AttachmentNames = ""
	msg.Metadata = controlledPayloadMetadata(msg.Metadata)
	return msg
}

func controlledPayloadMetadata(metadata map[string]any) map[string]any {
	if len(metadata) == 0 {
		return nil
	}
	out := make(map[string]any, 5)
	for _, key := range []string{"task", "taskDiscussion", "replyThreadRoot", "reaction", workflowAgentEditMetadataKey} {
		values, ok := metadata[key].(map[string]any)
		if !ok || len(values) == 0 {
			continue
		}
		cloned := make(map[string]any, len(values))
		for field, value := range values {
			switch key + "." + field {
			case "task.taskId",
				"task.taskNumber",
				"task.taskScope",
				"task.taskScopeRef",
				"task.taskScopeTarget",
				"task.taskScopeId",
				"task.sourceThreadRef",
				"task.rootTaskNumber",
				"task.parentTaskNumber",
				"taskDiscussion.entryId",
				"taskDiscussion.taskId",
				"replyThreadRoot.messageId",
				"replyThreadRoot.timestamp",
				"reaction.deliveryId",
				"reaction.reactionId",
				"reaction.targetMessageId":
				continue
			}
			cloned[field] = value
		}
		out[key] = cloned
	}
	if len(out) == 0 {
		return nil
	}
	return out
}

func controlledDeliveryPayloadIdentity(input DeliveryCompileInput) (string, bool) {
	author := input.AuthorIdentity
	return controlledTaskPayloadIdentity(ConversationBinding{
		ChannelID:        strings.TrimSpace(input.Target),
		ChannelType:      strings.TrimSpace(input.ChannelType),
		TargetID:         strings.TrimSpace(input.Target),
		ActiveTaskTarget: input.ActiveTaskTarget,
		WorkflowContext:  input.WorkflowContext,
	}, types.InboxMessage{
		Target:           strings.TrimSpace(input.Target),
		ChannelType:      strings.TrimSpace(input.ChannelType),
		ChannelName:      strings.TrimSpace(input.ChannelName),
		AuthorProfile:    authorProfileForPayload(author),
		Content:          input.Content,
		MessageType:      strings.TrimSpace(input.MessageType),
		Metadata:         input.Metadata,
		WorkflowContext:  input.WorkflowContext,
		AttachmentList:   input.Attachments,
		ActiveTaskTarget: input.ActiveTaskTarget,
		TeamContexts:     input.TeamContexts,
	})
}

func authorProfileForPayload(author types.AuthorIdentity) types.AuthorProfile {
	return types.AuthorProfile{
		ActorType:   strings.TrimSpace(author.ActorType),
		PrincipalID: strings.TrimSpace(author.PrincipalID),
		MemberID:    strings.TrimSpace(author.MemberID),
		TargetID:    strings.TrimSpace(author.TargetID),
		DisplayName: strings.TrimSpace(author.DisplayName),
	}
}
