package message

import (
	"reflect"
	"sort"
	"testing"

	"github.com/stretchr/testify/require"
)

func TestMessageDeliveryDiagnosticsAgentIncludesScopeAuditFields(t *testing.T) {
	t.Parallel()

	agentType := reflect.TypeOf(MessageDeliveryDiagnosticsAgent{})

	_, hasScopeStatus := agentType.FieldByName("ScopeStatus")
	require.True(t, hasScopeStatus, "MessageDeliveryDiagnosticsAgent should expose ScopeStatus for audit explainability")

	_, hasScopeExplanation := agentType.FieldByName("ScopeExplanation")
	require.True(t, hasScopeExplanation, "MessageDeliveryDiagnosticsAgent should expose ScopeExplanation for audit explainability")
}

func TestMessageDeliveryDiagnosticsAgentIncludesRuntimeActivityFields(t *testing.T) {
	t.Parallel()

	agentType := reflect.TypeOf(MessageDeliveryDiagnosticsAgent{})

	_, hasAgentActivity := agentType.FieldByName("AgentActivity")
	require.True(t, hasAgentActivity, "MessageDeliveryDiagnosticsAgent should expose AgentActivity for inline runtime status")

	_, hasAgentActivityDetail := agentType.FieldByName("AgentActivityDetail")
	require.True(t, hasAgentActivityDetail, "MessageDeliveryDiagnosticsAgent should expose AgentActivityDetail for inline runtime status")
}

func TestMessageDeliveryDiagnosticsAgentIncludesDeliveryExecutionFields(t *testing.T) {
	t.Parallel()

	agentType := reflect.TypeOf(MessageDeliveryDiagnosticsAgent{})

	_, hasSubscriptionState := agentType.FieldByName("SubscriptionState")
	require.True(t, hasSubscriptionState, "MessageDeliveryDiagnosticsAgent should expose SubscriptionState for per-space delivery truth")

	_, hasExecutionResult := agentType.FieldByName("ExecutionResult")
	require.True(t, hasExecutionResult, "MessageDeliveryDiagnosticsAgent should expose ExecutionResult for delivery vs wake diagnostics")
}

func TestMessageDeliveryDiagnosticsAgentIncludesOwnerPrivateExplainabilityFields(t *testing.T) {
	t.Parallel()

	agentType := reflect.TypeOf(MessageDeliveryDiagnosticsAgent{})

	_, hasOwnerPrivateState := agentType.FieldByName("OwnerPrivateState")
	require.True(t, hasOwnerPrivateState, "MessageDeliveryDiagnosticsAgent should expose OwnerPrivateState for owner-private delivery explainability")

	_, hasOwnerPrivateExplanation := agentType.FieldByName("OwnerPrivateExplanation")
	require.True(t, hasOwnerPrivateExplanation, "MessageDeliveryDiagnosticsAgent should expose OwnerPrivateExplanation for owner-private delivery explainability")
}

func TestDeriveMessageDiagnosticsRecipientsFromScopeFiltersControlledBroadcastToAuthorOwnedAgents(t *testing.T) {
	t.Parallel()

	recipients := DeriveMessageDiagnosticsRecipientsFromScope(
		MessageDeliveryDiagnosticsMessage{
			ChannelType:    "channel",
			AuthorMemberID: "member-human",
			AuthorUserID:   "user-owner",
			AuthorType:     "human",
			Content:        "@all run this",
		},
		[]DiagnosticsAgentState{
			{
				AgentID:       "agent-owner-online",
				AgentName:     "owner-online",
				MemberID:      "member-owner-online",
				OwnerUserID:   "user-owner",
				MachineOnline: true,
			},
			{
				AgentID:       "agent-owner-offline",
				AgentName:     "owner-offline",
				MemberID:      "member-owner-offline",
				OwnerUserID:   "user-owner",
				MachineOnline: false,
			},
			{
				AgentID:       "agent-other-online",
				AgentName:     "other-online",
				MemberID:      "member-other-online",
				OwnerUserID:   "user-other",
				MachineOnline: true,
			},
		},
	)

	require.Equal(t, []string{"member-owner-offline", "member-owner-online"}, diagnosticsRecipientMemberIDs(recipients))
	for _, recipient := range recipients {
		require.Equal(t, AgentDeliveryReasonChannelBroadcastAll, recipient.DeliveryReason)
	}
}

func TestDeriveMessageDiagnosticsRecipientsFromScopeTreatsOnlineBroadcastAsBestEffortOwnedWake(t *testing.T) {
	t.Parallel()

	recipients := DeriveMessageDiagnosticsRecipientsFromScope(
		MessageDeliveryDiagnosticsMessage{
			ChannelType:    "channel",
			AuthorMemberID: "member-human",
			AuthorUserID:   "user-owner",
			AuthorType:     "human",
			Content:        "@online run this",
		},
		[]DiagnosticsAgentState{
			{
				AgentID:       "agent-owner-offline",
				AgentName:     "owner-offline",
				MemberID:      "member-owner-offline",
				OwnerUserID:   "user-owner",
				MachineOnline: false,
			},
			{
				AgentID:       "agent-other-online",
				AgentName:     "other-online",
				MemberID:      "member-other-online",
				OwnerUserID:   "user-other",
				MachineOnline: true,
			},
		},
	)

	require.Equal(t, []DiagnosticsAgentState{
		{
			AgentID:         "agent-owner-offline",
			AgentName:       "owner-offline",
			MemberID:        "member-owner-offline",
			OwnerUserID:     "user-owner",
			DeliveryReason:  AgentDeliveryReasonChannelBroadcastOnline,
			ExecutionResult: SpaceDeliveryResultWake,
		},
	}, recipients)
}

func TestDeriveMessageDiagnosticsRecipientsFromScopeDoesNotTreatAgentAtAllAsControlledBroadcast(t *testing.T) {
	t.Parallel()

	message := MessageDeliveryDiagnosticsMessage{
		ChannelType:    "channel",
		AuthorMemberID: "member-agent-author",
		AuthorType:     "agent",
		Content:        "@all status",
	}
	recipients := DeriveMessageDiagnosticsRecipientsFromScope(
		message,
		[]DiagnosticsAgentState{
			{
				AgentID:  "agent-author",
				MemberID: "member-agent-author",
			},
			{
				AgentID:  "agent-peer",
				MemberID: "member-agent-peer",
			},
		},
	)

	require.Equal(t, MessageDeliveryRoutingModeSilent, DetectMessageDeliveryRoutingMode(message, recipients))
	require.Equal(t, []string{"member-agent-peer"}, diagnosticsRecipientMemberIDs(recipients))
	require.Equal(t, AgentDeliveryReasonSpaceDefault, recipients[0].DeliveryReason)
}

func TestBuildMessageDeliveryDiagnosticsAgentsMarksDurableOnlyRecipientsOutsideCurrentScope(t *testing.T) {
	t.Parallel()

	agents := BuildMessageDeliveryDiagnosticsAgents(
		MessageDeliveryDiagnosticsMessage{
			AuthorMemberID: "human-member",
			Content:        "@alpha-agent please handle this",
		},
		MessageDeliveryRoutingModeDirectMentions,
		[]DiagnosticsAgentState{
			{
				AgentID:         "agent-beta",
				AgentName:       "beta-agent",
				MemberID:        "member-beta",
				MachineID:       "machine-primary",
				AutoWakeEnabled: true,
				MachineOnline:   true,
			},
			{
				AgentID:         "agent-gamma",
				AgentName:       "gamma-agent",
				MemberID:        "member-gamma",
				MachineID:       "machine-secondary",
				AutoWakeEnabled: true,
				MachineOnline:   true,
			},
		},
		[]DiagnosticsAgentState{
			{
				AgentID:         "agent-alpha",
				AgentName:       "alpha-agent",
				MemberID:        "member-alpha",
				MachineID:       "machine-primary",
				AgentStatus:     "active",
				AutoWakeEnabled: true,
				MachineOnline:   true,
				DeliveryReason:  AgentDeliveryReasonDirectMention,
			},
		},
		nil,
		map[string]struct{}{},
	)

	require.Len(t, agents, 1)
	byName := make(map[string]MessageDeliveryDiagnosticsAgent, len(agents))
	for _, agent := range agents {
		byName[agent.AgentName] = agent
	}

	alpha := byName["alpha-agent"]
	require.Equal(t, MessageDeliveryStatusTargeted, alpha.DeliveryStatus)
	require.Equal(t, MessageDeliveryScopeStatusPreviousScopeMember, alpha.ScopeStatus)
	require.Equal(t, ExplainMessageDeliveryScopeStatus(MessageDeliveryScopeStatusPreviousScopeMember), alpha.ScopeExplanation)
}

func diagnosticsRecipientMemberIDs(recipients []DiagnosticsAgentState) []string {
	ids := make([]string, 0, len(recipients))
	for _, recipient := range recipients {
		ids = append(ids, recipient.MemberID)
	}
	sort.Strings(ids)
	return ids
}

func TestMergeMessageDeliveryDiagnosticsAgentsPrefersCurrentMessageDurableFactsOverSourceFallback(t *testing.T) {
	t.Parallel()

	agents := MergeMessageDeliveryDiagnosticsAgents(
		nil,
		[]DiagnosticsAgentState{
			{
				AgentID:           "agent-alpha",
				AgentName:         "alpha-agent",
				MemberID:          "member-alpha",
				MachineID:         "machine-alpha",
				AutoWakeEnabled:   true,
				MachineOnline:     true,
				DeliveryReason:    AgentDeliveryReasonTaskCommandNotify,
				ExecutionResult:   SpaceDeliveryResultWake,
				SubscriptionState: "auto-wake",
			},
			{
				AgentID:           "agent-alpha",
				AgentName:         "alpha-agent",
				MemberID:          "member-alpha",
				MachineID:         "machine-alpha",
				AutoWakeEnabled:   true,
				MachineOnline:     true,
				DeliveryReason:    AgentDeliveryReasonOwnerQuote,
				ExecutionResult:   SpaceDeliveryResultNoWake,
				SubscriptionState: "no-wake",
			},
		},
	)

	require.Len(t, agents, 1)
	require.Equal(t, AgentDeliveryReasonTaskCommandNotify, agents[0].DeliveryReason)
	require.Equal(t, SpaceDeliveryResultNoWake, agents[0].ExecutionResult)
	require.Equal(t, "no-wake", agents[0].SubscriptionState)
}

func TestBuildMessageDeliveryDiagnosticsAgentsSkipsOwnerPrivateExplainabilityForOwnerQuoteMessages(t *testing.T) {
	t.Parallel()

	agents := BuildMessageDeliveryDiagnosticsAgents(
		MessageDeliveryDiagnosticsMessage{
			AuthorMemberID: "owner-member",
			AuthorType:     "user",
			ChannelType:    "channel",
		},
		MessageDeliveryRoutingModeOwnerQuote,
		[]DiagnosticsAgentState{
			{
				AgentID:         "agent-alpha",
				AgentName:       "alpha-agent",
				MemberID:        "member-alpha",
				MachineID:       "machine-primary",
				AutoWakeEnabled: true,
				MachineOnline:   true,
			},
			{
				AgentID:         "agent-beta",
				AgentName:       "beta-agent",
				MemberID:        "member-beta",
				MachineID:       "machine-primary",
				AutoWakeEnabled: true,
				MachineOnline:   true,
			},
		},
		[]DiagnosticsAgentState{
			{
				AgentID:         "agent-alpha",
				AgentName:       "alpha-agent",
				MemberID:        "member-alpha",
				MachineID:       "machine-primary",
				AutoWakeEnabled: true,
				MachineOnline:   true,
				DeliveryReason:  AgentDeliveryReasonOwnerQuote,
			},
		},
		map[string]struct{}{
			"agent-alpha": {},
		},
		nil,
	)

	require.Len(t, agents, 1)
	for _, agent := range agents {
		require.Empty(t, agent.OwnerPrivateState)
		require.Empty(t, agent.OwnerPrivateExplanation)
	}
}

func TestBuildMessageDeliveryDiagnosticsAgentsSkipsOwnerPrivateExplainabilityForControlledBroadcast(t *testing.T) {
	t.Parallel()

	agents := BuildMessageDeliveryDiagnosticsAgents(
		MessageDeliveryDiagnosticsMessage{
			AuthorMemberID: "owner-member",
			AuthorUserID:   "owner-user",
			AuthorType:     "user",
			ChannelType:    "channel",
			Content:        "@all run diagnostics",
		},
		MessageDeliveryRoutingModeBroadcastAll,
		[]DiagnosticsAgentState{
			{
				AgentID:         "agent-owned",
				AgentName:       "owned-agent",
				MemberID:        "member-owned",
				OwnerUserID:     "owner-user",
				MachineID:       "machine-primary",
				AutoWakeEnabled: true,
				MachineOnline:   true,
				DeliveryReason:  AgentDeliveryReasonChannelBroadcastAll,
			},
			{
				AgentID:         "agent-other",
				AgentName:       "other-agent",
				MemberID:        "member-other",
				OwnerUserID:     "other-user",
				MachineID:       "machine-primary",
				AutoWakeEnabled: true,
				MachineOnline:   true,
			},
		},
		nil,
		nil,
		nil,
	)

	require.Len(t, agents, 2)
	for _, agent := range agents {
		require.Empty(t, agent.OwnerPrivateState)
		require.Empty(t, agent.OwnerPrivateExplanation)
	}
	require.False(t, BuildMessageDeliveryDiagnosticsSummary(agents).OwnerPrivateRequired)
}
