package exec

import (
	"net/http"
	"testing"
)

func TestAPIErrorCodeMapsConflictStatuses(t *testing.T) {
	for _, status := range []int{http.StatusConflict, http.StatusPreconditionFailed} {
		if got := apiErrorCode(status); got != CodeConflict {
			t.Errorf("apiErrorCode(%d) = %q, want %q", status, got, CodeConflict)
		}
	}
}

// TestAPIErrorCodeTypes413_428_429 verifies the corrected typed categories for
// the HTTP statuses the server emits for Workflow requests: 413 payload-too-large
// is a validation failure, 428 precondition-required is a conflict, and 429
// rate-limited is the rate-limited category (not internal).
func TestAPIErrorCodeTypes413_428_429(t *testing.T) {
	cases := []struct {
		status int
		want   ErrorCode
	}{
		{http.StatusRequestEntityTooLarge, CodeValidation}, // 413
		{http.StatusPreconditionRequired, CodeConflict},    // 428
		{http.StatusTooManyRequests, CodeRateLimited},      // 429
	}
	for _, tc := range cases {
		if got := apiErrorCode(tc.status); got != tc.want {
			t.Errorf("apiErrorCode(%d) = %q, want %q", tc.status, got, tc.want)
		}
	}
}

// TestAllowlistedWorkflowServerCodeExplicitProductionSet proves the allowlist is
// now an EXPLICIT reviewed set covering every production Workflow code the
// server emits across the plan/binding/invocation/run/step domains, the
// team_workflow_* and workflow_agent_* endpoints, and the generic workflow codes.
func TestAllowlistedWorkflowServerCodeExplicitProductionSet(t *testing.T) {
	productionCodes := []string{
		// plan domain.
		"plan_archived", "plan_compile_failed", "plan_conflict",
		"plan_draft_revision_conflict", "plan_id_required", "plan_idempotency_conflict",
		"plan_invalid_action", "plan_invalid_json", "plan_invalid_request",
		"plan_no_changes", "plan_not_found", "plan_request_failed",
		"plan_request_too_large", "plan_revision_conflict",
		// plan agent-edit validation.
		"invalid_beforeSeq", "invalid_limit", "invalid_plan_version",
		// binding domain.
		"binding_action_not_found", "binding_archived", "binding_conflict",
		"binding_forbidden", "binding_id_required", "binding_idempotency_conflict",
		"binding_idempotency_key_required", "binding_internal_error",
		"binding_invalid_idempotency_key", "binding_invalid_if_match",
		"binding_invalid_json", "binding_invalid_limit", "binding_invalid_request",
		"binding_invalid_revision", "binding_invalid_state", "binding_not_found",
		"binding_plan_archived", "binding_plan_not_found", "binding_plan_version_invalid",
		"binding_request_failed", "binding_request_too_large", "binding_revision_conflict",
		"binding_validation_failed",
		// invocation domain.
		"invocation_conflict", "invocation_forbidden", "invocation_idempotency_conflict",
		"invocation_idempotency_key_required", "invocation_input_invalid",
		"invocation_invalid_idempotency_key", "invocation_invalid_json",
		"invocation_invalid_request", "invocation_not_found",
		"invocation_plan_version_unavailable", "invocation_principal_unavailable",
		"invocation_request_failed", "invocation_request_too_large",
		// run domain.
		"run_command_not_found", "run_conflict", "run_forbidden",
		"run_invalid_action", "run_invalid_json", "run_invalid_request", "run_not_found",
		"run_request_failed", "run_request_too_large", "run_start_failed",
		"run_terminal_conflict", "run_unavailable",
		// step / callback domain.
		"step_already_resolved", "step_forbidden", "step_not_found",
		"step_not_ready", "step_not_waiting", "step_output_budget_exceeded",
		"step_output_contract_invalid", "step_output_invalid",
		"step_work_item_reference_invalid", "step_work_items_invalid",
		"step_work_scope_invalid", "step_work_scope_unauthorized",
		// team_workflow endpoint.
		"authoring_failed", "team_workflow_invalid_if_match", "team_workflow_invalid_limit",
		"team_workflow_invalid_request",
		"team_workflow_invalid_state", "team_workflow_request_failed",
		"team_workflow_unavailable",
		// workflow_agent endpoint.
		"workflow_agent_edit_revision_mismatch", "workflow_agent_edit_unavailable",
		"workflow_agent_wake_active",
		// generic workflow codes.
		"idempotency_conflict", "internal_error", "invalid_if_match",
		"invalid_request_body", "precondition_required", "workflow_request_failed",
		"workflow_run_unavailable",
	}
	for _, code := range productionCodes {
		if !allowlistedWorkflowServerCode(code) {
			t.Errorf("production Workflow code %q must be in the explicit trusted set", code)
		}
	}
}

// TestAllowlistedWorkflowServerCodeFailsClosedUnknown proves unknown codes fail
// closed even when they share a trusted workflow prefix (registry drift): the
// former prefix allowlist is gone, so an un-reviewed future code never projects.
func TestAllowlistedWorkflowServerCodeFailsClosedUnknown(t *testing.T) {
	for _, code := range []string{
		"",
		"totally_unknown_code",
		"plan_future_unreviewed",
		"binding_new_internal_thing",
		"invocation_leak_raw_sql",
		"run_secret_path",
		"step_stack_trace",
		"team_workflow_unknown_future",
		"workflow_agent_future_code",
	} {
		if allowlistedWorkflowServerCode(code) {
			t.Errorf("unknown code %q must fail closed (not be allowlisted)", code)
		}
	}
}

// TestAllowlistedWorkflowServerCodeIsExplicitNotPrefix proves the allowlist does
// not trust arbitrary prefixed codes (the exact behavior removed by #t2600).
func TestAllowlistedWorkflowServerCodeIsExplicitNotPrefix(t *testing.T) {
	for _, code := range []string{
		"plan_", "binding_anything", "invocation_made_up", "run_xyzzy", "step_bogus",
	} {
		if allowlistedWorkflowServerCode(code) {
			t.Errorf("prefix-trust is gone: %q must not be allowlisted without explicit review", code)
		}
	}
}

// TestAllowlistedWorkflowServerCodeFailsClosedOnWhitespace proves the allowlist
// matches server codes byte-for-byte: a code with surrounding whitespace is NOT
// trimmed into an allowlisted identity, so an untrusted server code can never
// unlock public problem/hint/diagnostics.
func TestAllowlistedWorkflowServerCodeFailsClosedOnWhitespace(t *testing.T) {
	for _, code := range []string{"  plan_request_failed  ", "\tplan_request_failed\n", " run_terminal_conflict "} {
		if allowlistedWorkflowServerCode(code) {
			t.Errorf("allowlist must fail closed on surrounding whitespace: %q must NOT be allowlisted", code)
		}
	}
}
