package channel

import (
	"context"
	"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/agent"
	"github.com/tidbcloud/AgentTeam/internal/store/message"
	"github.com/tidbcloud/AgentTeam/internal/store/model"
)

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

	appStore := &reviewFeedbackAppStoreStub{}
	handler := &ThreadHandler{
		reviewStore:   threadReviewStoreStub{rc: &agent.ReviewContext{ID: "rctx_1", ThreadID: "thr_1", SourceAgentID: "rcx_1"}},
		appStore:      appStore,
		messageFanout: &channelHandlerTestFanout{},
	}
	newLine := int64(7)
	startLine := int64(7)
	endLine := int64(9)
	response, err := handler.SubmitReviewFeedback(channelWorkspaceJSON(dto.ReviewFeedbackSubmitRequest{
		ContextID:       " rctx_1 ",
		ClientMessageID: "client-1",
		Locale:          "zh-CN",
		Summary:         "summary",
		Items: []dto.ReviewFeedbackItemRequest{{
			Type:              "line",
			Path:              "a.go",
			Side:              "new",
			NewLine:           &newLine,
			StartLine:         &startLine,
			EndLine:           &endLine,
			HunkHeader:        "@@",
			AnchorFingerprint: "fp",
			CodePreview:       "if err != nil {\nreturn err\n}",
			Body:              "fix this",
		}},
	}))
	require.NoError(t, err)
	require.Equal(t, http.StatusOK, response.Status)
	require.Equal(t, "rctx_1", appStore.createdRC.ID)
	require.Equal(t, "client-1", appStore.createdParams.ClientMessageID)
	require.Equal(t, "zh-CN", appStore.createdParams.Locale)
	require.Equal(t, "mem_1", appStore.createdParams.SubmittedByMemberID)
	require.Equal(t, "fix this", appStore.createdParams.Items[0].Body)
	require.Equal(t, "line", appStore.createdParams.Items[0].Type)
	require.Equal(t, "new", appStore.createdParams.Items[0].Side)
	require.Equal(t, newLine, *appStore.createdParams.Items[0].NewLine)
	require.Equal(t, startLine, *appStore.createdParams.Items[0].StartLine)
	require.Equal(t, endLine, *appStore.createdParams.Items[0].EndLine)
	require.Equal(t, "fp", appStore.createdParams.Items[0].AnchorFingerprint)
	require.Equal(t, "if err != nil {\nreturn err\n}", appStore.createdParams.Items[0].CodePreview)
	body := response.Body.(dto.ReviewFeedbackMessageResponse)
	require.Equal(t, "msg_review", body.Feedback.ID)
}

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

	appStore := &reviewFeedbackAppStoreStub{}
	handler := &ThreadHandler{
		reviewStore:   threadReviewStoreStub{rc: &agent.ReviewContext{ID: "rctx_1", ThreadID: "thr_1", SourceAgentID: "rcx_1"}},
		appStore:      appStore,
		messageFanout: &channelHandlerTestFanout{},
	}
	response, err := handler.SubmitReviewFeedback(channelWorkspaceJSON(dto.ReviewFeedbackSubmitRequest{
		ContextID:       "rctx_1",
		ClientMessageID: "client-approved",
		Locale:          "ja",
		Decision:        message.ReviewFeedbackDecisionApproved,
		Items:           []dto.ReviewFeedbackItemRequest{},
	}))
	require.NoError(t, err)
	require.Equal(t, http.StatusOK, response.Status)
	require.Equal(t, message.ReviewFeedbackDecisionApproved, appStore.createdParams.Decision)
	require.Empty(t, appStore.createdParams.Items)
}

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

	appStore := &reviewFeedbackAppStoreStub{}
	fanout := &channelHandlerTestFanout{}
	handler := &ThreadHandler{
		reviewStore:   threadReviewStoreStub{rc: &agent.ReviewContext{ID: "rctx_1", ThreadID: "chn_1", SourceAgentID: "rcx_1"}},
		appStore:      appStore,
		messageFanout: fanout,
	}

	listResponse, err := handler.ListReviewFeedback(channelWorkspaceRequestWithQuery("contextId=rctx_1"))
	require.NoError(t, err)
	require.Equal(t, http.StatusOK, listResponse.Status)
	require.Equal(t, 100, appStore.listLimit)
	require.Len(t, listResponse.Body.(dto.ReviewFeedbackListResponse).Feedback, 1)

	resolveResponse, err := handler.UpdateReviewFeedbackItemStatus(channelWorkspaceJSONWithParams(dto.ReviewFeedbackStatusRequest{
		ContextID: "rctx_1",
		Status:    message.ReviewFeedbackStatusResolved,
	},
		gin.Param{Key: "messageId", Value: " msg_review "},
		gin.Param{Key: "itemId", Value: " item_1 "},
	))
	require.NoError(t, err)
	require.Equal(t, http.StatusOK, resolveResponse.Status)
	require.Equal(t, message.ReviewFeedbackStatusResolved, appStore.status)
	require.Equal(t, "item_1", appStore.itemID)
	require.Equal(t, "wsp_1:chn_1:msg_review:updated", fanout.message)

	_, err = handler.ReopenReviewFeedbackItem(channelWorkspaceRequest(
		gin.Param{Key: "reviewContextId", Value: "rctx_1"},
		gin.Param{Key: "messageId", Value: "msg_review"},
		gin.Param{Key: "itemId", Value: "item_1"},
	))
	require.NoError(t, err)
	require.Equal(t, message.ReviewFeedbackStatusOpen, appStore.status)
}

func channelWorkspaceRequestWithQuery(query string, params ...gin.Param) api.WorkspaceScopedRequest {
	req := channelWorkspaceRequest(params...)
	req.Gin.Request = httptest.NewRequest(http.MethodGet, "/?"+query, nil)
	return req
}

type reviewFeedbackAppStoreStub struct {
	createdRC     agent.ReviewContext
	createdParams message.ReviewFeedbackBuildRequest
	listLimit     int
	status        string
	itemID        string
}

func (s *reviewFeedbackAppStoreStub) CreateThreadShareCardForUser(context.Context, model.User, string, string, string) (message.AppMessage, message.MessageDelivery, error) {
	return message.AppMessage{}, message.MessageDelivery{}, nil
}

func (s *reviewFeedbackAppStoreStub) LoadThreadShareCardForUser(context.Context, string, string, string) (message.ThreadShareCardView, error) {
	return message.ThreadShareCardView{}, nil
}

func (s *reviewFeedbackAppStoreStub) CreateReviewFeedbackForUser(_ context.Context, _ model.User, _ string, rc agent.ReviewContext, params message.ReviewFeedbackBuildRequest) (message.AppMessage, message.MessageDelivery, error) {
	s.createdRC = rc
	s.createdParams = params
	return message.AppMessage{ID: "msg_review", ChannelID: rc.ThreadID, Content: "Review feedback"}, message.MessageDelivery{MessageID: "msg_review"}, nil
}

func (s *reviewFeedbackAppStoreStub) ListReviewFeedbackForUser(_ context.Context, _ string, _ string, rc agent.ReviewContext, limit int) ([]message.AppMessage, error) {
	s.listLimit = limit
	return []message.AppMessage{{ID: "msg_review", ChannelID: rc.ThreadID}}, nil
}

func (s *reviewFeedbackAppStoreStub) SetReviewFeedbackItemStatusForUser(_ context.Context, _ model.User, _ string, rc agent.ReviewContext, messageID string, itemID string, status string) (message.AppMessage, error) {
	s.status = status
	s.itemID = itemID
	return message.AppMessage{ID: messageID, ChannelID: rc.ThreadID}, nil
}
