package exec

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

	"github.com/stretchr/testify/require"
)

// TestWorkflowRunLogEntryCarriesDetails verifies the desktop run log entry
// response preserves RunLogResponse.Details (the resolved safe failure
// contract) so the CLI logs/follow projection carries it to the Agent.
func TestWorkflowRunLogEntryCarriesDetails(t *testing.T) {
	now := time.Now().UTC()
	entry := workflowRunLogEntryResponse{
		Sequence: 1, Time: now, Level: "error", Kind: "effect_failed",
		Message: "Effect failed",
		Details: workflowOperationalJSON(`{
			"code":"message.delivery_failed","stage":"dispatch","path":"wnd_1",
			"problem":"The message could not be delivered","hint":"The message could not be delivered",
			"retryable":true
		}`),
	}
	encoded, err := json.Marshal(entry)
	require.NoError(t, err)
	require.Contains(t, string(encoded), `"details":`)
	require.Contains(t, string(encoded), "message.delivery_failed")
	require.Contains(t, string(encoded), `"retryable":true`)

	// Decode round-trip preserves the Details raw payload.
	var decoded workflowRunLogEntryResponse
	require.NoError(t, json.Unmarshal(encoded, &decoded))
	require.NotEmpty(t, decoded.Details)
	require.Contains(t, string(decoded.Details), "message.delivery_failed")
	require.Contains(t, string(decoded.Details), `"retryable":true`)
}

// TestWorkflowRunLogEntryProjectsLegacyErrorJSON verifies the desktop run
// log/follow projection carries the server-public failure contract: a legacy
// {code, message} ErrorJSON that reached the wire already has its arbitrary
// authoring message replaced by the bounded safe problem/hint, and the CLI log
// projection never exposes historical SQL/password/token/tenant/stack payloads.
func TestWorkflowRunLogEntryProjectsLegacyErrorJSON(t *testing.T) {
	now := time.Now().UTC()
	// This is the expectedized form the server now sends: the arbitrary
	// authoring message is gone, the bounded problem/hint derived from the code.
	legacyRaw := `{
		"code":"message.delivery_failed",
		"problem":"The message could not be delivered",
		"hint":"The message could not be delivered",
		"retryable":true,
		"stage":"dispatch",
		"path":"notify"
	}`
	entry := workflowRunLogEntryResponse{
		Sequence: 2, Time: now, Level: "error", Kind: "effect_failed",
		Message: "Effect failed", Details: workflowOperationalJSON(legacyRaw),
	}
	encoded, err := json.Marshal(entry)
	require.NoError(t, err)
	text := string(encoded)
	for _, secret := range []string{"SELECT", "password", "hunter2", "tenant", "token", "main.go", "arbitrary cause"} {
		require.NotContains(t, text, secret)
	}
	// The resolved bounded problem/hint survive to the Agent.
	require.Contains(t, text, "The message could not be delivered")
	require.Contains(t, text, `"retryable":true`)
	require.Contains(t, text, "message.delivery_failed")
}

// TestWorkflowRunLogEntryPreservesEventEnvelopeContext verifies the desktop
// run log/follow entry carries the schema-known per-event envelope projection
// produced by the server: the reviewed bounded activation/node/effect/kind/
// retry/message/receipt/recovery context survives next to the projected failure
// contract, and operational keys never reach the CLI output.
func TestWorkflowRunLogEntryPreservesEventEnvelopeContext(t *testing.T) {
	now := time.Now().UTC()
	envelope := `{
		"effectId":"wfx_1","activationId":"wra_1","nodeId":"wnd_1","kind":"message",
		"code":"message.delivery_failed","stage":"dispatch","path":"wnd_1",
		"problem":"The message could not be delivered","hint":"The message could not be delivered",
		"retryable":true,"retryAt":"2026-08-16T12:00:00Z",
		"messageId":"msg_1","receiptRecovery":true
	}`
	entry := workflowRunLogEntryResponse{
		Sequence: 3, Time: now, Level: "error", Kind: "effect_retry_scheduled",
		Message: "Effect retry scheduled", Details: workflowOperationalJSON(envelope),
	}
	encoded, err := json.Marshal(entry)
	require.NoError(t, err)
	text := string(encoded)
	// Envelope context survives to the CLI.
	require.Contains(t, text, `"effectId":"wfx_1"`)
	require.Contains(t, text, `"activationId":"wra_1"`)
	require.Contains(t, text, `"nodeId":"wnd_1"`)
	require.Contains(t, text, `"kind":"message"`)
	require.Contains(t, text, `"retryAt":"2026-08-16T12:00:00Z"`)
	require.Contains(t, text, `"messageId":"msg_1"`)
	require.Contains(t, text, `"receiptRecovery":true`)
	// Failure contract survives.
	require.Contains(t, text, `"code":"message.delivery_failed"`)
	require.Contains(t, text, `"stage":"dispatch"`)
	require.Contains(t, text, "The message could not be delivered")
	require.Contains(t, text, `"retryable":true`)

	// Decode round-trip preserves the raw envelope payload for downstream render.
	var decoded workflowRunLogEntryResponse
	require.NoError(t, json.Unmarshal(encoded, &decoded))
	require.NotEmpty(t, decoded.Details)
	require.Contains(t, string(decoded.Details), `"effectId":"wfx_1"`)
	require.Contains(t, string(decoded.Details), `"receiptRecovery":true`)
	require.Contains(t, string(decoded.Details), "message.delivery_failed")
}
