package workspace

import (
	"errors"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
	"time"

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

	"github.com/tidbcloud/AgentTeam/internal/api"
	"github.com/tidbcloud/AgentTeam/internal/api/dto"
	workspaceservice "github.com/tidbcloud/AgentTeam/internal/service/workspace"
	"github.com/tidbcloud/AgentTeam/internal/store/identity"
	storeworkspace "github.com/tidbcloud/AgentTeam/internal/store/workspace"
)

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

	service := &fakeWorkspaceService{}
	handler := &WorkspaceHandler{workspaceService: service}
	response, err := handler.ChannelSidebarPreference(channelSidebarPreferenceRequest(t, http.MethodGet))

	require.NoError(t, err)
	require.Equal(t, []string{"usr_1", "wsp_1"}, service.channelSidebarGetArgs)
	body := response.Body.(dto.WorkspaceChannelSidebarPreferenceResponse)
	require.Nil(t, body.Preference)
	require.Nil(t, body.Revision)
	require.Nil(t, body.UpdatedAt)
}

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

	updatedAt := time.Date(2026, 8, 7, 3, 4, 5, 0, time.UTC)
	service := &fakeWorkspaceService{
		channelSidebarPreference: workspaceservice.ChannelSidebarPreference{Revision: 7, UpdatedAt: updatedAt},
	}
	handler := &WorkspaceHandler{workspaceService: service}
	expectedRevision := int64(6)
	request := api.WorkspaceScopedJSON[dto.WorkspaceChannelSidebarPreferenceUpdateRequest]{
		WorkspaceScopedRequest: channelSidebarPreferenceRequest(t, http.MethodPut),
		Body: dto.WorkspaceChannelSidebarPreferenceUpdateRequest{
			ExpectedRevision: &expectedRevision,
			Preference: dto.WorkspaceChannelSidebarPreferenceState{
				Sections: []dto.WorkspaceChannelSidebarSection{{
					ID:         "section_1",
					Name:       "Design",
					Emoji:      "🎨",
					ChannelIDs: []string{"chn_1"},
				}},
				Collapsed: map[string]bool{"section_1": true},
			},
		},
	}

	response, err := handler.PutChannelSidebarPreference(request)

	require.NoError(t, err)
	require.Equal(t, []string{"usr_1", "wsp_1"}, service.channelSidebarPutArgs)
	require.EqualValues(t, 6, *service.channelSidebarRevision)
	require.Equal(t, "Design", service.channelSidebarPreference.State.Sections[0].Name)
	body := response.Body.(dto.WorkspaceChannelSidebarPreferenceResponse)
	require.NotNil(t, body.Preference)
	require.Equal(t, []string{"chn_1"}, body.Preference.Sections[0].ChannelIDs)
	require.EqualValues(t, 7, *body.Revision)
	require.Equal(t, updatedAt, *body.UpdatedAt)
}

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

	recorder := httptest.NewRecorder()
	ctx, _ := gin.CreateTestContext(recorder)
	ctx.Request = httptest.NewRequest(
		http.MethodPut,
		"/api/v1/workspaces/wsp_1/sidebar/channel-sections",
		strings.NewReader(`{"preference":{"sections":[],"collapsed":{}},"padding":"`+
			strings.Repeat("x", int(maxChannelSidebarPreferenceBodyBytes))+
			`"}`),
	)
	ctx.Request.Header.Set("Content-Type", "application/json")
	limitChannelSidebarPreferenceBody(ctx)

	_, err := api.BindJSON[dto.WorkspaceChannelSidebarPreferenceUpdateRequest](ctx)
	var maxBytesError *http.MaxBytesError
	require.Error(t, err)
	require.True(t, errors.As(err, &maxBytesError))
}

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

	state := channelSidebarPreferenceStateResponse(workspaceservice.ChannelSidebarPreferenceState{
		Sections: []workspaceservice.ChannelSidebarSection{{
			ID:    "section_1",
			Name:  "Design",
			Emoji: "🎨",
		}},
		Collapsed: map[string]bool{},
	})

	require.NotNil(t, state.Sections)
	require.NotNil(t, state.Sections[0].ChannelIDs)
	require.NotNil(t, state.Collapsed)
}

func channelSidebarPreferenceRequest(t *testing.T, method string) api.WorkspaceScopedRequest {
	t.Helper()
	ctx, _ := gin.CreateTestContext(httptest.NewRecorder())
	ctx.Request = httptest.NewRequest(method, "/api/v1/workspaces/wsp_1/sidebar/channel-sections", http.NoBody)
	return api.WorkspaceScopedRequest{
		AuthenticatedRequest: api.AuthenticatedRequest{
			Gin:  ctx,
			User: identity.AuthenticatedUser{User: identity.User{ID: "usr_1"}},
		},
		Workspace: storeworkspace.Summary{WorkspaceID: "wsp_1", MemberID: "mem_1"},
	}
}
