package agent

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

	"github.com/stretchr/testify/require"

	"github.com/tidbcloud/AgentTeam/internal/store/storeerr"
	"github.com/tidbcloud/AgentTeam/internal/workflow/orchestrator"
	"github.com/tidbcloud/AgentTeam/internal/workflow/runmanage"
)

// errorStepRuntime is a CallbackRuntime stub that returns a configured error
// from ExecuteCallback so the real step handler and classifier path is
// exercised for protected Step selectors.
type errorStepRuntime struct {
	err error
}

func (runtime *errorStepRuntime) ExecuteCallback(_ context.Context, _ orchestrator.CallbackCommand) (orchestrator.CallbackResult, error) {
	return orchestrator.CallbackResult{}, runtime.err
}

func (runtime *errorStepRuntime) ListCallbacks(_ context.Context, _ orchestrator.CallbackListCommand) (orchestrator.CallbackListResult, error) {
	return orchestrator.CallbackListResult{}, nil
}

// TestStepCrossTenantSelectorHTTPMatrix proves the real Agent Workflow Step HTTP
// handler preserves the distinction between a missing and forbidden Step.
func TestStepCrossTenantSelectorHTTPMatrix(t *testing.T) {
	missing := stepSelectorHTTPResponse(t, orchestrator.ErrCallbackNotFound)
	forbidden := stepSelectorHTTPResponse(t, orchestrator.ErrCallbackUnauthorized)

	require.Equal(t, http.StatusNotFound, missing.Code)
	require.Equal(t, http.StatusForbidden, forbidden.Code)
	require.Contains(t, missing.Body.String(), `"code":"step_not_found"`)
	require.Contains(t, forbidden.Body.String(), `"code":"step_forbidden"`)
	require.NotContains(t, missing.Body.String(), "wst_secret")
	require.NotContains(t, forbidden.Body.String(), "wst_secret")
}

func stepSelectorHTTPResponse(t *testing.T, selectorErr error) *httptest.ResponseRecorder {
	t.Helper()
	router := workflowPublicTestRouter(t, publicRouteRunService{}, &errorStepRuntime{err: selectorErr})
	response := postWorkflowStepCommand(t, router, map[string]any{
		"action": "get", "workflowRunId": "wri_secret", "workflowStepId": "wst_secret",
	})
	return response
}

// TestStepCrossTenantSelectorServiceMatrix proves the real Step classifier
// preserves missing and forbidden at the service boundary.
func TestStepCrossTenantSelectorServiceMatrix(t *testing.T) {
	handler := NewWorkflowStepHandler(&errorStepRuntime{}, nil)
	missing, ok := handler.classifyError(orchestrator.ErrCallbackNotFound)
	require.True(t, ok)
	forbidden, ok := handler.classifyError(orchestrator.ErrCallbackUnauthorized)
	require.True(t, ok)

	require.Equal(t, http.StatusNotFound, missing.Status)
	require.Equal(t, http.StatusForbidden, forbidden.Status)
	require.Equal(t, "step_not_found", missing.Code)
	require.Equal(t, "step_forbidden", forbidden.Code)
	require.NotEqual(t, missing.Message, forbidden.Message)
	require.Equal(t, missing.Stage, forbidden.Stage)
	require.Equal(t, "run", missing.Stage)
	require.Nil(t, missing.Details)
	require.Nil(t, forbidden.Details)
	require.NotContains(t, missing.Message, "wst_secret")
}

// TestAgentRunCrossTenantSelectorServiceMatrix proves the Agent Run transport
// preserves missing and forbidden at the service boundary.
func TestAgentRunCrossTenantSelectorServiceMatrix(t *testing.T) {
	missing, ok := classifyAgentWorkflowRunError(runmanage.RequestError{
		Kind: storeerr.ErrNotFound, Code: runmanage.ErrorCodeNotFound, Message: "Workflow Run wri_secret is missing",
		Details: map[string]any{"runId": "wri_secret"},
	})
	require.True(t, ok)
	forbidden, ok := classifyAgentWorkflowRunError(runmanage.RequestError{
		Kind: storeerr.ErrForbidden, Code: runmanage.ErrorCodeForbidden, Message: "Workflow Run wri_secret is forbidden",
		Details: map[string]any{"runId": "wri_secret"},
	})
	require.True(t, ok)

	require.Equal(t, http.StatusNotFound, missing.Status)
	require.Equal(t, http.StatusForbidden, forbidden.Status)
	require.Equal(t, "run_not_found", missing.Code)
	require.Equal(t, "run_forbidden", forbidden.Code)
	require.NotEqual(t, missing.Message, forbidden.Message)
	require.Equal(t, missing.Stage, forbidden.Stage)
	require.Equal(t, "run", missing.Stage)
	require.NotNil(t, missing.Details)
	require.NotNil(t, forbidden.Details)
	require.Contains(t, missing.Message, "wri_secret")
	require.Contains(t, forbidden.Message, "wri_secret")
}
