package run

import (
	"encoding/json"
	"testing"
	"time"

	"github.com/stretchr/testify/require"

	runstore "github.com/tidbcloud/AgentTeam/internal/store/workflow/run"
	"github.com/tidbcloud/AgentTeam/internal/workflow/invocation"
)

func TestResponsesSupportGraphTimelineWithoutRuntimeSecrets(t *testing.T) {
	now := time.Now().UTC()
	run := apiRunFixture(now)
	run.Identity = invocation.ExecutionIdentitySnapshot{
		OwnerUserID: "usr_00000000secret_owner", OwnerMemberID: "hsxxqww9wazk",
		PrincipalMemberID: "6lj9c1sxcskx", AuthorMemberID: "4mxe5im5mcmx",
	}
	run.LeaseOwner, run.LeaseToken = "worker_secret", "lease_secret"
	run.ErrorJSON = json.RawMessage(`{
		"message":"visible",
		"executionIdentity":{"ownerUserId":"usr_00000000nested_secret"},
		"leaseToken":"lease_nested_secret"
	}`)
	runResponse := runResponse(run, true)
	runJSON, err := json.Marshal(runResponse)
	require.NoError(t, err)
	require.NotContains(t, string(runJSON), "usr_00000000secret")
	require.NotContains(t, string(runJSON), "lease_secret")
	require.Contains(t, string(runJSON), "visible")
	require.JSONEq(t, `{"message":"visible"}`, string(runResponse.Error))
	require.Equal(t, run.PlanKey, runResponse.PlanKey)
	require.Equal(t, run.PlanDisplayName, runResponse.PlanDisplayName)
	require.Equal(t, run.BindingID, runResponse.BindingID)
	require.Equal(t, run.BindingKey, runResponse.BindingKey)
	require.Equal(t, run.BindingDisplayName, runResponse.BindingDisplayName)

	effect := runstore.Effect{
		ID: "wfx_baa86a9e8b16589a778ee2cfce0bef62", RunID: run.ID, ActivationID: "wra_6b86b273ff34fce19d6b804eff5a3f57", Kind: "send_message", Status: "awaiting",
		IdempotencyKey: "idempotency_secret", LeaseOwner: "worker_secret", LeaseToken: "lease_secret",
		RequestJSON: json.RawMessage(`{"message":{"ownerUserId":"usr_0000000request_secret","content":"visible request"}}`),
		ResultJSON: json.RawMessage(`{
			"messageId":"msg_visible",
			"idempotencyKey":"result_secret",
			"nested":{"authorMemberId":"p8xftv4b875n","kept":"yes"}
		}`),
		CreatedAt: now, UpdatedAt: now,
	}
	effectJSON, err := json.Marshal(effectResponseWithQueue(effect, runstore.EffectQueueContext{}))
	require.NoError(t, err)
	require.NotContains(t, string(effectJSON), "idempotency_secret")
	require.NotContains(t, string(effectJSON), "request_secret")
	require.NotContains(t, string(effectJSON), "result_secret")
	require.Contains(t, string(effectJSON), "visible request")
	require.Contains(t, string(effectJSON), "msg_visible")
	// The flat EffectResult is a closed typed schema: the unknown `nested` field
	// (outside exact user output) is dropped entirely, including its safe member.
	require.NotContains(t, string(effectJSON), "nested")
	require.NotContains(t, string(effectJSON), "kept")

	event := runstore.Event{
		ID: "wre_2", RunID: run.ID, Seq: 2, Type: "run_canceled", CreatedAt: now,
		PayloadJSON: json.RawMessage(`{
			"reason":"visible reason",
			"actor":{"type":"human","id":"usr_000000000actor","memberId":"0000000actor"},
			"ownerMemberId":"3qdqwuetm4zn",
			"nested":{"idempotency_key":"event_secret","nodeId":"node_visible"}
		}`),
	}
	eventResponse := eventResponse(event)
	eventJSON, err := json.Marshal(eventResponse)
	require.NoError(t, err)
	require.NotContains(t, string(eventJSON), "3qdqwuetm4zn")
	require.NotContains(t, string(eventJSON), "event_secret")
	require.Contains(t, string(eventJSON), "usr_000000000actor")
	require.Contains(t, string(eventJSON), "0000000actor")
	require.Contains(t, string(eventJSON), "visible reason")
	// run_canceled is typed to its real bounded {reason, actor} producer contract:
	// arbitrary nested context (even a safe nodeId) is not part of the contract and
	// must be dropped, so no invented/unknown context can survive.
	require.NotContains(t, string(eventJSON), "node_visible")
	require.Equal(t, int64(2), eventResponse.Sequence)
}

func TestRunListSummaryOmitsHeavyPayloads(t *testing.T) {
	run := apiRunFixture(time.Now().UTC())
	run.OutputJSON = json.RawMessage(`{"large":"output"}`)
	response := runResponse(run, false)
	require.Nil(t, response.Input)
	require.Nil(t, response.State)
	require.Nil(t, response.Output)
	require.Nil(t, response.Error)
	require.Equal(t, run.Status, response.Status)
}
