package agent

import (
	"net/http"
	"testing"

	"github.com/stretchr/testify/require"

	"github.com/tidbcloud/AgentTeam/internal/api/workflow/binding"
)

// TestAgentWorkflowActionsAlignWithHumanContract proves the Agent
// Plan/Trigger/Run transports project the same resolved public Workflow error
// contract as their Human HTTP counterparts for strict-JSON, idempotency,
// action, and request-validation failures. Each path carries the owning stage
// and a stable code; none reverts to the legacy HTTPError envelope.
func TestAgentWorkflowActionsAlignWithHumanContract(t *testing.T) {
	tests := []struct {
		name       string
		classify   func(error) (int, string, string, bool)
		err        error
		wantStatus int
		wantCode   string
		wantStage  string
	}{
		{
			name: "plan strict JSON", classify: classifyAgentWorkflowPlanErrorProjection,
			err:        publicError(http.StatusBadRequest, "plan_invalid_json", "invalid request body"),
			wantStatus: http.StatusBadRequest, wantCode: "plan_invalid_json", wantStage: "plan",
		},
		{
			name: "plan action required", classify: classifyAgentWorkflowPlanErrorProjection,
			err:        publicError(http.StatusBadRequest, "plan_invalid_action", "action is required"),
			wantStatus: http.StatusBadRequest, wantCode: "plan_invalid_action", wantStage: "plan",
		},
		{
			name: "plan no changes conflict", classify: classifyAgentWorkflowPlanErrorProjection,
			err:        publicError(http.StatusConflict, "plan_no_changes", "Plan draft update did not change source or layout"),
			wantStatus: http.StatusConflict, wantCode: "plan_no_changes", wantStage: "plan",
		},
		{
			name: "trigger strict JSON", classify: classifyWorkflowBindingErrorProjection,
			err:        publicError(http.StatusBadRequest, "binding_invalid_json", "invalid request body"),
			wantStatus: http.StatusBadRequest, wantCode: "binding_invalid_json", wantStage: "binding",
		},
		{
			name: "trigger context mismatch", classify: classifyWorkflowBindingErrorProjection,
			err:        publicError(http.StatusForbidden, "binding_agent_context_mismatch", "Workflow Trigger command context does not match the authenticated Agent session"),
			wantStatus: http.StatusForbidden, wantCode: "binding_agent_context_mismatch", wantStage: "binding",
		},
		{
			name: "run strict JSON", classify: classifyAgentWorkflowRunErrorProjection,
			err:        publicError(http.StatusBadRequest, "run_invalid_json", "invalid request body"),
			wantStatus: http.StatusBadRequest, wantCode: "run_invalid_json", wantStage: "run",
		},
		{
			name: "run command not found", classify: classifyAgentWorkflowRunErrorProjection,
			err:        publicError(http.StatusNotFound, "run_command_not_found", "unknown Workflow Run command action"),
			wantStatus: http.StatusNotFound, wantCode: "run_command_not_found", wantStage: "run",
		},
		{
			name: "run start unavailable", classify: classifyAgentWorkflowRunErrorProjection,
			err:        publicError(http.StatusServiceUnavailable, "run_unavailable", "Workflow Run start is not enabled"),
			wantStatus: http.StatusServiceUnavailable, wantCode: "run_unavailable", wantStage: "run",
		},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			status, code, stage, ok := test.classify(test.err)
			require.True(t, ok)
			require.Equal(t, test.wantStatus, status)
			require.Equal(t, test.wantCode, code)
			require.Equal(t, test.wantStage, stage)
		})
	}
}

func classifyAgentWorkflowPlanErrorProjection(err error) (int, string, string, bool) {
	response, ok := classifyAgentWorkflowPlanError(err)
	if !ok {
		return 0, "", "", false
	}
	return response.Status, response.Code, response.Stage, true
}

func classifyWorkflowBindingErrorProjection(err error) (int, string, string, bool) {
	response, ok := binding.ClassifyError(err)
	if !ok {
		return 0, "", "", false
	}
	return response.Status, response.Code, response.Stage, true
}

func classifyAgentWorkflowRunErrorProjection(err error) (int, string, string, bool) {
	response, ok := classifyAgentWorkflowRunError(err)
	if !ok {
		return 0, "", "", false
	}
	return response.Status, response.Code, response.Stage, true
}
