package execution

import (
	"context"
	"testing"

	"github.com/stretchr/testify/require"

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

func TestRequestGetProjectsDurableActionAndApplyAgentAcrossAllPublicStates(t *testing.T) {
	states := []struct {
		name          string
		approvalState string
		execution     *executionstore.Execution
		wantStatus    RequestStatus
	}{
		{name: "pending", approvalState: approval.StatePending, wantStatus: RequestPending},
		{
			name: "processing", approvalState: approval.StateApproved,
			execution:  &executionstore.Execution{Status: executionstore.ExecutionRunning},
			wantStatus: RequestProcessing,
		},
		{
			name: "succeeded", approvalState: approval.StateApproved,
			execution: &executionstore.Execution{
				Status: executionstore.ExecutionSucceeded, ResultRoleID: "role_result",
				ResultRoleVersionID: "rlv_result",
			},
			wantStatus: RequestSucceeded,
		},
		{name: "rejected", approvalState: approval.StateRejected, wantStatus: RequestRejected},
		{
			name: "failed", approvalState: approval.StateApproved,
			execution: &executionstore.Execution{
				Status: executionstore.ExecutionFailed, FailureCode: "not_found",
				FailureMessage: "Role no longer exists",
			},
			wantStatus: RequestFailed,
		},
		{name: "cancelled", approvalState: approval.StateCancelled, wantStatus: RequestCancelled},
	}
	actions := []struct {
		name        string
		action      RoleRequestAction
		proposal    RoleProposal
		wantAgentID string
	}{
		{
			name: "create", action: ActionCreate,
			proposal: RoleCreateProposal{Name: "Release", Definition: "Review releases"},
		},
		{
			name: "update", action: ActionUpdate,
			proposal: RoleUpdateProposal{
				RoleID: "role_1", ExpectedRoleVersionID: "rlv_1",
				Name: requestResultStringPointer("Release reviewer"),
			},
		},
		{
			name: "delete", action: ActionDelete,
			proposal: RoleDeleteProposal{RoleID: "role_1", ExpectedRoleVersionID: "rlv_1"},
		},
		{
			name: "apply", action: ActionApply,
			proposal: RoleApplyProposal{
				RoleID: "role_1", RoleVersionID: "rlv_1", AgentID: "agt_target",
			},
			wantAgentID: "agt_target",
		},
	}

	for _, state := range states {
		for _, action := range actions {
			t.Run(state.name+"/"+action.name, func(t *testing.T) {
				command := redSubmit()
				command.Proposal = action.proposal
				instance := redApprovalForSubmit(t, command, state.approvalState)
				dependencies := &recordingDependencies{}
				service := newREDService(t, dependencies)
				service.dependencies.LookupApproval = func(context.Context, ApprovalLookup) (approval.Instance, bool, error) {
					return instance, true, nil
				}
				service.dependencies.LoadExecution = func(context.Context, string, string) (executionstore.Execution, error) {
					require.NotNil(t, state.execution)
					execution := *state.execution
					execution.ApprovalID = instance.ID
					execution.WorkspaceID = instance.WorkspaceID
					return execution, nil
				}
				forbidRequestGetMutations(t, service)

				result, err := service.RequestGet(context.Background(), RequestGetCommand{
					Principal: AgentPrincipal(redPrincipal()), RequestID: command.RequestID,
				})
				require.NoError(t, err)
				require.Equal(t, state.wantStatus, result.Status)
				require.Equal(t, action.action, result.Action,
					"action must be projected from the durable Approval payload")
				require.Equal(t, action.wantAgentID, result.AgentID,
					"only apply may expose its durable target Agent")
			})
		}
	}
}

func requestResultStringPointer(value string) *string {
	return &value
}
