package im

import (
	"context"
	"fmt"
	"net/http"
	"strings"

	"github.com/tidbcloud/AgentTeam/internal/api"
	"github.com/tidbcloud/AgentTeam/internal/api/dto"
	"github.com/tidbcloud/AgentTeam/internal/api/dto/converter"
	"github.com/tidbcloud/AgentTeam/internal/api/im/fanout"
	storeim "github.com/tidbcloud/AgentTeam/internal/store/im"
	"github.com/tidbcloud/AgentTeam/internal/store/message"
)

// ReactionsHandler owns workspace-prefixed message reaction routes.
type ReactionsHandler struct {
	reactions          ReactionStore
	streamWriter       StreamEventWriter
	taskScopeLifecycle fanout.TaskScopeLifecycleGuard
}

// Get handles GET /api/v1/workspaces/:workspaceId/scopes/:scopeId/messages/:messageId/reactions.
func (h *ReactionsHandler) Get(r api.WorkspaceScopedRequest) (api.Response, error) {
	if h.reactions == nil {
		return api.Response{}, api.NewHTTPError(http.StatusInternalServerError, "Internal server error")
	}
	messageID, err := routeMessageID(r)
	if err != nil {
		return api.Response{}, err
	}
	reactions, err := h.reactions.GetMessageReactionsForUser(r.Context(), r.UserID(), r.WorkspaceID(), messageID)
	if err != nil {
		return api.Response{}, err
	}
	return api.OK(dto.AppMessageReactionListResponse{Reactions: converter.ToMessageReactionAggregates(reactions)}), nil
}

// Add handles PUT /api/v1/workspaces/:workspaceId/scopes/:scopeId/messages/:messageId/reactions/:emoji.
func (h *ReactionsHandler) Add(r api.WorkspaceScopedRequest) (api.Response, error) {
	if h.reactions == nil {
		return api.Response{}, api.NewHTTPError(http.StatusInternalServerError, "Internal server error")
	}
	messageID, err := routeMessageID(r)
	if err != nil {
		return api.Response{}, err
	}
	emoji := strings.TrimSpace(r.Gin.Param("emoji"))
	if emoji == "" {
		return api.Response{}, api.NewHTTPError(http.StatusBadRequest, "emoji is required")
	}
	result, err := h.reactions.AddReactionForUser(r.Context(), r.UserID(), r.WorkspaceID(), messageID, emoji)
	if err != nil {
		return api.Response{}, err
	}
	reactions, err := h.reactions.GetMessageReactionsForUser(r.Context(), r.UserID(), r.WorkspaceID(), messageID)
	if err != nil {
		return api.Response{}, err
	}
	dtoReactions := converter.ToMessageReactionAggregates(reactions)
	if result.Changed {
		if err := h.publishReactionChanged(
			r.Context(),
			r.WorkspaceID(),
			result.ChannelID,
			result.ChannelType,
			messageID,
			dtoReactions,
			result.Notification,
		); err != nil {
			return api.Response{}, err
		}
	}
	return api.OK(dto.AppMessageReactionMutationResponse{Changed: result.Changed, Reactions: dtoReactions}), nil
}

// Remove handles DELETE /api/v1/workspaces/:workspaceId/scopes/:scopeId/messages/:messageId/reactions/:emoji.
func (h *ReactionsHandler) Remove(r api.WorkspaceScopedRequest) (api.Response, error) {
	if h.reactions == nil {
		return api.Response{}, api.NewHTTPError(http.StatusInternalServerError, "Internal server error")
	}
	messageID, err := routeMessageID(r)
	if err != nil {
		return api.Response{}, err
	}
	emoji := strings.TrimSpace(r.Gin.Param("emoji"))
	if emoji == "" {
		return api.Response{}, api.NewHTTPError(http.StatusBadRequest, "emoji is required")
	}
	result, err := h.reactions.RemoveReactionForUser(r.Context(), r.UserID(), r.WorkspaceID(), messageID, emoji)
	if err != nil {
		return api.Response{}, err
	}
	reactions, err := h.reactions.GetMessageReactionsForUser(r.Context(), r.UserID(), r.WorkspaceID(), messageID)
	if err != nil {
		return api.Response{}, err
	}
	dtoReactions := converter.ToMessageReactionAggregates(reactions)
	if result.Changed {
		if err := h.publishReactionChanged(
			r.Context(),
			r.WorkspaceID(),
			result.ChannelID,
			result.ChannelType,
			messageID,
			dtoReactions,
			nil,
		); err != nil {
			return api.Response{}, err
		}
	}
	return api.OK(dto.AppMessageReactionMutationResponse{Changed: result.Changed, Reactions: dtoReactions}), nil
}

func (h *ReactionsHandler) publishReactionChanged(
	ctx context.Context,
	workspaceID string,
	channelID string,
	channelType string,
	messageID string,
	reactions []dto.MessageReactionAggregate,
	notification *message.HumanReactionNotification,
) error {
	channelID = strings.TrimSpace(channelID)
	isTask := strings.TrimSpace(channelType) == message.ConversationKindTask || strings.HasPrefix(channelID, "tsk_")
	if !isTask || h.taskScopeLifecycle == nil {
		return publishReactionChangedEvent(ctx, h.streamWriter, workspaceID, channelID, messageID, reactions, notification)
	}
	if channelID == "" {
		return fmt.Errorf("Task reaction channel is required")
	}
	protectedCtx := storeim.WithTaskLifecycleProtectedScopes(ctx, workspaceID, []string{channelID})
	_, err := h.taskScopeLifecycle.WithActiveTaskScopes(ctx, workspaceID, []string{channelID}, func() error {
		return publishReactionChangedEvent(protectedCtx, h.streamWriter, workspaceID, channelID, messageID, reactions, notification)
	})
	if err != nil {
		return fmt.Errorf("guard Task reaction publication: %w", err)
	}
	return nil
}

func routeMessageID(r api.WorkspaceScopedRequest) (string, error) {
	messageID := strings.TrimSpace(r.Gin.Param("messageId"))
	if messageID == "" {
		return "", api.NewHTTPError(http.StatusBadRequest, "messageId is required")
	}
	return messageID, nil
}
