package approvalapp

import (
	"fmt"
	"testing"

	"github.com/stretchr/testify/require"

	"github.com/tidbcloud/AgentTeam/internal/service/role/execution"
)

func TestRoleManagementTerminalObserverLoadsProjectedStateMatrix(t *testing.T) {
	for _, testCase := range []struct {
		name   string
		status execution.RequestStatus
	}{
		{"active exact processing continues", ""},
		{"resolved exact success returns stable receipt", execution.RequestSucceeded},
		{"resolved exact failure returns stable receipt", execution.RequestFailed},
	} {
		t.Run(testCase.name, func(t *testing.T) {
			fixture := newRoleManagementTerminalFixture(t, string(testCase.status)+"_load")
			want := execution.RequestResult{}
			wantMutations := 0
			if testCase.status != "" {
				want = fixture.success
				if testCase.status == execution.RequestFailed {
					want = roleManagementTerminalFailureForTest(fixture)
				}
				require.NoError(t, fixture.observer.ProjectTerminal(fixture.ctx, fixture.instance, want))
				wantMutations = 1
			}
			payload, err := decodeRoleManagementPayload(fixture.instance)
			require.NoError(t, err)
			result, resolved, err := fixture.observer.LoadProjectedTerminal(
				fixture.ctx, fixture.instance, payload,
			)
			require.NoError(t, err)
			require.Equal(t, testCase.status != "", resolved)
			require.Equal(t, want, result)
			require.Equal(t, wantMutations, fixture.notifications.mutationCalls)
		})
	}
}

func TestRoleManagementTerminalObserverRejectsInvalidProjectedState(t *testing.T) {
	for index, testCase := range []struct {
		name    string
		prepare func(*roleManagementTerminalFixture, *execution.ApprovalPayload)
	}{
		{"active processing content mismatch",
			func(fixture *roleManagementTerminalFixture, _ *execution.ApprovalPayload) {
				fixture.notifications.card.RenderPayloadJSON = replaceRoleCardValue(
					t, fixture.notifications.card.RenderPayloadJSON,
					"Release reviewer", "Redirected role",
				)
			}},
		{"malformed card",
			func(fixture *roleManagementTerminalFixture, _ *execution.ApprovalPayload) {
				fixture.notifications.card.RenderPayloadJSON = []byte("{")
			}},
		{
			name: "resolved status outside terminal closed set",
			prepare: func(fixture *roleManagementTerminalFixture, _ *execution.ApprovalPayload) {
				failure := roleManagementTerminalFailureForTest(fixture)
				require.NoError(t, fixture.observer.ProjectTerminal(fixture.ctx, fixture.instance, failure))
				fixture.notifications.card.RenderPayloadJSON = replaceRoleCardValue(
					t, fixture.notifications.card.RenderPayloadJSON,
					`"status":"failed"`, `"status":"rejected"`,
				)
			},
		},
		{
			name: "resolved unsafe failure",
			prepare: func(fixture *roleManagementTerminalFixture, _ *execution.ApprovalPayload) {
				failure := roleManagementTerminalFailureForTest(fixture)
				require.NoError(t, fixture.observer.ProjectTerminal(fixture.ctx, fixture.instance, failure))
				fixture.notifications.card.RenderPayloadJSON = replaceRoleCardValue(
					t, fixture.notifications.card.RenderPayloadJSON,
					"The role changed before this request was applied.", "raw upstream secret",
				)
			},
		},
		{"validated payload mismatch",
			func(_ *roleManagementTerminalFixture, payload *execution.ApprovalPayload) {
				payload.RequestID = "req_other"
			}},
	} {
		t.Run(testCase.name, func(t *testing.T) {
			fixture := newRoleManagementTerminalFixture(t, fmt.Sprintf("load_reject_%d", index))
			payload, err := decodeRoleManagementPayload(fixture.instance)
			require.NoError(t, err)
			testCase.prepare(fixture, &payload)
			mutations := fixture.notifications.mutationCalls
			_, _, err = fixture.observer.LoadProjectedTerminal(
				fixture.ctx, fixture.instance, payload,
			)
			require.Error(t, err)
			require.Equal(t, mutations, fixture.notifications.mutationCalls)
		})
	}
}

func roleManagementTerminalFailureForTest(
	fixture *roleManagementTerminalFixture,
) execution.RequestResult {
	failure := fixture.success
	failure.Status, failure.RoleID, failure.RoleVersionID = execution.RequestFailed, "", ""
	failure.FailureCode = string(execution.ApplyVersionConflict)
	failure.FailureMessage = "The role changed before this request was applied."
	return failure
}
