package agent

import (
	"testing"

	"github.com/stretchr/testify/require"

	"github.com/tidbcloud/AgentTeam/desktop/internal/agent/drivercontract"
	"github.com/tidbcloud/AgentTeam/desktop/internal/consts"
)

func newCodexLifecycleProcessForTest() *Process {
	driver := &CodexAppServerDriver{}
	return newTestProcessForRecovery("agent-codex-lifecycle", "thread-root", driver)
}

func TestCodexProcessUsesExplicitTurnBoundariesForRootLiveness(t *testing.T) {
	ap := newCodexLifecycleProcessForTest()
	var safety []bool
	ap.SetOnResumeSafety(func(_ uint64, safe bool, _ string) error { safety = append(safety, safe); return nil })
	ap.mu.Lock()
	ap.isIdle = true
	ap.mu.Unlock()

	for _, event := range []drivercontract.ParsedEvent{
		{Kind: consts.ParsedEventKindThinking, Text: "late reasoning"},
		{Kind: consts.ParsedEventKindText, Text: "late text"},
		{Kind: consts.ParsedEventKindToolCall, ToolName: "shell"},
		{Kind: consts.ParsedEventKindToolOutput, ToolName: "shell", Text: "late output"},
		{Kind: consts.ParsedEventKindError, Message: "late retrying error", Metadata: map[string]any{"will_retry": true}},
	} {
		ap.handleParsedEvent(event)
		require.True(t, ap.IsIdle(), "%s activity must not reopen an explicitly completed Codex turn", event.Kind)
	}

	ap.handleParsedEvent(drivercontract.ParsedEvent{
		Kind: consts.ParsedEventKindThinking,
		Metadata: map[string]any{
			codexAuthoritativeTurnStartMetadataKey: true,
		},
	})
	require.False(t, ap.IsIdle(), "authoritative Codex TurnStarted must own root liveness")
	require.NotEmpty(t, safety)
	require.False(t, safety[len(safety)-1], "runtime-native TurnStarted must mark the session unsafe")

	ap.handleParsedEvent(drivercontract.ParsedEvent{
		Kind:      consts.ParsedEventKindTurnEnd,
		SessionID: "thread-root",
		Metadata: map[string]any{
			runtimeCompletedTurnIDKey: "turn-current",
		},
	})
	require.True(t, ap.IsIdle())

	ap.handleParsedEvent(drivercontract.ParsedEvent{Kind: consts.ParsedEventKindToolOutput, Text: "late after end"})
	require.True(t, ap.IsIdle(), "defense-in-depth must keep future late emitters from reopening the root")
}

func TestCodexTerminalErrorTurnEndRestoresResumeSafetyWithoutHidingError(t *testing.T) {
	ap := newCodexLifecycleProcessForTest()
	ap.mu.Lock()
	ap.isIdle = false
	ap.mu.Unlock()

	var safety []bool
	var parsed []drivercontract.ParsedEvent
	var runtimeEvents []RuntimeEvent
	ap.SetOnResumeSafety(func(_ uint64, safe bool, _ string) error { safety = append(safety, safe); return nil })
	ap.SetOnEvent(func(ev drivercontract.ParsedEvent) { parsed = append(parsed, ev) })
	ap.SetOnRuntimeEvent(func(ev RuntimeEvent) { runtimeEvents = append(runtimeEvents, ev) })

	ap.handleParsedEvent(drivercontract.ParsedEvent{
		Kind:    consts.ParsedEventKindError,
		Message: "provider capacity exhausted",
		Metadata: map[string]any{
			"will_retry": false,
		},
	})
	require.NotEmpty(t, safety)
	require.False(t, safety[len(safety)-1])
	require.Len(t, runtimeEvents, 1, "terminal error remains user-visible before finalization")
	require.Contains(t, runtimeEvents[0].Detail, "provider capacity exhausted")

	ap.handleParsedEvent(drivercontract.ParsedEvent{
		Kind:      consts.ParsedEventKindTurnEnd,
		SessionID: "thread-root",
		Metadata: map[string]any{
			runtimeCompletedTurnIDKey:            "turn-failed",
			codexTerminalErrorTurnEndMetadataKey: true,
		},
	})

	require.True(t, ap.IsIdle())
	require.True(t, safety[len(safety)-1], "authoritative terminal boundary is resumable like Codex TUI")
	ap.mu.Lock()
	require.Empty(t, ap.runtimeErrorDetail, "turn-scoped error must not poison the session after TurnEnd")
	require.True(t, ap.runtimeErrorEventEmitted, "already emitted failure evidence must be retained")
	ap.mu.Unlock()
	require.Len(t, parsed, 2)
	require.Equal(t, consts.ParsedEventKindError, parsed[0].Kind)
	require.Equal(t, consts.ParsedEventKindTurnEnd, parsed[1].Kind)
}
