package channel

import (
	"bytes"
	"net/http"
	"net/http/httptest"
	"testing"

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

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

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

	handler := &ChannelCRUDHandler{}
	router := gin.New()
	router.GET("/api/channels", handleChannelWorkspace(handler.List))

	recorder := httptest.NewRecorder()
	request := httptest.NewRequest(http.MethodGet, "/api/channels", nil)
	router.ServeHTTP(recorder, request)

	require.Equal(t, http.StatusUnauthorized, recorder.Code)
	require.Contains(t, recorder.Body.String(), "Unauthorized")
}

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

	handler := &ChannelCRUDHandler{}
	router := gin.New()
	router.POST("/api/channels", withChannelAuth(handleChannelWorkspaceJSON[dto.ChannelCreateRequest](handler.Create)))

	recorder := httptest.NewRecorder()
	request := httptest.NewRequest(http.MethodPost, "/api/channels", bytes.NewBufferString(`{"name"`))
	request.Header.Set("Content-Type", "application/json")
	router.ServeHTTP(recorder, request)

	require.Equal(t, http.StatusBadRequest, recorder.Code)
	require.Contains(t, recorder.Body.String(), "Invalid request body")
}

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

	handler := &ChannelDMHandler{}
	router := gin.New()
	router.POST("/api/channels/dm", withChannelAuth(handleChannelWorkspaceJSON[dto.OpenDMRequest](handler.OpenDM)))

	recorder := httptest.NewRecorder()
	request := httptest.NewRequest(http.MethodPost, "/api/channels/dm", bytes.NewBufferString(`{"agentId":"agt_1","userId":"usr_2"}`))
	request.Header.Set("Content-Type", "application/json")
	router.ServeHTTP(recorder, request)

	require.Equal(t, http.StatusBadRequest, recorder.Code)
	require.Contains(t, recorder.Body.String(), "mutually exclusive")
}

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

	handler := &ChannelMembershipHandler{}
	router := gin.New()
	router.POST("/api/channels/:channelId/members", withChannelAuth(handleChannelWorkspaceJSON[dto.AddChannelMemberRequest](handler.AddMember)))

	recorder := httptest.NewRecorder()
	request := httptest.NewRequest(http.MethodPost, "/api/channels/chn_1/members", bytes.NewBufferString(`{}`))
	request.Header.Set("Content-Type", "application/json")
	router.ServeHTTP(recorder, request)

	require.Equal(t, http.StatusBadRequest, recorder.Code)
	require.Contains(t, recorder.Body.String(), "required")
}

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

	store := &channelHandlerTestStore{}
	unread := &fakeChannelUnreadReader{}
	handler := &ChannelCRUDHandler{appStore: store, unreadRuntime: unread}

	response, err := handler.Delete(channelWorkspaceRequest(gin.Param{Key: "channelId", Value: " chn_1 "}))
	require.NoError(t, err)
	require.Equal(t, dto.EmptyResponse{}, response.Body)
	require.Equal(t, "chn_1", store.deleted.channelID)
	require.Equal(t, []unreadservice.ScopeInvalidated{{
		WorkspaceID: "wsp_1",
		ScopeID:     "chn_1",
		MemberIDs:   []string{"mem_1"},
		Reason:      "channel_delete",
	}}, unread.invalidated)
}

func withChannelAuth(handler gin.HandlerFunc) gin.HandlerFunc {
	return func(ctx *gin.Context) {
		api.SetAuthenticatedUser(ctx, identity.AuthenticatedUser{User: identity.User{ID: "usr_1"}})
		api.SetCurrentWorkspace(ctx, workspace.Summary{WorkspaceID: "wsp_1"})
		handler(ctx)
	}
}
