package run

import (
	"encoding/json"
	"github.com/stretchr/testify/require"
	store "github.com/tidbcloud/AgentTeam/internal/store/workflow/run"
	"testing"
	"time"
)

func TestRunShowResponseProjectsWaitingAgentWithoutLeakingRuntimeFields(t *testing.T) {
	now := time.Now().UTC()
	run := store.Run{ID: "wri_1", InvocationID: "wiv_1", PlanID: "pln_1", PlanVersionID: "plv_1", Status: store.RunStatusWaiting, InputJSON: json.RawMessage(`{"x":1}`), StateJSON: json.RawMessage(`{}`), QueuedAt: now, CreatedAt: now, UpdatedAt: now}
	step := store.Activation{ID: "wra_6b86b273ff34fce19d6b804eff5a3f57", RunID: run.ID, NodeID: "ask", Opcode: "send_message", Status: store.ActivationStatusWaiting, AttemptCount: 1, UpdatedAt: now}
	effect := store.Effect{ID: "wfx_baa86a9e8b16589a778ee2cfce0bef62", RunID: run.ID, ActivationID: step.ID, Kind: store.EffectKindMessage, Status: store.EffectStatusAwaiting, RequestJSON: json.RawMessage(`{"leaseToken":"secret","text":"work"}`), UpdatedAt: now}
	got := RunShowResponse(run, []store.Activation{step}, []store.Effect{effect})
	require.Equal(t, "ask", got.CurrentStep.StepID)
	require.Equal(t, "agent", got.WaitingOn.Kind)
	raw, err := json.Marshal(got)
	require.NoError(t, err)
	require.NotContains(t, string(raw), "secret")
}

func TestRunShowResponseProjectsResponderCapacityWait(t *testing.T) {
	now := time.Now().UTC()
	run := store.Run{ID: "wri_1", InvocationID: "wiv_1", PlanID: "pln_1", PlanVersionID: "plv_1", Status: store.RunStatusWaiting, InputJSON: json.RawMessage(`{"x":1}`), StateJSON: json.RawMessage(`{}`), QueuedAt: now, CreatedAt: now, UpdatedAt: now}
	step := store.Activation{ID: "wra_6b86b273ff34fce19d6b804eff5a3f57", RunID: run.ID, NodeID: "ask", Opcode: "send_message", Status: store.ActivationStatusWaiting, AttemptCount: 1, UpdatedAt: now}
	effect := store.Effect{ID: "wfx_baa86a9e8b16589a778ee2cfce0bef62", RunID: run.ID, ActivationID: step.ID, Kind: store.EffectKindMessage, Status: store.EffectStatusWaitingForResponder, RequestJSON: json.RawMessage(`{}`), UpdatedAt: now}

	got := RunShowResponse(run, []store.Activation{step}, []store.Effect{effect})
	require.Equal(t, "agent", got.WaitingOn.Kind)
	require.Equal(t, store.WaitingReasonResponderCapacity, got.WaitingOn.Reason)
	receipt := EffectResponse(effect)
	require.Equal(t, store.WaitingReasonResponderCapacity, receipt.WaitingReason)
	raw, err := json.Marshal(struct {
		Show    any `json:"show"`
		Receipt any `json:"receipt"`
	}{Show: got, Receipt: receipt})
	require.NoError(t, err)
	require.NotContains(t, string(raw), "agt_secret", "capacity waits must not expose candidate identity")
}

func TestRunLogResponseUsesResponderCapacityMessageAndSafeContext(t *testing.T) {
	got := RunLogResponse(store.Event{Seq: 3, Type: store.EventTypeEffectWaitingForResponderCapacity, PayloadJSON: json.RawMessage(`{"effectId":"wfx_baa86a9e8b16589a778ee2cfce0bef62","activationId":"a00000000000000000001","nodeId":"wnd_1","kind":"send_message","waitingReason":"responder_capacity","candidateAgentId":"agt_secret","leaseToken":"secret"}`), CreatedAt: time.Now().UTC()})
	require.Equal(t, "Waiting for responder capacity", got.Message)
	require.Contains(t, string(got.Details), `"waitingReason":"responder_capacity"`)
	require.NotContains(t, string(got.Details), "agt_secret")
	require.NotContains(t, string(got.Details), "leaseToken")
}

func TestRunLogResponseUsesBusinessMessageAndRedactsOperationalIdentity(t *testing.T) {
	got := RunLogResponse(store.Event{Seq: 3, Type: "effect_failed", PayloadJSON: json.RawMessage(`{"leaseOwner":"secret","failureCode":"delivery"}`), CreatedAt: time.Now().UTC()})
	require.Equal(t, "error", got.Level)
	require.Equal(t, "Effect failed", got.Message)
	require.NotContains(t, string(got.Details), "secret")
}

// TestRunShowErrorJSONPreservesEmptyDiagnosticsTruncation verifies the Run
// show/log transport projection preserves prior truncation metadata (omittedCount/
// truncatedFields/serializedBytes) when the persisted error_json carries an empty
// diagnostics list, without fabricating diagnostics.
func TestRunShowErrorJSONPreservesEmptyDiagnosticsTruncation(t *testing.T) {
	errorJSON := json.RawMessage(`{
		"code": "run.plan_version_invalid",
		"stage": "material",
		"problem": "The exact Workflow PlanVersion is invalid",
		"diagnostics": [],
		"truncation": {
			"diagnosticsTruncated": true,
			"omittedCount": 5,
			"truncatedFields": ["message", "path"],
			"serializedBytes": 1200
		}
	}`)
	got := safeOperationalJSON(errorJSON)
	require.NotNil(t, got)
	require.Contains(t, string(got), `"omittedCount":5`)
	require.Contains(t, string(got), `"truncatedFields":["message","path"]`)
	require.Contains(t, string(got), `"serializedBytes":1200`)
	require.Contains(t, string(got), `"diagnostics":[]`, "the empty diagnostics array is the honest projection of an all-dropped list")
}

// TestRunLogDetailsPreservesEmptyDiagnosticsTruncation verifies the Run log
// event details projection carries the typed truncation report through
// safeOperationalJSON when the event payload has an empty diagnostics list.
func TestRunLogDetailsPreservesEmptyDiagnosticsTruncation(t *testing.T) {
	event := store.Event{Seq: 7, Type: "effect_failed", PayloadJSON: json.RawMessage(`{
		"effectId": "wfx_baa86a9e8b16589a778ee2cfce0bef62",
		"code": "message.target_invalid",
		"diagnostics": [],
		"truncation": {
			"diagnosticsTruncated": true,
			"omittedCount": 2,
			"truncatedFields": ["field"],
			"serializedBytes": 300
		}
	}`), CreatedAt: time.Now().UTC()}
	got := RunLogResponse(event)
	require.Equal(t, "error", got.Level)
	require.NotContains(t, string(got.Details), "leaseOwner")
	require.Contains(t, string(got.Details), `"omittedCount":2`)
	require.Contains(t, string(got.Details), `"truncatedFields":["field"]`)
}
