package run

import (
	"bytes"
	"context"
	"encoding/json"
	"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/identity"
	runstore "github.com/tidbcloud/AgentTeam/internal/store/workflow/run"
	"github.com/tidbcloud/AgentTeam/internal/store/workspace"
	"github.com/tidbcloud/AgentTeam/internal/workflow/calendar"
)

func TestRoutesExposeOnlyRunReadAndCancelLifecycle(t *testing.T) {
	gin.SetMode(gin.TestMode)
	router := gin.New()
	RegisterRoutes(router, Dependencies{Service: &fakeService{}, Calendar: &fakeCalendarService{}})
	got := make([]string, 0)
	for _, route := range router.Routes() {
		got = append(got, route.Method+" "+route.Path)
	}
	require.ElementsMatch(t, []string{
		"GET /api/v1/workspaces/:workspaceId/workflow/calendar",
		"GET /api/v1/workspaces/:workspaceId/workflow/runs",
		"GET /api/v1/workspaces/:workspaceId/workflow/runs/:runId",
		"GET /api/v1/workspaces/:workspaceId/workflow/runs/:runId/activations",
		"GET /api/v1/workspaces/:workspaceId/workflow/runs/:runId/effects",
		"GET /api/v1/workspaces/:workspaceId/workflow/runs/:runId/events",
		"GET /api/v1/workspaces/:workspaceId/workflow/runs/:runId/artifacts/read",
		"POST /api/v1/workspaces/:workspaceId/workflow/runs/:runId/cancel",
	}, got)
	for _, route := range got {
		require.NotContains(t, route, "DELETE")
		require.NotContains(t, route, "PATCH")
		require.NotContains(t, route, "archive")
		require.NotContains(t, route, "restore")
		require.NotContains(t, route, "retry")
	}
}

type fakeCalendarService struct {
	result calendar.Result
	input  *calendar.ListInput
	err    error
}

func (fake *fakeCalendarService) List(_ context.Context, input calendar.ListInput) (calendar.Result, error) {
	fake.input = &input
	return fake.result, fake.err
}

type routeAuth struct{}

func (routeAuth) AuthenticateSession(context.Context, string) (identity.AuthenticatedUser, error) {
	return identity.AuthenticatedUser{User: identity.User{ID: "usr_000000000route"}}, nil
}

type routeWorkspace struct{}

func (routeWorkspace) GetWorkspaceForUser(_ context.Context, userID, workspaceID string) (workspace.Summary, error) {
	return workspace.Summary{
		WorkspaceID: workspaceID, MemberID: "0000000route", MemberUserID: userID, WorkspaceRole: "owner",
	}, nil
}

func TestRegisteredCancelRouteAuthenticatesAndInjectsHumanProvenance(t *testing.T) {
	gin.SetMode(gin.TestMode)
	now := time.Now().UTC()
	run := apiRunFixture(now)
	run.Status = runstore.RunStatusCanceled
	fake := &fakeService{cancelResult: CancelResult{Run: run, Replayed: true}}
	router := gin.New()
	RegisterRoutes(router, Dependencies{AuthStore: routeAuth{}, WorkspaceStore: routeWorkspace{}, Service: fake})

	request := httptest.NewRequest(http.MethodPost,
		"/api/v1/workspaces/wsp_1/workflow/runs/wri_1/cancel",
		bytes.NewBufferString(`{"reason":"stop from web"}`))
	request.Header.Set("Authorization", "Bearer route-token")
	request.Header.Set("Content-Type", "application/json")
	recorder := httptest.NewRecorder()
	router.ServeHTTP(recorder, request)

	require.Equal(t, http.StatusOK, recorder.Code, recorder.Body.String())
	require.Equal(t, &CancelInput{
		WorkspaceID: "wsp_1", RunID: "wri_1", Reason: "stop from web",
		Actor: Actor{Type: "human", ID: "usr_000000000route", MemberID: "0000000route"},
	}, fake.cancelInput)
	var response dto.WorkflowRunCancelResponse
	require.NoError(t, json.Unmarshal(recorder.Body.Bytes(), &response))
	require.True(t, response.Replayed)
	require.Equal(t, run.ID, response.Run.ID)
}
