package privateagent

import (
	"context"
	"time"

	"github.com/tidbcloud/AgentTeam/internal/core/domain"
	"github.com/tidbcloud/AgentTeam/internal/coredb"
	"github.com/tidbcloud/AgentTeam/internal/store/message"
)

// WriteConversationMessageParams describes an app-visible conversation message
// that is persisted through the canonical scope message pipeline.
type WriteConversationMessageParams struct {
	// AuthorMemberID and AuthorType are the durable scope_messages author. Do
	// not replace them with routing or notification principals.
	AuthorMemberID           string
	AuthorType               domain.MemberType
	Target                   message.ThreadAccess
	Content                  string
	AttachmentIDs            []string
	CreatedAt                time.Time
	AllowControlledBroadcast bool
	MentionHints             []message.MessageMentionHint
	MessageType              string
	ExplicitRoutingPlan      *message.AgentRoutingPlan
	Metadata                 map[string]any
	Source                   message.ContributionSource
	// RoutingPrincipalMemberID is the authority used for mention/routing
	// resolution. Empty means "route as AuthorMemberID".
	RoutingPrincipalMemberID string
	ClientMessageID          string
	// IdempotencyPayloadDigest is an internal trusted override for writes whose
	// persisted representation contains server-derived materialization. Public
	// message inputs never expose this field.
	IdempotencyPayloadDigest string
	InterruptPolicy          string
	Freshness                message.SendMessageFreshness
	// NotifyPrincipalMemberID is an optional source-adapter notification
	// subject and must not change the durable message author.
	NotifyPrincipalMemberID    string
	IdempotencySubjectMemberID string
	OwnerQuoteSourceMessageIDs []string
	ForceScopeID               string
	ForceMessageID             string
	ForceWorkspaceGlobalSeq    int64
	// AllocateWorkspaceGlobalSeq allocates the workspace-global message
	// sequence after idempotency replay has been checked. It is reserved for
	// scopes whose local sequence is not the workspace sequence authority.
	AllocateWorkspaceGlobalSeq bool
}

// WriteConversationMessage persists a channel/thread/DM message through
// scope_messages plus scope fanout outbox facts.
func (store *Store) WriteConversationMessage(ctx context.Context, tx coredb.Tx, params WriteConversationMessageParams) (message.MessageDelivery, error) {
	result, err := store.WriteConversationMessageResult(ctx, tx, params)
	if err != nil {
		return message.MessageDelivery{}, err
	}
	if result.State == message.SendStateHeld {
		return message.MessageDelivery{}, badRequest("message send was held by freshness gate")
	}
	return result.Delivery, nil
}

func (store *Store) WriteConversationMessageResult(ctx context.Context, tx coredb.Tx, params WriteConversationMessageParams) (message.SendMessageResult, error) {
	createdAt := params.CreatedAt
	if !createdAt.IsZero() {
		createdAt = createdAt.UTC()
	}
	explicitPlan := params.ExplicitRoutingPlan
	return store.writeConversationMessageResult(ctx, tx, conversationMessageWriteInput{
		AuthorMemberID:     params.AuthorMemberID,
		AuthorType:         params.AuthorType,
		Content:            params.Content,
		AttachmentIDs:      params.AttachmentIDs,
		CreatedAt:          createdAt,
		ConversationTarget: &params.Target,
		ConversationOptions: conversationMessageWriteOptions{
			AllowControlledBroadcast:   params.AllowControlledBroadcast,
			MentionHints:               params.MentionHints,
			MessageType:                params.MessageType,
			ExplicitRoutingPlan:        explicitPlan,
			Metadata:                   params.Metadata,
			Source:                     params.Source,
			RoutingPrincipalMemberID:   params.RoutingPrincipalMemberID,
			ClientMessageID:            params.ClientMessageID,
			IdempotencyPayloadDigest:   params.IdempotencyPayloadDigest,
			InterruptPolicy:            params.InterruptPolicy,
			Freshness:                  params.Freshness,
			NotifyPrincipalMemberID:    params.NotifyPrincipalMemberID,
			IdempotencySubjectMemberID: params.IdempotencySubjectMemberID,
			OwnerQuoteSourceMessageIDs: params.OwnerQuoteSourceMessageIDs,
			ForceScopeID:               params.ForceScopeID,
			ForceMessageID:             params.ForceMessageID,
			ForceWorkspaceGlobalSeq:    params.ForceWorkspaceGlobalSeq,
			AllocateWorkspaceGlobalSeq: params.AllocateWorkspaceGlobalSeq,
		},
	})
}
