package fanout

import (
	"context"
	"errors"
	"testing"
	"time"

	"github.com/stretchr/testify/require"

	"github.com/tidbcloud/AgentTeam/internal/core/runeffect"
	"github.com/tidbcloud/AgentTeam/internal/schedule"
	messageservice "github.com/tidbcloud/AgentTeam/internal/service/message"
	"github.com/tidbcloud/AgentTeam/internal/store/identity"
	"github.com/tidbcloud/AgentTeam/internal/store/message"
	schedulestore "github.com/tidbcloud/AgentTeam/internal/store/schedule"
)

type fakeScheduleOwnerQueryer struct {
	err error
}

func (queryer fakeScheduleOwnerQueryer) LoadHumanOwner(context.Context, string, string) (identity.User, error) {
	if queryer.err != nil {
		return identity.User{}, queryer.err
	}
	return identity.User{
		ID:          "usr_schedule_owner",
		Email:       "owner@example.com",
		Name:        "owner",
		DisplayName: "Owner",
		CreatedAt:   time.Date(2026, 5, 30, 8, 0, 0, 0, time.UTC),
		UpdatedAt:   time.Date(2026, 5, 30, 8, 0, 0, 0, time.UTC),
	}, nil
}

type recordingScheduleHumanWriter struct {
	owner     identity.User
	workspace string
	target    string
	content   string
	options   messageservice.SendOptions
}

func (writer *recordingScheduleHumanWriter) ValidateHumanMessage(context.Context, identity.User, string, string, string, messageservice.SendOptions) error {
	return nil
}

func (writer *recordingScheduleHumanWriter) SendHumanMessage(_ context.Context, owner identity.User, workspaceID string, targetRef string, content string, _ []string, options messageservice.SendOptions) (message.AppMessage, message.MessageDelivery, error) {
	writer.owner = owner
	writer.workspace = workspaceID
	writer.target = targetRef
	writer.content = content
	writer.options = options
	return message.AppMessage{
			ID:      "msg_schedule_effect",
			Content: content,
			Source:  options.Source,
		}, message.MessageDelivery{
			MessageID: "msg_schedule_effect",
		}, nil
}

func TestScheduleEffectWriterCreatesHumanMessageEffect(t *testing.T) {
	humanWriter := &recordingScheduleHumanWriter{}
	writer := ScheduleEffectWriter{
		owners:    fakeScheduleOwnerQueryer{},
		messaging: humanWriter,
	}
	source := message.ContributionSourceForAutomationRun(message.AutomationRunContributionSourceParams{
		RunID:               "arun_1",
		AutomationID:        "sch_1",
		AutomationName:      "Daily check",
		SourceType:          "one_shot_absolute",
		SourceOccurrenceRef: "2026-05-30T09:00:00Z",
	})

	effect, err := writer.WriteScheduleEffect(context.Background(), schedule.EffectRequest{
		WorkspaceID:     "wsp_1",
		TargetRef:       "chn_1",
		Content:         "hello from schedule",
		AlsoStartThread: true,
		MentionHints: []message.MessageMentionHint{{
			Type:  "agent",
			ID:    "agt_1",
			Token: "old-helper",
		}},
		Source:          source,
		ClientMessageID: "schedule:sch_1:2026-05-30T09:00:00Z",
		OwnerMemberID:   "mem_owner",
		AuthorMemberID:  "mem_owner",
	})

	require.NoError(t, err)
	require.Equal(t, runeffect.MessageEffect("msg_schedule_effect"), effect)
	require.Equal(t, "usr_schedule_owner", humanWriter.owner.ID)
	require.Equal(t, "wsp_1", humanWriter.workspace)
	require.Equal(t, "chn_1", humanWriter.target)
	require.Equal(t, "hello from schedule", humanWriter.content)
	require.Equal(t, source.Normalize(), humanWriter.options.Source)
	require.Equal(t, "schedule:sch_1:2026-05-30T09:00:00Z", humanWriter.options.ClientMessageID)
	require.Equal(t, "mem_owner", humanWriter.options.ExpectedAuthorMemberID)
	require.True(t, humanWriter.options.StartAgentThread)
	require.Equal(t, []message.MessageMentionHint{{
		Type:  "agent",
		ID:    "agt_1",
		Token: "old-helper",
	}}, humanWriter.options.MentionHints)
}

func TestScheduleEffectWriterMapsMissingOwnerToNotFound(t *testing.T) {
	writer := ScheduleEffectWriter{
		owners:    fakeScheduleOwnerQueryer{err: errors.New("schedule owner member is not active")},
		messaging: &recordingScheduleHumanWriter{},
	}

	_, err := writer.WriteScheduleEffect(context.Background(), schedule.EffectRequest{
		WorkspaceID:     "wsp_1",
		TargetRef:       "chn_1",
		Content:         "hello from schedule",
		Source:          message.ContributionSourceForAutomationRun(message.AutomationRunContributionSourceParams{RunID: "arun_1", AutomationID: "sch_1"}),
		ClientMessageID: "schedule:sch_1:once",
		OwnerMemberID:   "mem_missing",
	})

	require.ErrorContains(t, err, "schedule owner member is not active")
}

func TestScheduleEffectWriterReturnsTaskDiscussionEffectForTaskTarget(t *testing.T) {
	humanWriter := &recordingScheduleHumanWriter{}
	writer := ScheduleEffectWriter{owners: fakeScheduleOwnerQueryer{}, messaging: humanWriter}

	effect, err := writer.WriteScheduleEffect(context.Background(), schedule.EffectRequest{
		WorkspaceID:     "wsp_1",
		TargetKind:      schedulestore.TargetKindTask,
		TargetRef:       "tsk_abc123",
		Content:         "scheduled Task update",
		Source:          message.ContributionSourceForAutomationRun(message.AutomationRunContributionSourceParams{RunID: "arun_task", AutomationID: "sch_task"}),
		ClientMessageID: "schedule:sch_task:once",
		OwnerMemberID:   "mem_owner",
		AuthorMemberID:  "mem_owner",
	})

	require.NoError(t, err)
	require.Equal(t, runeffect.TaskDiscussionEffect("msg_schedule_effect"), effect)
	require.Equal(t, "tsk_abc123", humanWriter.target)
	require.False(t, humanWriter.options.StartAgentThread)
}
