package fanout

import (
	"context"
	"fmt"
	"strings"

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

type ScheduleOwnerResolver interface {
	LoadHumanOwner(ctx context.Context, workspaceID string, ownerMemberID string) (identity.User, error)
}

// ScheduleEffectWriter persists schedule automation effects through the normal
// human message creation path, then emits the same daemon/browser fan-out as
// POST Workspace IM message create.
type ScheduleEffectWriter struct {
	owners    ScheduleOwnerResolver
	messaging HumanMessageWriter
	fanout    DeliveryFanout
}

// NewScheduleEffectWriter builds the production writer for Schedule execution.
func NewScheduleEffectWriter(owners ScheduleOwnerResolver, dependencies Dependencies) ScheduleEffectWriter {
	return ScheduleEffectWriter{
		owners:    owners,
		messaging: dependencies.HumanMessages,
		fanout: NewDeliveryFanout(dependencies.Hub, dependencies.StreamWriter, dependencies.Logger).
			WithReplyThreadRootPreloader(dependencies.ReplyThreadRootPreloader).
			WithScopeFanoutMarker(dependencies.ScopeFanoutMarker).
			WithTaskScopeLifecycleGuard(dependencies.TaskScopeLifecycle).
			WithUnreadRuntime(dependencies.UnreadRuntime).
			WithUnreadDeltaSink(dependencies.UnreadDeltaSink),
	}
}

// WriteScheduleEffect writes exactly one visible effect for a reserved schedule
// automation run.
func (writer ScheduleEffectWriter) WriteScheduleEffect(ctx context.Context, request schedule.EffectRequest) (runeffect.Effect, error) {
	if err := schedule.ValidateEffectRequest(request); err != nil {
		return runeffect.Effect{}, err
	}
	if writer.owners == nil {
		return runeffect.Effect{}, fmt.Errorf("schedule owner resolver is not configured")
	}
	if writer.messaging == nil {
		return runeffect.Effect{}, fmt.Errorf("schedule message writer service is nil")
	}
	owner, err := writer.owners.LoadHumanOwner(ctx, request.WorkspaceID, request.OwnerMemberID)
	if err != nil {
		return runeffect.Effect{}, err
	}
	appMessage, delivery, err := SendAndFanoutHumanMessage(ctx, writer.messaging, writer.fanout, owner, request.WorkspaceID, request.TargetRef, request.Content, nil, message.SendOptions{
		Metadata:               request.Metadata,
		Source:                 request.Source,
		MentionHints:           request.MentionHints,
		ClientMessageID:        request.ClientMessageID,
		ExpectedAuthorMemberID: request.AuthorMemberID,
		StartAgentThread:       request.AlsoStartThread,
	})
	if err != nil {
		return runeffect.Effect{}, err
	}
	messageID := strings.TrimSpace(delivery.MessageID)
	if messageID == "" {
		messageID = strings.TrimSpace(appMessage.ID)
	}
	if messageID == "" {
		return runeffect.Effect{}, fmt.Errorf("schedule message writer returned no message id")
	}
	if strings.TrimSpace(request.TargetKind) == schedulestore.TargetKindTask {
		return runeffect.TaskDiscussionEffect(messageID), nil
	}
	return runeffect.MessageEffect(messageID), nil
}

var _ schedule.EffectWriter = ScheduleEffectWriter{}
