package channel

import (
	"context"
	"net/http"
	"testing"

	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/require"

	"github.com/tidbcloud/AgentTeam/internal/api/dto"
	"github.com/tidbcloud/AgentTeam/internal/store/message"
	unreadservice "github.com/tidbcloud/AgentTeam/internal/unread"
)

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

	store := &channelHandlerTestStore{}
	realtime := &channelHandlerTestRealtime{}
	fanout := &channelHandlerTestFanout{}
	unread := &fakeChannelUnreadReader{workspaceHasUnread: true}
	handler := &ChannelShareCardHandler{appStore: store, realtime: realtime, messageFanout: fanout, unreadRuntime: unread}

	createResponse, err := handler.Create(channelWorkspaceJSONWithParams(dto.ChannelShareCardCreateRequest{TargetChannelID: " chn_all "}, gin.Param{Key: "channelId", Value: " chn_private "}))
	require.NoError(t, err)
	require.Equal(t, http.StatusOK, createResponse.Status)
	require.Equal(t, "usr_1", store.shareCreated.userID)
	require.Equal(t, "wsp_1", store.shareCreated.workspaceID)
	require.Equal(t, "chn_private", store.shareCreated.sourceChannelID)
	require.Equal(t, "chn_all", store.shareCreated.targetChannelID)
	require.Equal(t, "wsp_1:chn_all:msg_share", fanout.message)
	require.Equal(t, "msg_share", createResponse.Body.(dto.AppMessage).ID)

	shareResponse, err := handler.Share(channelWorkspaceJSONWithParams(dto.ChannelShareCardCreateRequest{TargetChannelID: " chn_team "}, gin.Param{Key: "messageId", Value: " msg_share "}))
	require.NoError(t, err)
	require.Equal(t, http.StatusOK, shareResponse.Status)
	require.Equal(t, "usr_1", store.shareShared.userID)
	require.Equal(t, "wsp_1", store.shareShared.workspaceID)
	require.Equal(t, "msg_share", store.shareShared.messageID)
	require.Equal(t, "chn_team", store.shareShared.targetChannelID)
	require.Equal(t, "wsp_1:chn_team:msg_share_again", fanout.message)
	require.Equal(t, "msg_share_again", shareResponse.Body.(dto.AppMessage).ID)

	getResponse, err := handler.Get(channelWorkspaceRequest(gin.Param{Key: "messageId", Value: " msg_share "}))
	require.NoError(t, err)
	require.Equal(t, "msg_share", store.shareLoaded.messageID)
	require.Equal(t, "joinable", getResponse.Body.(dto.ChannelShareCardView).Status)

	joinResponse, err := handler.Join(channelWorkspaceRequest(gin.Param{Key: "messageId", Value: " msg_share "}))
	require.NoError(t, err)
	require.Equal(t, "msg_share", store.shareJoined.messageID)
	require.Equal(t, []string{"wsp_1:usr_1:chn_private"}, realtime.scopeSubscribed)
	require.Equal(t, []string{"wsp_1"}, realtime.updated)
	require.Equal(t, "private", joinResponse.Body.(dto.ChannelResponse).Name)
	require.Equal(t, []unreadservice.ScopeInvalidated{{
		WorkspaceID: "wsp_1",
		ScopeID:     "chn_private",
		MemberIDs:   []string{"mem_1"},
		Reason:      "channel_share_card_join",
	}}, unread.invalidated)
	require.Equal(t, []unreadservice.UnreadDelta{
		channelSuppressedUnreadDelta("chn_private", true),
	}, realtime.unreadUpdated)

	revokeResponse, err := handler.Revoke(channelWorkspaceRequest(gin.Param{Key: "messageId", Value: " msg_share "}))
	require.NoError(t, err)
	require.Equal(t, "msg_share", store.shareRevoked.messageID)
	require.True(t, revokeResponse.Body.(dto.ChannelShareCardView).Revoked)
}

type channelHandlerTestFanout struct {
	message        string
	projectionOnly bool
}

func (f *channelHandlerTestFanout) EmitCommittedHumanMessage(_ context.Context, workspaceID string, appMessage message.AppMessage, delivery message.MessageDelivery) {
	f.message = workspaceID + ":" + appMessage.ChannelID + ":" + delivery.MessageID
}

func (f *channelHandlerTestFanout) EmitCommittedMessageProjectionOnly(_ context.Context, workspaceID string, appMessage message.AppMessage, delivery message.MessageDelivery) {
	f.message = workspaceID + ":" + appMessage.ChannelID + ":" + delivery.MessageID
	f.projectionOnly = true
}

func (f *channelHandlerTestFanout) EmitMessageUpdated(_ context.Context, workspaceID string, appMessage message.AppMessage) {
	f.message = workspaceID + ":" + appMessage.ChannelID + ":" + appMessage.ID + ":updated"
}

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

	handler := &ChannelShareCardHandler{appStore: &channelHandlerTestStore{}}

	_, err := handler.Create(channelWorkspaceJSONWithParams(dto.ChannelShareCardCreateRequest{TargetChannelID: "chn_all"}, gin.Param{Key: "channelId", Value: " "}))
	requireHTTPError(t, err, http.StatusBadRequest, "channelId is required")

	_, err = handler.Create(channelWorkspaceJSONWithParams(dto.ChannelShareCardCreateRequest{TargetChannelID: " "}, gin.Param{Key: "channelId", Value: "chn_private"}))
	requireHTTPError(t, err, http.StatusBadRequest, "targetChannelId is required")

	_, err = handler.Share(channelWorkspaceJSONWithParams(dto.ChannelShareCardCreateRequest{TargetChannelID: "chn_all"}, gin.Param{Key: "messageId", Value: " "}))
	requireHTTPError(t, err, http.StatusBadRequest, "messageId is required")

	_, err = handler.Share(channelWorkspaceJSONWithParams(dto.ChannelShareCardCreateRequest{TargetChannelID: " "}, gin.Param{Key: "messageId", Value: "msg_share"}))
	requireHTTPError(t, err, http.StatusBadRequest, "targetChannelId is required")

	_, err = handler.Get(channelWorkspaceRequest(gin.Param{Key: "messageId", Value: " "}))
	requireHTTPError(t, err, http.StatusBadRequest, "messageId is required")
}
