package message

import (
	"fmt"
	"sort"
	"strings"
)

// MessageSemanticClass classifies the authority represented by a scope message
// row. Phase 1 keeps this as a descriptor/classifier only; write enforcement
// and migration gates are owned by later guardrail phases.
type MessageSemanticClass string

const (
	MessageClassTranscript MessageSemanticClass = "transcript"
	MessageClassEnvelope   MessageSemanticClass = "envelope"
	MessageClassLegacy     MessageSemanticClass = "legacy"
)

// MessageRenderKind is the stable render family exposed by the descriptor. It
// is not a storage message type and may be derived from metadata for legacy rows.
type MessageRenderKind string

const (
	MessageRenderText               MessageRenderKind = "text"
	MessageRenderSystemEvent        MessageRenderKind = "system_event"
	MessageRenderOwnerQuote         MessageRenderKind = "owner_quote"
	MessageRenderChannelShareCard   MessageRenderKind = "channel_share_card"
	MessageRenderThreadShareCard    MessageRenderKind = "thread_share_card"
	MessageRenderSticker            MessageRenderKind = "sticker"
	MessageRenderAppInteractiveCard MessageRenderKind = "app_interactive_card"
	MessageRenderServiceAccountPost MessageRenderKind = "service_account_post"
	MessageRenderNone               MessageRenderKind = "none"
)

// MessageReadCapability describes how a row participates in read paths.
type MessageReadCapability string

const (
	MessageReadTranscript         MessageReadCapability = "transcript"
	MessageReadHiddenEnvelope     MessageReadCapability = "hidden_envelope"
	MessageReadLegacyHidden       MessageReadCapability = "legacy_hidden"
	MessageReadUnregisteredCompat MessageReadCapability = "unregistered_compat"
)

// MessageMutationCapability describes message-lifecycle operations available to
// the row family. It deliberately stays coarse until behavior is wired to this
// descriptor in later phases.
type MessageMutationCapability string

const (
	MessageMutationStandard   MessageMutationCapability = "standard_message"
	MessageMutationRestricted MessageMutationCapability = "restricted_message"
	MessageMutationNone       MessageMutationCapability = "none"
)

// MessageRenderCapability groups render behavior without turning the descriptor
// into a large boolean bag.
type MessageRenderCapability string

const (
	MessageRenderCapabilityInline MessageRenderCapability = "inline"
	MessageRenderCapabilityCard   MessageRenderCapability = "card"
	MessageRenderCapabilityNone   MessageRenderCapability = "none"
)

// MessageDeliveryProfile describes the routing/delivery shape implied by the
// row family. It is descriptive only in Phase 1.
type MessageDeliveryProfile string

const (
	MessageDeliveryContentRouting MessageDeliveryProfile = "content_routing"
	MessageDeliveryExplicit       MessageDeliveryProfile = "explicit_routing"
	MessageDeliveryTaskScope      MessageDeliveryProfile = "task_scope"
	MessageDeliveryNone           MessageDeliveryProfile = "none"
)

// MessageParticipationProfile describes whether a row contributes to
// human/agent participation counters. It is intentionally separate from
// HumanVisible because some visible render variants (for example channel share
// cards) are not conversational participation.
type MessageParticipationProfile string

const (
	MessageParticipationCounted  MessageParticipationProfile = "counted"
	MessageParticipationExcluded MessageParticipationProfile = "excluded"
)

// MessageAgentUnreadProfile describes whether a row contributes to agent
// unread/read-path labels. It is intentionally separate from HumanVisible
// because some human-visible context rows (for example owner quotes) should not
// wake or label agents as unread.
type MessageAgentUnreadProfile string

const (
	MessageAgentUnreadCounted  MessageAgentUnreadProfile = "counted"
	MessageAgentUnreadExcluded MessageAgentUnreadProfile = "excluded"
)

// MessageModerationProfile describes moderation ownership for the row family.
type MessageModerationProfile string

const (
	MessageModerationUserContent  MessageModerationProfile = "user_content"
	MessageModerationSystemSkip   MessageModerationProfile = "system_skip"
	MessageModerationInternalSkip MessageModerationProfile = "internal_skip"
)

// MessageWriteDisposition separates current compatibility behavior from target
// write policy so Phase 1 can describe active legacy/envelope writers without
// prematurely enforcing later migrations.
type MessageWriteDisposition string

const (
	MessageWriteAllowed        MessageWriteDisposition = "allowed"
	MessageWriteRestricted     MessageWriteDisposition = "restricted"
	MessageWriteLegacyReadOnly MessageWriteDisposition = "legacy_read_only"
	MessageWriteDisallowed     MessageWriteDisposition = "disallowed"
)

// MessageMigrationTarget names the target authority for rows that should move
// away from their current message shape.
type MessageMigrationTarget string

const (
	MessageMigrationNone                       MessageMigrationTarget = ""
	MessageMigrationTaskDispatchDeliveryIntent MessageMigrationTarget = "task_dispatch_delivery_intent"
)

// MessageSemantics is a thin descriptor for a scope message row. It classifies
// existing rows and proposed message writes; it must not create domain facts,
// workflow jobs, task facts, or outbox jobs.
type MessageSemantics struct {
	Registered              bool
	MessageType             string
	SystemCardKind          string
	Class                   MessageSemanticClass
	RenderKind              MessageRenderKind
	HumanVisible            bool
	ReadCapability          MessageReadCapability
	MutationCapability      MessageMutationCapability
	RenderCapability        MessageRenderCapability
	DeliveryProfile         MessageDeliveryProfile
	ParticipationProfile    MessageParticipationProfile
	AgentUnreadProfile      MessageAgentUnreadProfile
	ModerationProfile       MessageModerationProfile
	CurrentWriteDisposition MessageWriteDisposition
	TargetWriteDisposition  MessageWriteDisposition
	MigrationTarget         MessageMigrationTarget
	RemovalCondition        string
}

func (semantics MessageSemantics) CurrentWriteAllowed() bool {
	switch semantics.CurrentWriteDisposition {
	case MessageWriteAllowed, MessageWriteRestricted:
		return true
	default:
		return false
	}
}

func (semantics MessageSemantics) TargetWriteAllowed() bool {
	switch semantics.TargetWriteDisposition {
	case MessageWriteAllowed, MessageWriteRestricted:
		return true
	default:
		return false
	}
}

func transcriptSemantics(messageType string, render MessageRenderKind, renderCapability MessageRenderCapability, delivery MessageDeliveryProfile, moderation MessageModerationProfile, currentWrite MessageWriteDisposition) MessageSemantics {
	return MessageSemantics{
		Registered:              true,
		MessageType:             messageType,
		Class:                   MessageClassTranscript,
		RenderKind:              render,
		HumanVisible:            true,
		ReadCapability:          MessageReadTranscript,
		MutationCapability:      MessageMutationStandard,
		RenderCapability:        renderCapability,
		DeliveryProfile:         delivery,
		ParticipationProfile:    MessageParticipationCounted,
		AgentUnreadProfile:      MessageAgentUnreadCounted,
		ModerationProfile:       moderation,
		CurrentWriteDisposition: currentWrite,
		TargetWriteDisposition:  currentWrite,
	}
}

func withReadVisibilityProfiles(semantics MessageSemantics, participation MessageParticipationProfile, agentUnread MessageAgentUnreadProfile) MessageSemantics {
	semantics.ParticipationProfile = participation
	semantics.AgentUnreadProfile = agentUnread
	return semantics
}

func taskDescriptionAttachmentSemantics() MessageSemantics {
	return MessageSemantics{
		Registered:              true,
		MessageType:             MessageTypeTaskDescriptionAttachment,
		Class:                   MessageClassTranscript,
		RenderKind:              MessageRenderNone,
		HumanVisible:            false,
		ReadCapability:          MessageReadTranscript,
		MutationCapability:      MessageMutationRestricted,
		RenderCapability:        MessageRenderCapabilityNone,
		DeliveryProfile:         MessageDeliveryNone,
		ParticipationProfile:    MessageParticipationExcluded,
		AgentUnreadProfile:      MessageAgentUnreadExcluded,
		ModerationProfile:       MessageModerationInternalSkip,
		CurrentWriteDisposition: MessageWriteRestricted,
		TargetWriteDisposition:  MessageWriteRestricted,
	}
}

func appInteractiveCardSemantics() MessageSemantics {
	return MessageSemantics{
		Registered:              true,
		MessageType:             MessageTypeAppInteractiveCard,
		Class:                   MessageClassTranscript,
		RenderKind:              MessageRenderAppInteractiveCard,
		HumanVisible:            true,
		ReadCapability:          MessageReadTranscript,
		MutationCapability:      MessageMutationRestricted,
		RenderCapability:        MessageRenderCapabilityCard,
		DeliveryProfile:         MessageDeliveryExplicit,
		ParticipationProfile:    MessageParticipationCounted,
		AgentUnreadProfile:      MessageAgentUnreadCounted,
		ModerationProfile:       MessageModerationInternalSkip,
		CurrentWriteDisposition: MessageWriteRestricted,
		TargetWriteDisposition:  MessageWriteRestricted,
	}
}

var messageTypeSemantics = map[string]MessageSemantics{
	"":                                   transcriptSemantics("", MessageRenderText, MessageRenderCapabilityInline, MessageDeliveryContentRouting, MessageModerationUserContent, MessageWriteAllowed),
	MessageTypeSystem:                    withReadVisibilityProfiles(transcriptSemantics(MessageTypeSystem, MessageRenderSystemEvent, MessageRenderCapabilityInline, MessageDeliveryExplicit, MessageModerationSystemSkip, MessageWriteAllowed), MessageParticipationExcluded, MessageAgentUnreadExcluded),
	MessageTypeOwnerQuote:                withReadVisibilityProfiles(transcriptSemantics(MessageTypeOwnerQuote, MessageRenderOwnerQuote, MessageRenderCapabilityCard, MessageDeliveryContentRouting, MessageModerationUserContent, MessageWriteAllowed), MessageParticipationCounted, MessageAgentUnreadExcluded),
	MessageTypeChannelShareCard:          withReadVisibilityProfiles(transcriptSemantics(MessageTypeChannelShareCard, MessageRenderChannelShareCard, MessageRenderCapabilityCard, MessageDeliveryExplicit, MessageModerationUserContent, MessageWriteRestricted), MessageParticipationExcluded, MessageAgentUnreadExcluded),
	MessageTypeThreadShareCard:           withReadVisibilityProfiles(transcriptSemantics(MessageTypeThreadShareCard, MessageRenderThreadShareCard, MessageRenderCapabilityCard, MessageDeliveryExplicit, MessageModerationUserContent, MessageWriteRestricted), MessageParticipationExcluded, MessageAgentUnreadExcluded),
	MessageTypeTaskDiscussion:            transcriptSemantics(MessageTypeTaskDiscussion, MessageRenderText, MessageRenderCapabilityInline, MessageDeliveryTaskScope, MessageModerationUserContent, MessageWriteRestricted),
	MessageTypeTaskDescriptionAttachment: taskDescriptionAttachmentSemantics(),
	MessageTypeSticker:                   transcriptSemantics(MessageTypeSticker, MessageRenderSticker, MessageRenderCapabilityInline, MessageDeliveryContentRouting, MessageModerationUserContent, MessageWriteAllowed),
	MessageTypeAppInteractiveCard:        appInteractiveCardSemantics(),
	MessageTypeServiceAccountPost:        transcriptSemantics(MessageTypeServiceAccountPost, MessageRenderServiceAccountPost, MessageRenderCapabilityCard, MessageDeliveryExplicit, MessageModerationUserContent, MessageWriteRestricted),
	MessageTypeTaskDispatch: {
		Registered:              true,
		MessageType:             MessageTypeTaskDispatch,
		Class:                   MessageClassEnvelope,
		RenderKind:              MessageRenderNone,
		HumanVisible:            false,
		ReadCapability:          MessageReadHiddenEnvelope,
		MutationCapability:      MessageMutationNone,
		RenderCapability:        MessageRenderCapabilityNone,
		DeliveryProfile:         MessageDeliveryExplicit,
		ParticipationProfile:    MessageParticipationExcluded,
		AgentUnreadProfile:      MessageAgentUnreadExcluded,
		ModerationProfile:       MessageModerationInternalSkip,
		CurrentWriteDisposition: MessageWriteLegacyReadOnly,
		TargetWriteDisposition:  MessageWriteDisallowed,
		MigrationTarget:         MessageMigrationTaskDispatchDeliveryIntent,
		RemovalCondition:        "after old-node message-source and activity-event dispatch outboxes drain, old-daemon task_dispatch receive compatibility is removed, and retained hidden rows are cleaned up",
	},
}

var systemCardKindSemantics = map[string]MessageSemantics{}

func IsRegisteredMessageType(messageType string) bool {
	_, ok := messageTypeSemantics[strings.TrimSpace(messageType)]
	return ok
}

func RegisteredMessageTypeSemantics() []MessageSemantics {
	messageTypes := make([]string, 0, len(messageTypeSemantics))
	for messageType := range messageTypeSemantics {
		messageTypes = append(messageTypes, messageType)
	}
	sort.Strings(messageTypes)
	descriptors := make([]MessageSemantics, 0, len(messageTypes))
	for _, messageType := range messageTypes {
		descriptors = append(descriptors, messageTypeSemantics[messageType])
	}
	return descriptors
}

func RegisteredSystemCardKinds() []string {
	kinds := make([]string, 0, len(systemCardKindSemantics))
	for kind := range systemCardKindSemantics {
		kinds = append(kinds, kind)
	}
	sort.Strings(kinds)
	return kinds
}

func RegisteredSystemCardKindSemantics() []MessageSemantics {
	kinds := RegisteredSystemCardKinds()
	descriptors := make([]MessageSemantics, 0, len(kinds))
	for _, kind := range kinds {
		descriptors = append(descriptors, systemCardKindSemantics[kind])
	}
	return descriptors
}

func IsPublicHumanCreateMessageTypeAllowed(messageType string) bool {
	normalized := NormalizeAppMessageType(messageType)
	if normalized == "" && strings.TrimSpace(messageType) != "" {
		return false
	}
	descriptor := ClassifyMessageSemantics(normalized, nil)
	if !descriptor.Registered || descriptor.Class != MessageClassTranscript || !descriptor.CurrentWriteAllowed() {
		return false
	}
	switch normalized {
	case MessageTypeChannelShareCard, MessageTypeThreadShareCard, MessageTypeTaskDescriptionAttachment, MessageTypeAppInteractiveCard, MessageTypeServiceAccountPost:
		return false
	default:
		return true
	}
}

// ClassifyMessageSemantics returns the descriptor for an existing row or
// proposed write. Unknown message types remain transcript-visible for read
// compatibility. system.card.kind is no longer a read compatibility subtype:
// upgrade migrations remove retained system-card rows, and active writers are
// gated before append so the shape cannot re-enter hot storage.
func ClassifyMessageSemantics(messageType string, metadata map[string]any) MessageSemantics {
	trimmedType := strings.TrimSpace(messageType)
	if descriptor, ok := messageTypeSemantics[trimmedType]; ok {
		return applyLifecycleSemantics(descriptor, metadata)
	}
	return applyLifecycleSemantics(MessageSemantics{
		Registered:              false,
		MessageType:             trimmedType,
		Class:                   MessageClassTranscript,
		RenderKind:              MessageRenderText,
		HumanVisible:            true,
		ReadCapability:          MessageReadUnregisteredCompat,
		MutationCapability:      MessageMutationStandard,
		RenderCapability:        MessageRenderCapabilityInline,
		DeliveryProfile:         MessageDeliveryContentRouting,
		ParticipationProfile:    MessageParticipationCounted,
		AgentUnreadProfile:      MessageAgentUnreadCounted,
		ModerationProfile:       MessageModerationUserContent,
		CurrentWriteDisposition: MessageWriteDisallowed,
		TargetWriteDisposition:  MessageWriteDisallowed,
		RemovalCondition:        fmt.Sprintf("register message type %q before allowing target-state writes", trimmedType),
	}, metadata)
}

func applyLifecycleSemantics(semantics MessageSemantics, metadata map[string]any) MessageSemantics {
	if !MessageMetadataIsRewound(metadata) {
		return semantics
	}
	semantics.HumanVisible = false
	semantics.ReadCapability = MessageReadHiddenEnvelope
	semantics.RenderKind = MessageRenderNone
	semantics.RenderCapability = MessageRenderCapabilityNone
	semantics.DeliveryProfile = MessageDeliveryNone
	semantics.ParticipationProfile = MessageParticipationExcluded
	semantics.AgentUnreadProfile = MessageAgentUnreadExcluded
	semantics.ModerationProfile = MessageModerationInternalSkip
	semantics.MutationCapability = MessageMutationNone
	semantics.CurrentWriteDisposition = MessageWriteDisallowed
	semantics.TargetWriteDisposition = MessageWriteDisallowed
	return semantics
}

func ClassifyScopeMessageSemantics(record ScopeMessageRecord) MessageSemantics {
	return ClassifyMessageSemantics(record.MessageType, metadataMapFromJSON(record.Metadata))
}

func ClassifyAppMessageSemantics(message AppMessage) MessageSemantics {
	return ClassifyMessageSemantics(message.MessageType, message.Metadata)
}

func SystemCardKind(metadata map[string]any) string {
	card, ok := metadata["card"].(map[string]any)
	if !ok {
		return ""
	}
	kind, ok := card["kind"]
	if !ok || kind == nil {
		return ""
	}
	return strings.TrimSpace(fmt.Sprint(kind))
}
