package webagent

import (
	"errors"
	"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/runtimecontrol"
	"github.com/tidbcloud/AgentTeam/internal/store/agent"
	"github.com/tidbcloud/AgentTeam/internal/store/identity"
	"github.com/tidbcloud/AgentTeam/internal/store/model"
	"github.com/tidbcloud/AgentTeam/internal/store/workspace"
)

func TestReviewFingerprintReturnsPayloadOnCapabilityHit(t *testing.T) {
	store := &fakeStore{review: &agent.ReviewContext{ID: "rcx_1", MachineID: "mach_1", SourceAgentID: "agt_1", RootPath: "/repo"}}
	want := agent.AgentReviewFingerprint{
		HeadCommit:      "abc123",
		RepositoryToken: "repo-token",
		BranchToken:     "branch-token",
		WorkingToken:    "working-token",
		WorkingStrength: agent.AgentReviewFingerprintWorkingStrengthHint,
	}
	runtime := &fakeRuntime{fingerprint: &want}
	handler := &ReviewHandler{reviewStore: store, runtime: runtime}

	response, err := handler.Fingerprint(reviewFingerprintRequest(t))
	require.NoError(t, err)
	require.Equal(t, http.StatusOK, response.Status)
	require.Equal(t, "rcx_1", store.lastReviewID)
}

func TestReviewFingerprintReturnsCapabilityMissAs501(t *testing.T) {
	store := &fakeStore{review: &agent.ReviewContext{ID: "rcx_1", MachineID: "mach_1", SourceAgentID: "agt_1", RootPath: "/repo"}}
	runtime := &fakeRuntime{fingerprint: nil} // nil → capability miss sentinel
	handler := &ReviewHandler{reviewStore: store, runtime: runtime}

	_, err := handler.Fingerprint(reviewFingerprintRequest(t))
	var httpErr api.HTTPError
	require.True(t, errors.As(err, &httpErr))
	require.Equal(t, http.StatusNotImplemented, httpErr.Status)
	require.Equal(t, runtimecontrol.ReviewFingerprintDaemonUpgradeMessage, httpErr.Error())
}

// TestReviewFingerprintMapsUnsupportedOutcomeToHTTP422 pins the webagent
// handler-level mapping for the unified non-fallback sentinel. When the
// daemon Hub mapper reconstructs ErrReviewTargetUnsupported from the
// desktop's stable wire outcome (ReviewTargetUnsupportedOutcomeCode) or
// legacy ErrorCode (ReviewTargetUnsupportedLegacyErrorCode), the webagent
// Fingerprint handler MUST target HTTP 422 — NOT 500 (generic) and NOT 501
// (capability miss).
//
// Why 422 and not 501: the desktop DID answer the RPC — it signaled a
// product limitation (control-char paths in the worktree) that no daemon
// upgrade can fix. 501 would mislead the frontend into prompting for an
// upgrade; 422 tells it the repo itself is the problem.
//
// The fake injects the typed sentinel directly because the outcome→sentinel
// mapping lives in the daemon Hub (covered by the elastic/session
// classification tests in internal/api/daemon). Wiring a full Hub here
// would duplicate that coverage and couple this test to daemon internals.
//
// Together with the daemon-side outcome→sentinel tests and the channel
// counterpart (TestThreadReviewFingerprintMapsUnsupportedOutcomeToHTTP422),
// this pins the full wire-outcome → HTTP-422 contract on both HTTP returns.
func TestReviewFingerprintMapsUnsupportedOutcomeToHTTP422(t *testing.T) {
	store := &fakeStore{review: &agent.ReviewContext{ID: "rcx_1", MachineID: "mach_1", SourceAgentID: "agt_1", RootPath: "/repo"}}
	runtime := &fakeRuntime{fingerprintErr: runtimecontrol.ErrReviewTargetUnsupported}
	handler := &ReviewHandler{reviewStore: store, runtime: runtime}

	_, err := handler.Fingerprint(reviewFingerprintRequest(t))
	var httpErr api.HTTPError
	require.True(t, errors.As(err, &httpErr))
	require.Equal(t, http.StatusUnprocessableEntity, httpErr.Status,
		"unsupported repo content MUST target as 422 so the frontend distinguishes it from transient 500s and the capability-miss 501; got %v", err)
	require.Equal(t, runtimecontrol.ReviewTargetUnsupportedMessage, httpErr.Error())
}

func reviewFingerprintRequest(t *testing.T) api.WorkspaceScopedRequest {
	t.Helper()
	gin.SetMode(gin.TestMode)
	ctx, _ := gin.CreateTestContext(httptest.NewRecorder())
	ctx.Params = gin.Params{{Key: "reviewId", Value: "rcx_1"}}
	ctx.Request = httptest.NewRequest(http.MethodGet, "/?baseRef=main&headRef=HEAD", nil)
	return api.WorkspaceScopedRequest{
		AuthenticatedRequest: api.AuthenticatedRequest{
			Gin:  ctx,
			User: identity.AuthenticatedUser{User: model.User{ID: "usr_1"}},
		},
		Workspace: workspace.Summary{WorkspaceID: "wsp_1"},
	}
}
