package im

import (
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"strconv"
	"testing"

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

	"github.com/tidbcloud/AgentTeam/internal/store/channel"
	"github.com/tidbcloud/AgentTeam/internal/store/message"
)

func TestScopeSnapshotRegisteredRouteSerializesChannelAdmission(t *testing.T) {
	tests := []struct {
		name       string
		resolution channel.ChannelAccessResolution
		wantStatus int
		wantError  string
	}{
		{
			name: "joined",
			resolution: channel.ChannelAccessResolution{
				Status:  channel.ChannelAccessStatusJoined,
				Channel: message.ChannelSummary{ChannelID: "chn_1", Joined: true},
			},
			wantStatus: http.StatusOK,
		},
		{
			name: "public joinable",
			resolution: channel.ChannelAccessResolution{
				Status: channel.ChannelAccessStatusPublicJoinable,
				JoinableChannel: channel.DiscoverableChannel{
					ChannelID:  "chn_1",
					Visibility: "public",
				},
			},
			wantStatus: http.StatusForbidden,
			wantError:  "forbidden",
		},
		{
			name:       "inaccessible",
			resolution: channel.ChannelAccessResolution{Status: channel.ChannelAccessStatusInaccessible},
			wantStatus: http.StatusForbidden,
			wantError:  "forbidden",
		},
		{
			name:       "not found",
			resolution: channel.ChannelAccessResolution{Status: channel.ChannelAccessStatusNotFound},
			wantStatus: http.StatusNotFound,
			wantError:  "not found",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			gin.SetMode(gin.TestMode)
			access := &fakeScopeSnapshotAccessResolver{resolution: tt.resolution}
			messages := &fakeScopeSnapshotMessageStore{page: message.AppMessagePage{
				Messages: []message.AppMessage{{ID: "msg_1", ChannelID: "chn_1"}},
			}}
			members := &fakeScopeSnapshotChannelMemberStore{}
			router := gin.New()
			RegisterRoutes(router, Dependencies{
				SessionAuth:    fakeWSAuth{},
				WorkspaceStore: fakeWSWorkspaceResolver{},
				ChannelAccess:  access,
				Messages:       messages,
				ChannelMembers: members,
			})

			recorder := httptest.NewRecorder()
			request := httptest.NewRequest(
				http.MethodGet,
				"/api/v1/workspaces/wsp_1/scopes/chn_1/snapshot?include=messages,members",
				nil,
			)
			request.Header.Set("Authorization", "Bearer token")
			router.ServeHTTP(recorder, request)

			require.Equal(t, tt.wantStatus, recorder.Code, recorder.Body.String())
			if tt.wantStatus == http.StatusOK {
				require.JSONEq(t, `{
					"messages": {
						"messages": [{
							"id": "msg_1",
							"seq": 0,
							"channelId": "chn_1",
							"content": "",
							"createdAt": "0001-01-01T00:00:00Z"
						}],
						"hasMore": false
					},
					"members": {
						"agents": [],
						"humans": [],
						"totalCount": 0,
						"hasMore": false,
						"nextCursor": ""
					}
				}`, recorder.Body.String())
				var body scopeSnapshotResponse
				require.NoError(t, json.Unmarshal(recorder.Body.Bytes(), &body))
				require.Equal(t, "msg_1", body.Messages.Messages[0].ID)
				require.NotNil(t, body.Members)
				require.Equal(t, 1, messages.calls)
				require.Equal(t, 1, members.calls)
				return
			}
			require.JSONEq(t, `{"error":`+strconv.Quote(tt.wantError)+`}`, recorder.Body.String())
			require.NotContains(t, recorder.Body.String(), "msg_1")
			require.NotContains(t, recorder.Body.String(), "members")
			require.Zero(t, messages.calls)
			require.Zero(t, members.calls)
		})
	}
}

func TestScopeSnapshotRegisteredRouteFailsClosedWithoutChannelAccess(t *testing.T) {
	gin.SetMode(gin.TestMode)
	messages := &fakeScopeSnapshotMessageStore{}
	members := &fakeScopeSnapshotChannelMemberStore{}
	router := gin.New()
	RegisterRoutes(router, Dependencies{
		SessionAuth:    fakeWSAuth{},
		WorkspaceStore: fakeWSWorkspaceResolver{},
		Messages:       messages,
		ChannelMembers: members,
	})

	recorder := httptest.NewRecorder()
	request := httptest.NewRequest(
		http.MethodGet,
		"/api/v1/workspaces/wsp_1/scopes/chn_1/snapshot?include=messages,members",
		nil,
	)
	request.Header.Set("Authorization", "Bearer token")
	router.ServeHTTP(recorder, request)

	require.Equal(t, http.StatusInternalServerError, recorder.Code, recorder.Body.String())
	require.JSONEq(t, `{"error":"scope snapshot channel access dependency is not configured"}`, recorder.Body.String())
	require.Zero(t, messages.calls)
	require.Zero(t, members.calls)
}

func TestScopeSnapshotRegisteredRoutePreservesGenericMessageOnlyScope(t *testing.T) {
	gin.SetMode(gin.TestMode)
	access := &fakeScopeSnapshotAccessResolver{
		resolution: channel.ChannelAccessResolution{Status: channel.ChannelAccessStatusNotFound},
	}
	messages := &fakeScopeSnapshotMessageStore{page: message.AppMessagePage{
		Messages: []message.AppMessage{{ID: "msg_thread", ChannelID: "thr_1"}},
	}}
	router := gin.New()
	RegisterRoutes(router, Dependencies{
		SessionAuth:    fakeWSAuth{},
		WorkspaceStore: fakeWSWorkspaceResolver{},
		ChannelAccess:  access,
		Messages:       messages,
	})

	recorder := httptest.NewRecorder()
	request := httptest.NewRequest(
		http.MethodGet,
		"/api/v1/workspaces/wsp_1/scopes/thr_1/snapshot?include=messages",
		nil,
	)
	request.Header.Set("Authorization", "Bearer token")
	router.ServeHTTP(recorder, request)

	require.Equal(t, http.StatusOK, recorder.Code, recorder.Body.String())
	require.Contains(t, recorder.Body.String(), `"id":"msg_thread"`)
	require.Zero(t, access.calls, "generic message-only scopes retain their existing store authorization")
	require.Equal(t, 1, messages.calls)
}
