package app

import (
	"context"
	"testing"
	"time"

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

// TestAppNotificationWithdrawCallsDerivativeCleanerPostCommit verifies that
// MutateAppNotification's withdraw path calls the derivative cleaner post-commit.
func TestAppNotificationWithdrawCallsDerivativeCleanerPostCommit(t *testing.T) {
	ctx := context.Background()
	pool := startAppRollbackDB(t, ctx)
	var cleanerCalls []string
	store := New(pool, Options{
		AppCredentialStorageKey: testAppCredentialStorageKey(),
		DeleteAttachmentDerivatives: func(_ context.Context, _ string, attachmentID string) {
			cleanerCalls = append(cleanerCalls, attachmentID)
		},
	})
	now := time.Now().UTC()
	fixture := seedAppAdminFixture(t, pool, "withdraw_derivative_cleanup", now)

	const (
		identityID = "agentteam.test.approval.withdraw_cleanup"
		handlerKey = "agentteam.test.approval.withdraw_cleanup"
		basisKind  = "test_withdraw_cleanup_assignment"
		basisRef   = "assignment:req_withdraw_cleanup"
	)
	approvalApp := installApprovalAppForNotificationTests(t, ctx, pool, fixture, "withdraw_cleanup", now)
	configureApprovalAppRecipientForTests(t, ctx, pool, store, fixture, approvalApp, "test.approval", "request", basisKind, basisRef)
	require.NoError(t, store.RegisterAppNotificationBuiltinService(AppNotificationBuiltinServiceRegistration{
		IdentityID: identityID, ServiceDefinitionSlug: "agentteam-approval", ActionHandlerKey: handlerKey,
		AllowedBusinessNamespaces: []string{"test.approval"},
		AllowedTargetKinds:        []string{AppNotificationTargetHumanDM},
		AllowedContentKinds:       []string{AppNotificationContentInteractiveCard},
	}))

	created, err := store.CreateAppNotificationForBuiltin(ctx, identityID, fixture.WorkspaceID, AppNotificationCommand{
		ClientNotificationID: "approval:req_withdraw_cleanup",
		Target:               AppNotificationTarget{Kind: AppNotificationTargetHumanDM, MemberID: fixture.MemberID},
		RecipientBasis:       AppNotificationRecipientBasis{Kind: basisKind, Ref: basisRef},
		Content: AppNotificationContent{
			Kind:         AppNotificationContentInteractiveCard,
			FallbackText: "Approval requested",
			CardJSON:     []byte(`{"schemaVersion":"1","actions":[{"actionKey":"approve","type":"callback","label":"Approve"}]}`),
		},
		BusinessObject: AppNotificationBusinessObject{
			Namespace: "test.approval", Type: "request", ID: "req_withdraw_cleanup", Version: 1,
		},
		CausedByActor: AppNotificationActorRef{Type: "human", ID: fixture.MemberID},
	})
	require.NoError(t, err)
	require.Equal(t, int64(1), created.NotificationRevision)

	// Attach a thumbnail-able image to the message so we have a derivative target.
	attID := "att_withdraw_cleanup"
	mustExec(t, pool, `
INSERT INTO attachments (id, workspace_id, scope_id, uploaded_by_member_id, message_id, filename, content_type, size_bytes, created_at)
VALUES ($1, $2, $3, $4, $5, 'photo.png', 'image/png', 1024, $6)
`, attID, fixture.WorkspaceID, created.ScopeID, fixture.MemberID, created.MessageID, now)

	require.Empty(t, cleanerCalls, "cleaner must not fire before withdraw")

	// Withdraw the notification — scrubs the message + its attachments.
	_, err = store.MutateAppNotificationForBuiltin(ctx, identityID, fixture.WorkspaceID, AppNotificationMutationCommand{
		NotificationID:   created.NotificationID,
		Kind:             AppNotificationMutationWithdraw,
		ExpectedRevision: created.NotificationRevision,
		ClientMutationID: "withdraw:cleanup:test",
		CausedByActor:    AppNotificationActorRef{Type: appNotificationExecutionActorInternalService, ID: identityID},
	})
	require.NoError(t, err)

	// Post-commit: the derivative cleaner must have been called.
	require.Contains(t, cleanerCalls, attID, "derivative cleaner must fire post-commit with the withdrawn attachment's ID")
}
