package execution

import (
	"bytes"
	"context"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"testing"
	"time"

	"github.com/stretchr/testify/require"

	"github.com/tidbcloud/AgentTeam/internal/store/approval"
	executionstore "github.com/tidbcloud/AgentTeam/internal/store/role/execution"
)

const executionSecretSentinel = "secret-sql-network-object-42"

type executionProbe struct {
	approval     approval.Instance
	loadErr      error
	recover      executionstore.Success
	recoverFound bool
	recoverErr   error
	authority    ExecutionAuthority
	authorityErr error
	applyResult  executionstore.Success
	applyErr     error
	projected    RequestResult
	projectedOK  bool
	projectedErr error
	now          func() time.Time
	settleErr    error

	calls             []string
	projectedLoads    int
	loadWorkspaceID   string
	loadApprovalID    string
	recoverCommand    RecoverAppliedCommand
	authorityCommand  ExecutionAuthorityCommand
	applyCommand      ApplyCommand
	nowCalls          int
	settleContexts    []context.Context
	settleContextLive []bool
	settleFences      []executionstore.ExecutionFence
	settleFailures    []executionstore.Failure
	settleSuccesses   []executionstore.Success
	settleTimes       []time.Time
}

func newExecutionProbe(
	t *testing.T,
	proposal RoleProposal,
) (*Service, *executionProbe, executionstore.ClaimedExecution, time.Time) {
	t.Helper()
	claimNow := time.Unix(100, 0).UTC()
	command := redSubmit()
	command.Proposal = proposal
	prepared, err := prepareSubmission(command)
	require.NoError(t, err)
	instance := approval.Instance{
		ID: "appr_1", WorkspaceID: prepared.command.Principal.WorkspaceID,
		SubjectType: SubjectType, SubjectRef: prepared.idempotencyKey, SubjectVersion: 1,
		Payload: prepared.deterministicJSON, PayloadDigest: prepared.payloadDigest,
		IdempotencyKey: prepared.idempotencyKey, ApplicantMemberID: prepared.command.Principal.MemberID,
		State: approval.StateApproved, Decision: approval.DecisionApproved,
		DecidedByMemberID: "mem_manager", Version: 2,
	}
	expiry := claimNow.Add(time.Minute)
	claim := executionstore.ClaimedExecution{
		Execution: executionstore.Execution{
			ApprovalID: instance.ID, WorkspaceID: instance.WorkspaceID,
			Status: executionstore.ExecutionRunning, LeaseToken: "lease_1", LeaseExpiresAt: &expiry,
		},
		Fence: executionstore.ExecutionFence{
			WorkspaceID: instance.WorkspaceID, ApprovalID: instance.ID, LeaseToken: "lease_1",
		},
	}
	owned := []string(nil)
	if value, ok := prepared.payload.Apply, prepared.payload.Apply != nil; ok {
		owned = []string{value.AgentID}
	}
	probe := &executionProbe{
		approval: instance,
		authority: ExecutionAuthority{
			ManagerUserID: "usr_manager",
			OwnedAgentIDs: owned,
		},
		applyResult: executionstore.Success{RoleID: "role_1", RoleVersionID: "rlv_1"},
		now:         func() time.Time { return claimNow.Add(time.Second) },
	}
	service := NewService(Dependencies{
		LoadApproval: func(_ context.Context, workspaceID, approvalID string) (approval.Instance, error) {
			probe.calls = append(probe.calls, "load")
			probe.loadWorkspaceID = workspaceID
			probe.loadApprovalID = approvalID
			return probe.approval, probe.loadErr
		},
		LoadProjectedTerminal: func(
			_ context.Context,
			instance approval.Instance,
			payload ApprovalPayload,
		) (RequestResult, bool, error) {
			probe.projectedLoads++
			require.Equal(t, probe.approval, instance)
			require.Equal(t, strictDecodePayloadForTest(t, probe.approval.Payload), payload)
			return probe.projected, probe.projectedOK, probe.projectedErr
		},
		RecoverApplied: func(
			_ context.Context,
			command RecoverAppliedCommand,
		) (executionstore.Success, bool, error) {
			probe.calls = append(probe.calls, "recover")
			probe.recoverCommand = command
			return probe.recover, probe.recoverFound, probe.recoverErr
		},
		RecheckExecutionAuthority: func(
			_ context.Context,
			command ExecutionAuthorityCommand,
		) (ExecutionAuthority, error) {
			probe.calls = append(probe.calls, "authority")
			probe.authorityCommand = command
			return probe.authority, probe.authorityErr
		},
		Apply: func(_ context.Context, command ApplyCommand) (executionstore.Success, error) {
			probe.calls = append(probe.calls, "apply")
			probe.applyCommand = command
			return probe.applyResult, probe.applyErr
		},
		ProjectTerminal: func(context.Context, approval.Instance, RequestResult) error { return nil },
		Succeed: func(
			ctx context.Context,
			fence executionstore.ExecutionFence,
			success executionstore.Success,
			now time.Time,
		) error {
			probe.recordSettle(ctx, "succeed", fence, executionstore.Failure{}, success, now)
			return probe.settleErr
		},
		ReleaseUncertain: func(
			ctx context.Context,
			fence executionstore.ExecutionFence,
			now time.Time,
		) error {
			probe.recordSettle(ctx, "release", fence, executionstore.Failure{}, executionstore.Success{}, now)
			return probe.settleErr
		},
		FailDeterministic: func(
			ctx context.Context,
			fence executionstore.ExecutionFence,
			failure executionstore.Failure,
			now time.Time,
		) error {
			probe.recordSettle(ctx, "fail", fence, failure, executionstore.Success{}, now)
			return probe.settleErr
		},
		Now: func() time.Time {
			probe.calls = append(probe.calls, "now")
			probe.nowCalls++
			if probe.now == nil {
				return time.Time{}
			}
			return probe.now()
		},
	})
	return service, probe, claim, claimNow
}

func (probe *executionProbe) recordSettle(
	ctx context.Context,
	kind string,
	fence executionstore.ExecutionFence,
	failure executionstore.Failure,
	success executionstore.Success,
	now time.Time,
) {
	probe.calls = append(probe.calls, kind)
	probe.settleContexts = append(probe.settleContexts, ctx)
	probe.settleContextLive = append(probe.settleContextLive, ctx.Err() == nil)
	probe.settleFences = append(probe.settleFences, fence)
	probe.settleFailures = append(probe.settleFailures, failure)
	probe.settleSuccesses = append(probe.settleSuccesses, success)
	probe.settleTimes = append(probe.settleTimes, now)
}

func TestExecuteUsesPreparedDeterministicPayloadAndPassesAuthorityOnce(t *testing.T) {
	service, probe, claim, claimNow := newExecutionProbe(
		t,
		RoleApplyProposal{RoleID: "role_1", RoleVersionID: "rlv_1", AgentID: "agt_target"},
	)

	err := service.Execute(context.Background(), claim, claimNow)
	require.NoError(t, err)
	require.Equal(t, []string{"load", "recover", "authority", "apply", "now", "succeed"}, probe.calls)
	require.Equal(t, claim.Execution.WorkspaceID, probe.loadWorkspaceID)
	require.Equal(t, claim.Execution.ApprovalID, probe.loadApprovalID)
	require.Equal(t, probe.approval, probe.authorityCommand.Approval)
	require.Equal(t, strictDecodePayloadForTest(t, probe.approval.Payload), probe.authorityCommand.Payload)
	require.Equal(t, "mem_manager", probe.authorityCommand.RequiredManagerMemberID)
	require.Equal(t, "agt_target", probe.authorityCommand.ApplyTargetAgentID)
	require.Equal(t, probe.approval, probe.applyCommand.Approval)
	require.Equal(t, strictDecodePayloadForTest(t, probe.approval.Payload), probe.applyCommand.Payload)
	require.Equal(t, probe.authority, probe.applyCommand.Authority)
	require.Equal(t, probe.approval.ID, probe.applyCommand.OperationKey)
	require.Equal(t, probe.approval.PayloadDigest, probe.applyCommand.RequestDigest)
	requireSingleSettle(t, probe, claim, claimNow, "succeed")
	require.Equal(t, probe.applyResult, probe.settleSuccesses[0])
}

func TestExecuteRejectsInvalidClaimBeforeAnyDependency(t *testing.T) {
	for _, testCase := range []struct {
		name   string
		mutate func(*executionstore.ClaimedExecution, *time.Time)
	}{
		{"zero now", func(_ *executionstore.ClaimedExecution, now *time.Time) { *now = time.Time{} }},
		{"both workspace empty", func(claim *executionstore.ClaimedExecution, _ *time.Time) {
			claim.Execution.WorkspaceID, claim.Fence.WorkspaceID = "", ""
		}},
		{"both workspace blank", func(claim *executionstore.ClaimedExecution, _ *time.Time) {
			claim.Execution.WorkspaceID, claim.Fence.WorkspaceID = " ", " "
		}},
		{"both workspace untrimmed", func(claim *executionstore.ClaimedExecution, _ *time.Time) {
			claim.Execution.WorkspaceID, claim.Fence.WorkspaceID = " ws_1 ", " ws_1 "
		}},
		{"both approval empty", func(claim *executionstore.ClaimedExecution, _ *time.Time) {
			claim.Execution.ApprovalID, claim.Fence.ApprovalID = "", ""
		}},
		{"both approval blank", func(claim *executionstore.ClaimedExecution, _ *time.Time) {
			claim.Execution.ApprovalID, claim.Fence.ApprovalID = " ", " "
		}},
		{"both approval untrimmed", func(claim *executionstore.ClaimedExecution, _ *time.Time) {
			claim.Execution.ApprovalID, claim.Fence.ApprovalID = " appr_1 ", " appr_1 "
		}},
		{"both token empty", func(claim *executionstore.ClaimedExecution, _ *time.Time) {
			claim.Execution.LeaseToken, claim.Fence.LeaseToken = "", ""
		}},
		{"both token blank", func(claim *executionstore.ClaimedExecution, _ *time.Time) {
			claim.Execution.LeaseToken, claim.Fence.LeaseToken = " ", " "
		}},
		{"both token untrimmed", func(claim *executionstore.ClaimedExecution, _ *time.Time) {
			claim.Execution.LeaseToken, claim.Fence.LeaseToken = " lease_1 ", " lease_1 "
		}},
		{"execution workspace", func(claim *executionstore.ClaimedExecution, _ *time.Time) { claim.Execution.WorkspaceID = "ws_other" }},
		{"execution approval", func(claim *executionstore.ClaimedExecution, _ *time.Time) { claim.Execution.ApprovalID = "appr_other" }},
		{"execution token", func(claim *executionstore.ClaimedExecution, _ *time.Time) { claim.Execution.LeaseToken = "lease_other" }},
		{"fence workspace", func(claim *executionstore.ClaimedExecution, _ *time.Time) { claim.Fence.WorkspaceID = "ws_other" }},
		{"fence approval", func(claim *executionstore.ClaimedExecution, _ *time.Time) { claim.Fence.ApprovalID = "appr_other" }},
		{"fence token", func(claim *executionstore.ClaimedExecution, _ *time.Time) { claim.Fence.LeaseToken = "lease_other" }},
		{"not running", func(claim *executionstore.ClaimedExecution, _ *time.Time) {
			claim.Execution.Status = executionstore.ExecutionQueued
		}},
		{"nil expiry", func(claim *executionstore.ClaimedExecution, _ *time.Time) { claim.Execution.LeaseExpiresAt = nil }},
		{"expiry equal", func(claim *executionstore.ClaimedExecution, now *time.Time) {
			claim.Execution.LeaseExpiresAt = timePointer(*now)
		}},
		{"expiry past", func(claim *executionstore.ClaimedExecution, now *time.Time) {
			claim.Execution.LeaseExpiresAt = timePointer(now.Add(-time.Second))
		}},
	} {
		t.Run(testCase.name, func(t *testing.T) {
			service, probe, claim, claimNow := newExecutionProbe(
				t, RoleCreateProposal{Name: "Release", Definition: "Review releases"},
			)
			testCase.mutate(&claim, &claimNow)

			err := service.Execute(context.Background(), claim, claimNow)
			require.ErrorIs(t, err, ErrInvariant)
			require.Empty(t, probe.calls)
			require.Zero(t, probe.nowCalls)
		})
	}
}

func TestExecuteRequiresStrictSubmissionDeterministicPayload(t *testing.T) {
	description := "updated"
	for name, proposal := range map[string]RoleProposal{
		"create": RoleCreateProposal{Name: "Release", Definition: "Review releases"},
		"update": RoleUpdateProposal{
			RoleID: "role_1", ExpectedRoleVersionID: "rlv_1", Description: &description,
		},
		"delete": RoleDeleteProposal{RoleID: "role_1", ExpectedRoleVersionID: "rlv_1"},
		"apply":  RoleApplyProposal{RoleID: "role_1", RoleVersionID: "rlv_1", AgentID: "agt_target"},
	} {
		t.Run("prepare submission "+name, func(t *testing.T) {
			service, probe, claim, claimNow := newExecutionProbe(t, proposal)
			require.NoError(t, service.Execute(context.Background(), claim, claimNow),
				"prepareSubmission deterministicJSON and digest are the positive contract")
			require.Equal(t, []string{"load", "recover", "authority", "apply", "now", "succeed"}, probe.calls)
			require.Equal(t, probe.approval, probe.applyCommand.Approval)
			require.Equal(t, strictDecodePayloadForTest(t, probe.approval.Payload), probe.applyCommand.Payload)
			requireSingleSettle(t, probe, claim, claimNow, "succeed")
			require.Equal(t, probe.applyResult, probe.settleSuccesses[0])
		})
	}

	for _, testCase := range []struct {
		name   string
		mutate func(t *testing.T, value approval.Instance) []byte
	}{
		{"typed field order with deterministic digest", typedPayloadJSONForTest},
		{"pretty whitespace with deterministic digest", prettyPayloadJSONForTest},
	} {
		t.Run(testCase.name, func(t *testing.T) {
			service, probe, claim, claimNow := newExecutionProbe(
				t, RoleCreateProposal{Name: "Release", Definition: "Review releases"},
			)
			probe.approval.Payload = testCase.mutate(t, probe.approval)

			require.NoError(t, service.Execute(context.Background(), claim, claimNow))
			require.Equal(t, []string{"load", "recover", "authority", "apply", "now", "succeed"}, probe.calls)
			requireSingleSettle(t, probe, claim, claimNow, "succeed")
		})
	}

	for _, testCase := range []struct {
		name   string
		mutate func(t *testing.T, value approval.Instance) []byte
	}{
		{"typed field order with raw digest", typedPayloadJSONForTest},
		{"pretty whitespace with raw digest", prettyPayloadJSONForTest},
		{"field case", func(_ *testing.T, value approval.Instance) []byte {
			return bytes.Replace(value.Payload, []byte(`"action"`), []byte(`"Action"`), 1)
		}},
		{"untrimmed value", func(_ *testing.T, value approval.Instance) []byte {
			return bytes.Replace(value.Payload, []byte(`"Name":"Release"`), []byte(`"Name":" Release "`), 1)
		}},
		{"duplicate exact", func(_ *testing.T, value approval.Instance) []byte {
			return injectObjectField(value.Payload, `"action":"create",`)
		}},
		{"duplicate ASCII case", func(_ *testing.T, value approval.Instance) []byte {
			return injectObjectField(value.Payload, `"Action":"create",`)
		}},
		{"duplicate nested exact", func(_ *testing.T, value approval.Instance) []byte {
			return bytes.Replace(
				value.Payload,
				[]byte(`"Name":"Release"`),
				[]byte(`"Name":"Release","Name":"Other"`),
				1,
			)
		}},
		{"duplicate nested ASCII case", func(_ *testing.T, value approval.Instance) []byte {
			return bytes.Replace(
				value.Payload,
				[]byte(`"Name":"Release"`),
				[]byte(`"Name":"Release","name":"Other"`),
				1,
			)
		}},
		{"duplicate unicode long s", func(_ *testing.T, value approval.Instance) []byte {
			return bytes.Replace(
				value.Payload,
				[]byte(`"Description":""`),
				[]byte(`"Description":"","Deſcription":"`+executionSecretSentinel+`"`),
				1,
			)
		}},
		{"duplicate unicode kelvin", func(_ *testing.T, value approval.Instance) []byte {
			return appendBeforeObjectEnd(value.Payload, `,"unknownKey":1,"unknownKey":2`)
		}},
		{"unknown", func(_ *testing.T, value approval.Instance) []byte {
			return appendBeforeObjectEnd(value.Payload, `,"unknown":"`+executionSecretSentinel+`"`)
		}},
		{"trailing", func(_ *testing.T, value approval.Instance) []byte {
			return append(append([]byte(nil), value.Payload...), []byte(` {}`)...)
		}},
		{"action union", func(_ *testing.T, value approval.Instance) []byte {
			return appendBeforeObjectEnd(value.Payload, `,"delete":{"RoleID":"role_1","ExpectedRoleVersionID":"rlv_1"}`)
		}},
	} {
		t.Run(testCase.name, func(t *testing.T) {
			service, probe, claim, claimNow := newExecutionProbe(
				t, RoleCreateProposal{Name: "Release", Definition: "Review releases"},
			)
			probe.approval.Payload = testCase.mutate(t, probe.approval)
			sum := sha256.Sum256(probe.approval.Payload)
			probe.approval.PayloadDigest = hex.EncodeToString(sum[:])

			err := service.Execute(context.Background(), claim, claimNow)
			require.NoError(t, err)
			require.Equal(t, []string{"load", "now", "fail"}, probe.calls)
			requireSafeFailure(t, probe.settleFailures[0], "invalid_request")
			requireSingleSettle(t, probe, claim, claimNow, "fail")
		})
	}
}

func TestExecuteRejectsPayloadWithRecomputedWrongIdentity(t *testing.T) {
	for _, field := range []string{"applicant", "request"} {
		t.Run(field, func(t *testing.T) {
			service, probe, claim, claimNow := newExecutionProbe(
				t, RoleCreateProposal{Name: "Release", Definition: "Review releases"},
			)
			payload := strictDecodePayloadForTest(t, probe.approval.Payload)
			if field == "applicant" {
				payload.ApplicantAgentID = "agt_other"
			} else {
				payload.RequestID = "req_other"
			}
			raw, err := json.Marshal(payload)
			require.NoError(t, err)
			probe.approval.Payload, err = deterministicJSONBytes(raw)
			require.NoError(t, err)
			sum := sha256.Sum256(probe.approval.Payload)
			probe.approval.PayloadDigest = hex.EncodeToString(sum[:])

			require.NoError(t, service.Execute(context.Background(), claim, claimNow))
			require.Equal(t, []string{"load", "now", "fail"}, probe.calls)
			requireSafeFailure(t, probe.settleFailures[0], "invalid_request")
			requireSingleSettle(t, probe, claim, claimNow, "fail")
		})
	}
}

func TestExecuteClassifiesAuthorityResultsAndPassesValue(t *testing.T) {
	t.Run("passthrough and membership", func(t *testing.T) {
		service, probe, claim, claimNow := newExecutionProbe(
			t, RoleApplyProposal{RoleID: "role_1", RoleVersionID: "rlv_1", AgentID: "agt_target"},
		)
		probe.authority = ExecutionAuthority{
			ManagerUserID: "usr_manager", OwnedAgentIDs: []string{"agt_other", "agt_target"},
		}
		require.NoError(t, service.Execute(context.Background(), claim, claimNow))
		require.Equal(t, probe.authority, probe.applyCommand.Authority)
		require.Equal(t, 1, countCall(probe.calls, "authority"))
	})

	for _, testCase := range []struct {
		name      string
		authority ExecutionAuthority
	}{
		{"empty manager", ExecutionAuthority{}},
		{"blank manager", ExecutionAuthority{ManagerUserID: "  "}},
		{"untrimmed manager", ExecutionAuthority{ManagerUserID: " usr_manager "}},
		{"blank owned", ExecutionAuthority{ManagerUserID: "usr_manager", OwnedAgentIDs: []string{""}}},
		{"untrimmed owned", ExecutionAuthority{ManagerUserID: "usr_manager", OwnedAgentIDs: []string{" agt_target "}}},
		{"duplicate owned", ExecutionAuthority{ManagerUserID: "usr_manager", OwnedAgentIDs: []string{"agt_target", "agt_target"}}},
	} {
		t.Run(testCase.name, func(t *testing.T) {
			service, probe, claim, claimNow := newExecutionProbe(
				t, RoleApplyProposal{RoleID: "role_1", RoleVersionID: "rlv_1", AgentID: "agt_target"},
			)
			probe.authority = testCase.authority
			require.NoError(t, service.Execute(context.Background(), claim, claimNow))
			require.Equal(t, []string{"load", "recover", "authority", "now", "release"}, probe.calls)
			requireSingleSettle(t, probe, claim, claimNow, "release")
		})
	}

	t.Run("target not owned", func(t *testing.T) {
		service, probe, claim, claimNow := newExecutionProbe(
			t, RoleApplyProposal{RoleID: "role_1", RoleVersionID: "rlv_1", AgentID: "agt_target"},
		)
		probe.authority.OwnedAgentIDs = []string{"agt_other"}
		require.NoError(t, service.Execute(context.Background(), claim, claimNow))
		require.Equal(t, []string{"load", "recover", "authority", "now", "fail"}, probe.calls)
		requireSafeFailure(t, probe.settleFailures[0], "authority_denied")
		requireSingleSettle(t, probe, claim, claimNow, "fail")
	})
}

func TestExecuteReleasesLoadAndAuthorityUncertainErrors(t *testing.T) {
	for _, testCase := range []struct {
		name string
		err  error
	}{
		{"generic", errors.New(executionSecretSentinel)},
		{"not found", executionstore.ErrNotFound},
		{"cancelled", context.Canceled},
		{"wrapped deterministic looking", fmt.Errorf(
			"load failed: %w: %s",
			&ApplyError{Outcome: ApplyNotFound},
			executionSecretSentinel,
		)},
	} {
		t.Run("load "+testCase.name, func(t *testing.T) {
			service, probe, claim, claimNow := newExecutionProbe(
				t, RoleCreateProposal{Name: "Release", Definition: "Review releases"},
			)
			probe.loadErr = testCase.err
			require.NoError(t, service.Execute(context.Background(), claim, claimNow))
			require.Equal(t, []string{"load", "now", "release"}, probe.calls)
			requireSingleSettle(t, probe, claim, claimNow, "release")
		})
		t.Run("authority "+testCase.name, func(t *testing.T) {
			service, probe, claim, claimNow := newExecutionProbe(
				t, RoleCreateProposal{Name: "Release", Definition: "Review releases"},
			)
			probe.authorityErr = testCase.err
			require.NoError(t, service.Execute(context.Background(), claim, claimNow))
			require.Equal(t, []string{"load", "recover", "authority", "now", "release"}, probe.calls)
			requireSingleSettle(t, probe, claim, claimNow, "release")
		})
	}

	t.Run("authority typed denied", func(t *testing.T) {
		service, probe, claim, claimNow := newExecutionProbe(
			t, RoleCreateProposal{Name: "Release", Definition: "Review releases"},
		)
		probe.authorityErr = fmt.Errorf(
			"%w: %s",
			&ApplyError{Outcome: ApplyAuthorityDenied},
			executionSecretSentinel,
		)
		require.NoError(t, service.Execute(context.Background(), claim, claimNow))
		require.Equal(t, []string{"load", "recover", "authority", "now", "fail"}, probe.calls)
		requireSafeFailure(t, probe.settleFailures[0], "authority_denied")
		requireSingleSettle(t, probe, claim, claimNow, "fail")
	})
}

func TestExecuteClassifiesApplyOutcomesAndNeverPersistsCauses(t *testing.T) {
	for _, testCase := range []struct {
		outcome ApplyOutcome
		code    string
		message string
	}{
		{ApplyInvalidRequest, "invalid_request", "The approved role request is invalid."},
		{ApplyAuthorityDenied, "authority_denied", "The role operation is no longer authorized."},
		{ApplyNotFound, "not_found", "The requested role resource was not found."},
		{ApplyVersionConflict, "version_conflict", "The role changed before this request was applied."},
		{ApplyOperationConflict, "operation_conflict", "A conflicting role operation already exists."},
	} {
		t.Run(string(testCase.outcome), func(t *testing.T) {
			service, probe, claim, claimNow := newExecutionProbe(
				t, RoleCreateProposal{Name: "Release", Definition: "Review releases"},
			)
			probe.applyErr = fmt.Errorf(
				"%w: %s",
				&ApplyError{Outcome: testCase.outcome},
				executionSecretSentinel,
			)
			require.NoError(t, service.Execute(context.Background(), claim, claimNow))
			require.Equal(t, []string{"load", "recover", "authority", "apply", "now", "fail"}, probe.calls)
			require.Equal(t, executionstore.Failure{Code: testCase.code, Message: testCase.message}, probe.settleFailures[0])
			require.NotContains(t, probe.settleFailures[0].Message, executionSecretSentinel)
			requireSingleSettle(t, probe, claim, claimNow, "fail")
		})
	}

	for _, testCase := range []struct {
		name string
		err  error
	}{
		{"timeout", &ApplyError{Outcome: ApplyTimeout}},
		{"server", &ApplyError{Outcome: ApplyServerError}},
		{"cancelled", &ApplyError{Outcome: ApplyContextCancelled}},
		{"response lost", &ApplyError{Outcome: ApplyResponseLost}},
		{"unknown outcome", fmt.Errorf("%w: %s", &ApplyError{Outcome: "future"}, executionSecretSentinel)},
		{"naked", errors.New(executionSecretSentinel)},
	} {
		t.Run(testCase.name, func(t *testing.T) {
			service, probe, claim, claimNow := newExecutionProbe(
				t, RoleCreateProposal{Name: "Release", Definition: "Review releases"},
			)
			probe.applyErr = testCase.err
			require.NoError(t, service.Execute(context.Background(), claim, claimNow))
			require.Equal(t, []string{"load", "recover", "authority", "apply", "now", "release"}, probe.calls)
			require.Empty(t, probe.settleFailures[0])
			requireSingleSettle(t, probe, claim, claimNow, "release")
		})
	}

	t.Run("empty success", func(t *testing.T) {
		service, probe, claim, claimNow := newExecutionProbe(
			t, RoleCreateProposal{Name: "Release", Definition: "Review releases"},
		)
		probe.applyResult = executionstore.Success{}
		require.NoError(t, service.Execute(context.Background(), claim, claimNow))
		require.Equal(t, []string{"load", "recover", "authority", "apply", "now", "release"}, probe.calls)
		requireSingleSettle(t, probe, claim, claimNow, "release")
	})
}

func TestExecuteUsesFreshSettleTimeAndExactlyOneOriginalFence(t *testing.T) {
	for _, testCase := range []struct {
		name      string
		applyErr  error
		settle    string
		settleErr error
	}{
		{"success", nil, "succeed", nil},
		{"deterministic", &ApplyError{Outcome: ApplyNotFound}, "fail", nil},
		{"uncertain", errors.New("network"), "release", nil},
		{"expired conflict", nil, "succeed", executionstore.ErrConflict},
	} {
		t.Run(testCase.name, func(t *testing.T) {
			service, probe, claim, claimNow := newExecutionProbe(
				t, RoleCreateProposal{Name: "Release", Definition: "Review releases"},
			)
			probe.applyErr = testCase.applyErr
			probe.settleErr = testCase.settleErr
			settleNow := claim.Execution.LeaseExpiresAt.Add(time.Second)
			probe.now = func() time.Time { return settleNow }

			err := service.Execute(context.Background(), claim, claimNow)
			if testCase.settleErr == nil {
				require.NoError(t, err)
			} else {
				require.ErrorIs(t, err, testCase.settleErr)
			}
			require.Equal(t, 1, totalSettles(probe))
			require.Equal(t, testCase.settle, probe.calls[len(probe.calls)-1])
			require.Equal(t, claim.Fence, probe.settleFences[0])
			require.Equal(t, settleNow, probe.settleTimes[0])
			require.NotEqual(t, claimNow, probe.settleTimes[0])
			require.Equal(t, 1, probe.nowCalls)
			require.Equal(t, "now", probe.calls[len(probe.calls)-2])
		})
	}

	t.Run("expiry equal reaches store", func(t *testing.T) {
		service, probe, claim, claimNow := newExecutionProbe(
			t, RoleCreateProposal{Name: "Release", Definition: "Review releases"},
		)
		probe.settleErr = executionstore.ErrConflict
		probe.now = func() time.Time { return *claim.Execution.LeaseExpiresAt }
		require.ErrorIs(t, service.Execute(context.Background(), claim, claimNow), executionstore.ErrConflict)
		require.Equal(t, *claim.Execution.LeaseExpiresAt, probe.settleTimes[0])
		require.Equal(t, 1, totalSettles(probe))
		require.Equal(t, 1, probe.nowCalls)
		require.Equal(t, "now", probe.calls[len(probe.calls)-2])
	})

	for _, testCase := range []struct {
		name         string
		now          func(time.Time) func() time.Time
		wantNowCalls int
	}{
		{"nil clock", func(time.Time) func() time.Time { return nil }, 0},
		{"zero clock", func(time.Time) func() time.Time { return func() time.Time { return time.Time{} } }, 1},
		{"backward clock", func(claimNow time.Time) func() time.Time {
			return func() time.Time { return claimNow.Add(-time.Second) }
		}, 1},
	} {
		t.Run(testCase.name, func(t *testing.T) {
			service, probe, claim, claimNow := newExecutionProbe(
				t, RoleCreateProposal{Name: "Release", Definition: "Review releases"},
			)
			probe.now = testCase.now(claimNow)
			if testCase.name == "nil clock" {
				service.dependencies.Now = nil
			}
			require.ErrorIs(t, service.Execute(context.Background(), claim, claimNow), ErrInvariant)
			require.Equal(t, 0, totalSettles(probe))
			require.Equal(t, testCase.wantNowCalls, probe.nowCalls)
			if testCase.wantNowCalls == 0 {
				require.Equal(t, []string{"load", "recover", "authority", "apply"}, probe.calls)
			} else {
				require.Equal(t, []string{"load", "recover", "authority", "apply", "now"}, probe.calls)
			}
		})
	}
}

func TestExecuteReturnsEverySettleErrorWithoutFallback(t *testing.T) {
	for _, settleErr := range []error{
		executionstore.ErrConflict,
		errors.New(executionSecretSentinel),
	} {
		for _, testCase := range []struct {
			name     string
			applyErr error
			settle   string
		}{
			{"succeed", nil, "succeed"},
			{"fail", &ApplyError{Outcome: ApplyNotFound}, "fail"},
			{"release", errors.New("network uncertain"), "release"},
		} {
			t.Run(testCase.name+"/"+settleErr.Error(), func(t *testing.T) {
				service, probe, claim, claimNow := newExecutionProbe(
					t, RoleCreateProposal{Name: "Release", Definition: "Review releases"},
				)
				probe.applyErr = testCase.applyErr
				probe.settleErr = settleErr

				err := service.Execute(context.Background(), claim, claimNow)
				require.ErrorIs(t, err, settleErr)
				requireSingleSettle(t, probe, claim, claimNow, testCase.settle)
				require.Equal(t, 1, probe.nowCalls)
			})
		}
	}
}

func TestExecuteCancelledContextUsesBoundedDetachedSingleSettle(t *testing.T) {
	service, probe, claim, claimNow := newExecutionProbe(
		t, RoleCreateProposal{Name: "Release", Definition: "Review releases"},
	)
	probe.applyErr = &ApplyError{Outcome: ApplyContextCancelled}
	probe.settleErr = errors.New("settle failed")
	ctx, cancel := context.WithCancel(context.Background())
	cancel()

	err := service.Execute(ctx, claim, claimNow)
	require.EqualError(t, err, "settle failed")
	require.Equal(t, 1, totalSettles(probe))
	require.Equal(t, []string{"load", "recover", "authority", "apply", "now", "release"}, probe.calls)
	require.Equal(t, 1, probe.nowCalls)
	require.True(t, probe.settleContextLive[0])
	deadline, ok := probe.settleContexts[0].Deadline()
	require.True(t, ok)
	require.WithinDuration(t, time.Now(), deadline, time.Minute)
	require.ErrorIs(t, probe.settleContexts[0].Err(), context.Canceled)
}

func requireSafeFailure(t *testing.T, failure executionstore.Failure, code string) {
	t.Helper()
	messages := map[string]string{
		"invalid_request":    "The approved role request is invalid.",
		"authority_denied":   "The role operation is no longer authorized.",
		"not_found":          "The requested role resource was not found.",
		"version_conflict":   "The role changed before this request was applied.",
		"operation_conflict": "A conflicting role operation already exists.",
	}
	require.Equal(t, executionstore.Failure{Code: code, Message: messages[code]}, failure)
	require.NotContains(t, failure.Message, executionSecretSentinel)
}

func strictDecodePayloadForTest(t *testing.T, raw []byte) ApprovalPayload {
	var payload ApprovalPayload
	require.NoError(t, json.Unmarshal(raw, &payload))
	return payload
}

func typedPayloadJSONForTest(t *testing.T, value approval.Instance) []byte {
	raw, err := json.Marshal(strictDecodePayloadForTest(t, value.Payload))
	require.NoError(t, err)
	return raw
}

func prettyPayloadJSONForTest(t *testing.T, value approval.Instance) []byte {
	t.Helper()
	var output bytes.Buffer
	require.NoError(t, json.Indent(&output, value.Payload, "", "  "))
	return output.Bytes()
}

func injectObjectField(raw []byte, field string) []byte {
	return append(append([]byte{'{'}, []byte(field)...), raw[1:]...)
}

func appendBeforeObjectEnd(raw []byte, suffix string) []byte {
	return append(append(append([]byte(nil), raw[:len(raw)-1]...), []byte(suffix)...), '}')
}

func timePointer(value time.Time) *time.Time { return &value }

func countCall(calls []string, target string) int {
	count := 0
	for _, call := range calls {
		if call == target {
			count++
		}
	}
	return count
}

func totalSettles(probe *executionProbe) int {
	return countCall(probe.calls, "succeed") + countCall(probe.calls, "release") + countCall(probe.calls, "fail")
}

func requireSingleSettle(
	t *testing.T,
	probe *executionProbe,
	claim executionstore.ClaimedExecution,
	claimNow time.Time,
	kind string,
) {
	t.Helper()
	require.Equal(t, 1, totalSettles(probe))
	require.Equal(t, kind, probe.calls[len(probe.calls)-1])
	require.Equal(t, 1, probe.nowCalls)
	require.Equal(t, "now", probe.calls[len(probe.calls)-2])
	require.Equal(t, claim.Fence, probe.settleFences[0])
	require.NotZero(t, probe.settleTimes[0])
	require.False(t, probe.settleTimes[0].Before(claimNow))
	require.NotEqual(t, claimNow, probe.settleTimes[0])
	deadline, ok := probe.settleContexts[0].Deadline()
	require.True(t, ok)
	require.WithinDuration(t, time.Now(), deadline, time.Minute)
}
