package channel

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

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

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

type fakeChannelAttachmentStore struct {
	options         message.ChannelAttachmentListOptions
	page            message.ChannelAttachmentPage
	threadChannelID string
}

func (store *fakeChannelAttachmentStore) ListChannelAttachmentsForUser(_ context.Context, _ string, _ string, _ string, options message.ChannelAttachmentListOptions) (message.ChannelAttachmentPage, error) {
	store.options = options
	return store.page, nil
}

func (store *fakeChannelAttachmentStore) ListThreadAttachmentsForUser(_ context.Context, _ string, _ string, threadChannelID string, options message.ChannelAttachmentListOptions) (message.ChannelAttachmentPage, error) {
	store.threadChannelID = threadChannelID
	store.options = options
	return store.page, nil
}

func TestChannelAttachmentsHandlerListParsesFilters(t *testing.T) {
	store := &fakeChannelAttachmentStore{page: message.ChannelAttachmentPage{Items: []message.ChannelAttachmentItem{{ID: "att_1", Kind: message.ChannelAttachmentKindFile, Title: "notes.txt", CreatedAt: time.Date(2026, 6, 30, 8, 0, 0, 0, time.UTC), Message: message.ChannelAttachmentMessageLocation{MessageID: "msg_1", ScopeID: "chn_1", ScopeType: "channel", ParentChannelID: "chn_1"}}}}}
	handler := &ChannelAttachmentsHandler{appStore: store}
	request := channelWorkspaceRequest(gin.Param{Key: "channelId", Value: "chn_1"})
	request.Gin.Request = httptestRequestWithQuery("/api/v1/workspaces/wsp_1/channels/chn_1/attachments?kind=image&q=shot&fromMemberId=mem_1&includeThreads=false&limit=25&dateFrom=2026-06-01T00:00:00Z&dateTo=2026-06-30T23:59:59Z")

	response, err := handler.List(request)
	require.NoError(t, err)
	require.Equal(t, http.StatusOK, response.Status)
	body := response.Body.(dto.ChannelAttachmentPage)
	require.Len(t, body.Items, 1)
	require.Equal(t, "notes.txt", body.Items[0].Title)
	require.Equal(t, message.ChannelAttachmentKindImage, store.options.Kind)
	require.Equal(t, "shot", store.options.Query)
	require.Equal(t, "mem_1", store.options.FromMemberID)
	require.False(t, store.options.IncludeThreads)
	require.Equal(t, 25, store.options.Limit)
	require.NotNil(t, store.options.DateFrom)
	require.NotNil(t, store.options.DateTo)
}

func TestChannelAttachmentsHandlerListDefaultsToRecentThirty(t *testing.T) {
	store := &fakeChannelAttachmentStore{}
	handler := &ChannelAttachmentsHandler{appStore: store}
	request := channelWorkspaceRequest(gin.Param{Key: "channelId", Value: "chn_1"})
	request.Gin.Request = httptestRequestWithQuery("/api/v1/workspaces/wsp_1/channels/chn_1/attachments")

	response, err := handler.List(request)
	require.NoError(t, err)
	require.Equal(t, http.StatusOK, response.Status)
	require.Equal(t, 30, store.options.Limit)
	require.True(t, store.options.IncludeThreads)
}

func TestChannelAttachmentsHandlerListThreadParsesFilters(t *testing.T) {
	store := &fakeChannelAttachmentStore{page: message.ChannelAttachmentPage{Items: []message.ChannelAttachmentItem{{ID: "att_thread", Kind: message.ChannelAttachmentKindFile, Title: "thread.txt", CreatedAt: time.Date(2026, 6, 30, 8, 0, 0, 0, time.UTC), Message: message.ChannelAttachmentMessageLocation{MessageID: "msg_thread", ScopeID: "thr_1", ScopeType: "thread", ParentChannelID: "chn_1", ThreadChannelID: "thr_1"}}}}}
	handler := &ChannelAttachmentsHandler{appStore: store}
	request := channelWorkspaceRequest(gin.Param{Key: "threadChannelId", Value: "thr_1"})
	request.Gin.Request = httptestRequestWithQuery("/api/v1/workspaces/wsp_1/threads/thr_1/attachments?kind=image&q=shot&fromMemberId=mem_1&limit=15")

	response, err := handler.ListThread(request)
	require.NoError(t, err)
	require.Equal(t, http.StatusOK, response.Status)
	body := response.Body.(dto.ChannelAttachmentPage)
	require.Len(t, body.Items, 1)
	require.Equal(t, "thread.txt", body.Items[0].Title)
	require.Equal(t, message.ChannelAttachmentKindImage, store.options.Kind)
	require.Equal(t, "shot", store.options.Query)
	require.Equal(t, "mem_1", store.options.FromMemberID)
	require.Equal(t, "thr_1", store.threadChannelID)
	require.False(t, store.options.IncludeThreads)
	require.Equal(t, 15, store.options.Limit)
}

func TestChannelAttachmentsHandlerListThreadRejectsInvalidCursor(t *testing.T) {
	handler := &ChannelAttachmentsHandler{appStore: &fakeChannelAttachmentStore{}}
	request := channelWorkspaceRequest(gin.Param{Key: "threadChannelId", Value: "thr_1"})
	request.Gin.Request = httptestRequestWithQuery("/api/v1/workspaces/wsp_1/threads/thr_1/attachments?cursor=bad")

	_, err := handler.ListThread(request)
	requireHTTPError(t, err, http.StatusBadRequest, "invalid attachment cursor")
}

func TestChannelAttachmentsHandlerRejectsInvalidCursor(t *testing.T) {
	handler := &ChannelAttachmentsHandler{appStore: &fakeChannelAttachmentStore{}}
	request := channelWorkspaceRequest(gin.Param{Key: "channelId", Value: "chn_1"})
	request.Gin.Request = httptestRequestWithQuery("/api/v1/workspaces/wsp_1/channels/chn_1/attachments?cursor=bad")

	_, err := handler.List(request)
	requireHTTPError(t, err, http.StatusBadRequest, "invalid attachment cursor")
}

func httptestRequestWithQuery(target string) *http.Request {
	return httptest.NewRequest(http.MethodGet, target, nil)
}
