package task

import (
	"context"
	"encoding/json"
	"errors"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"

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

	"github.com/tidbcloud/AgentTeam/internal/api"
	"github.com/tidbcloud/AgentTeam/internal/store/agent"
	"github.com/tidbcloud/AgentTeam/internal/store/storeerr"
)

type taskReviewHandlerStore struct {
	contexts []agent.ReviewContext
	err      error
	userID   string
	wsID     string
	taskID   string
}

func (s *taskReviewHandlerStore) ListReviewContextsForTaskID(_ context.Context, userID string, workspaceID string, taskID string) ([]agent.ReviewContext, error) {
	s.userID = userID
	s.wsID = workspaceID
	s.taskID = taskID
	if s.err != nil {
		return nil, s.err
	}
	return s.contexts, nil
}

func taskReviewHTTPResponse(t *testing.T, store TaskReviewContextStore, path string) *httptest.ResponseRecorder {
	t.Helper()
	router := testTaskReviewRouter(store)
	request := httptest.NewRequest(http.MethodGet, path, nil)
	request.Header.Set("Authorization", "Bearer human-token")
	response := httptest.NewRecorder()
	router.ServeHTTP(response, request)
	return response
}

func testTaskReviewRouter(store TaskReviewContextStore) http.Handler {
	router := newTestTaskReviewRouter()
	// RegisterRoutes is the production route assembly. The other Task
	// dependencies are intentionally nil because the request below selects only
	// the real ReviewContextHandler.List path.
	RegisterRoutes(router, Dependencies{
		AuthStore:      taskRouteSessionAuth{token: "human-token", user: taskRouteHumanUser()},
		WorkspaceStore: taskRouteWorkspaceResolver{userID: "usr_human", workspaceID: "wsp_test"},
		ReviewStore:    store,
	})
	return router
}

func newTestTaskReviewRouter() *gin.Engine {
	return gin.New()
}

func TestTaskReviewContextHandlerListInvokesAuthorizedHumanPath(t *testing.T) {
	store := &taskReviewHandlerStore{contexts: []agent.ReviewContext{{
		ID:            "rcx_task_visible",
		WorkspaceID:   "wsp_test",
		TaskScopeID:   "tsk_visible",
		SourceAgentID: "agt_source",
		MachineID:     "mac_source",
		RootPath:      "/repo/visible",
		DisplayName:   "Visible review",
		CreatedAt:     time.Unix(1, 0).UTC(),
	}}}
	response := taskReviewHTTPResponse(t, store, "/api/v1/workspaces/wsp_test/tasks/task_visible/review-contexts")

	require.Equal(t, http.StatusOK, response.Code)
	require.Equal(t, "usr_human", store.userID)
	require.Equal(t, "wsp_test", store.wsID)
	require.Equal(t, "task_visible", store.taskID)
	var payload struct {
		Contexts []map[string]any `json:"contexts"`
	}
	require.NoError(t, json.Unmarshal(response.Body.Bytes(), &payload))
	require.Len(t, payload.Contexts, 1)
	require.Equal(t, "rcx_task_visible", payload.Contexts[0]["id"])
}

func TestTaskReviewContextHandlerListDoesNotEnumerateUnauthorizedOrUnknownTasks(t *testing.T) {
	for _, testCase := range []struct {
		name   string
		taskID string
		err    error
	}{{
		name:   "unknown",
		taskID: "task_unknown",
		err:    storeerr.ErrNotFound,
	}, {
		name:   "inactive",
		taskID: "task_inactive",
		err:    storeerr.ErrNotFound,
	}, {
		name:   "cross workspace",
		taskID: "task_foreign",
		err:    storeerr.ErrNotFound,
	}, {
		name:   "unauthorized",
		taskID: "task_private",
		err:    storeerr.ErrNotFound,
	}} {
		t.Run(testCase.name, func(t *testing.T) {
			store := &taskReviewHandlerStore{err: testCase.err}
			response := taskReviewHTTPResponse(t, store, "/api/v1/workspaces/wsp_test/tasks/"+testCase.taskID+"/review-contexts")
			require.Equal(t, http.StatusNotFound, response.Code)
			body := response.Body.String()
			require.NotContains(t, body, testCase.taskID)
			require.NotContains(t, body, "rcx_foreign")
			require.NotContains(t, body, "/repo/private")
			require.NotContains(t, body, "mac_private")
		})
	}
}

func TestTaskReviewContextHandlerListRejectsMissingTaskIDBeforeStore(t *testing.T) {
	store := &taskReviewHandlerStore{}
	response := taskReviewHTTPResponse(t, store, "/api/v1/workspaces/wsp_test/tasks/%20/review-contexts")
	require.Equal(t, http.StatusBadRequest, response.Code)
	require.Empty(t, store.taskID)
	require.NotContains(t, response.Body.String(), "/repo")
}

func TestTaskReviewContextHandlerListPropagatesNonEnumerationError(t *testing.T) {
	store := &taskReviewHandlerStore{err: errors.New("backend unavailable")}
	response := taskReviewHTTPResponse(t, store, "/api/v1/workspaces/wsp_test/tasks/task_visible/review-contexts")
	require.Equal(t, http.StatusInternalServerError, response.Code)
	require.NotContains(t, response.Body.String(), "task_visible")
}

// Compile-time guards make the test's route helpers fail loudly if a future
// handler contract changes instead of silently bypassing the production path.
var _ TaskReviewContextStore = (*taskReviewHandlerStore)(nil)
var _ api.SessionAuthenticator = taskRouteSessionAuth{}
