// Package permission provides integration tests for the permission removal workflow.
// This file contains property-based integration tests that verify the automatic
// permission revocation workflow for permissions past the grace period.
//
// Feature: autosso-refactoring, Property 19: 权限自动回收
// For any permission past the grace period, the system should automatically
// revoke it and send notification.
//
// **Validates: Requirements 10.3**
package permission

import (
	"context"
	"errors"
	"sync"
	"testing"
	"time"

	"AutoSSO/internal/config"
	"AutoSSO/internal/pkg/logger"
	"AutoSSO/internal/provider"
	"AutoSSO/internal/state"

	"pgregory.net/rapid"
)

// =============================================================================
// Mock Implementations for Removal Integration Testing
// =============================================================================

// removalMockStateStore is a mock state store for removal integration testing.
type removalMockStateStore struct {
	instances        map[string]*state.ApprovalInstance
	pendingRemovals  []*state.PendingRemoval
	deletedInstances map[string]bool
	mu               sync.Mutex
	nextRemovalID    int64
	saveError        error
	deleteError      error
}

func newRemovalMockStateStore() *removalMockStateStore {
	return &removalMockStateStore{
		instances:        make(map[string]*state.ApprovalInstance),
		pendingRemovals:  make([]*state.PendingRemoval, 0),
		deletedInstances: make(map[string]bool),
		nextRemovalID:    1,
	}
}

func (s *removalMockStateStore) SaveInstance(ctx context.Context, instance *state.ApprovalInstance) error {
	s.mu.Lock()
	defer s.mu.Unlock()
	if s.saveError != nil {
		return s.saveError
	}
	if instance != nil {
		s.instances[instance.ID] = instance
	}
	return nil
}

func (s *removalMockStateStore) GetInstance(ctx context.Context, instanceID string) (*state.ApprovalInstance, error) {
	s.mu.Lock()
	defer s.mu.Unlock()
	return s.instances[instanceID], nil
}

func (s *removalMockStateStore) ListInstances(ctx context.Context, filter *state.InstanceFilter) ([]*state.ApprovalInstance, error) {
	s.mu.Lock()
	defer s.mu.Unlock()
	result := make([]*state.ApprovalInstance, 0, len(s.instances))
	for _, inst := range s.instances {
		if filter != nil {
			if filter.Status != "" && inst.Status != filter.Status {
				continue
			}
			if filter.CloudType != "" && inst.CloudType != filter.CloudType {
				continue
			}
			if filter.Email != "" && inst.Email != filter.Email {
				continue
			}
		}
		result = append(result, inst)
	}
	return result, nil
}

func (s *removalMockStateStore) DeleteInstance(ctx context.Context, instanceID string) error {
	s.mu.Lock()
	defer s.mu.Unlock()
	delete(s.instances, instanceID)
	return nil
}

func (s *removalMockStateStore) SavePendingRemoval(ctx context.Context, removal *state.PendingRemoval) error {
	s.mu.Lock()
	defer s.mu.Unlock()
	if s.saveError != nil {
		return s.saveError
	}
	if removal != nil {
		removal.ID = s.nextRemovalID
		s.nextRemovalID++
		s.pendingRemovals = append(s.pendingRemovals, removal)
	}
	return nil
}

func (s *removalMockStateStore) GetPendingRemovals(ctx context.Context) ([]*state.PendingRemoval, error) {
	s.mu.Lock()
	defer s.mu.Unlock()
	result := make([]*state.PendingRemoval, len(s.pendingRemovals))
	copy(result, s.pendingRemovals)
	return result, nil
}

func (s *removalMockStateStore) DeletePendingRemoval(ctx context.Context, id int64) error {
	s.mu.Lock()
	defer s.mu.Unlock()
	if s.deleteError != nil {
		return s.deleteError
	}
	for i, r := range s.pendingRemovals {
		if r.ID == id {
			s.pendingRemovals = append(s.pendingRemovals[:i], s.pendingRemovals[i+1:]...)
			break
		}
	}
	return nil
}

func (s *removalMockStateStore) SaveDeletedInstance(ctx context.Context, instanceID string) error {
	s.mu.Lock()
	defer s.mu.Unlock()
	s.deletedInstances[instanceID] = true
	return nil
}

func (s *removalMockStateStore) IsInstanceDeleted(ctx context.Context, instanceID string) (bool, error) {
	s.mu.Lock()
	defer s.mu.Unlock()
	return s.deletedInstances[instanceID], nil
}

func (s *removalMockStateStore) Close() error {
	return nil
}

func (s *removalMockStateStore) getPendingRemovalsCount() int {
	s.mu.Lock()
	defer s.mu.Unlock()
	return len(s.pendingRemovals)
}

func (s *removalMockStateStore) hasPendingRemovalFor(accountID, email, permissionSet string) bool {
	s.mu.Lock()
	defer s.mu.Unlock()
	for _, r := range s.pendingRemovals {
		if r.AccountID == accountID && r.Email == email && r.PermissionSet == permissionSet {
			return true
		}
	}
	return false
}

func (s *removalMockStateStore) getPendingRemovalByID(id int64) *state.PendingRemoval {
	s.mu.Lock()
	defer s.mu.Unlock()
	for _, r := range s.pendingRemovals {
		if r.ID == id {
			return r
		}
	}
	return nil
}

// removalMockProvider is a mock provider for removal integration testing.
type removalMockProvider struct {
	name                  string
	addPermissionCalls    []provider.PermissionRequest
	removePermissionCalls []provider.PermissionRequest
	mu                    sync.Mutex
	shouldFail            bool
	failureError          error
}

func newRemovalMockProvider(name string) *removalMockProvider {
	return &removalMockProvider{
		name:                  name,
		addPermissionCalls:    make([]provider.PermissionRequest, 0),
		removePermissionCalls: make([]provider.PermissionRequest, 0),
	}
}

func (m *removalMockProvider) Name() string {
	return m.name
}

func (m *removalMockProvider) AddPermission(ctx context.Context, req *provider.PermissionRequest) error {
	m.mu.Lock()
	defer m.mu.Unlock()
	if req != nil {
		m.addPermissionCalls = append(m.addPermissionCalls, *req)
	}
	if m.shouldFail {
		if m.failureError != nil {
			return m.failureError
		}
		return context.DeadlineExceeded
	}
	return nil
}

func (m *removalMockProvider) RemovePermission(ctx context.Context, req *provider.PermissionRequest) error {
	m.mu.Lock()
	defer m.mu.Unlock()
	if req != nil {
		m.removePermissionCalls = append(m.removePermissionCalls, *req)
	}
	if m.shouldFail {
		if m.failureError != nil {
			return m.failureError
		}
		return context.DeadlineExceeded
	}
	return nil
}

func (m *removalMockProvider) GetUserID(ctx context.Context, email string) (string, error) {
	return "user-" + email, nil
}

func (m *removalMockProvider) ValidateCredentials(ctx context.Context) error {
	return nil
}

func (m *removalMockProvider) Close() error {
	return nil
}

func (m *removalMockProvider) getRemovePermissionCalls() []provider.PermissionRequest {
	m.mu.Lock()
	defer m.mu.Unlock()
	result := make([]provider.PermissionRequest, len(m.removePermissionCalls))
	copy(result, m.removePermissionCalls)
	return result
}

func (m *removalMockProvider) reset() {
	m.mu.Lock()
	defer m.mu.Unlock()
	m.addPermissionCalls = make([]provider.PermissionRequest, 0)
	m.removePermissionCalls = make([]provider.PermissionRequest, 0)
	m.shouldFail = false
	m.failureError = nil
}

// =============================================================================
// Generator Functions for Removal Property-Based Testing
// =============================================================================

// genRemovalEmail generates a valid email address for removal testing.
func genRemovalEmail() *rapid.Generator[string] {
	return rapid.Custom(func(t *rapid.T) string {
		user := rapid.StringMatching(`[a-z][a-z0-9]{2,10}`).Draw(t, "user")
		domain := rapid.SampledFrom([]string{"example.com", "test.org", "company.io", "pingcap.com"}).Draw(t, "domain")
		return user + "@" + domain
	})
}

// genRemovalAccountID generates a valid cloud account ID based on provider type.
func genRemovalAccountID(providerType provider.ProviderType) *rapid.Generator[string] {
	switch providerType {
	case provider.ProviderAWS:
		return rapid.StringMatching(`[0-9]{12}`)
	case provider.ProviderGCP:
		return rapid.StringMatching(`[a-z][a-z0-9-]{5,20}`)
	case provider.ProviderAzure:
		return rapid.StringMatching(`[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}`)
	case provider.ProviderAlibaba:
		return rapid.StringMatching(`[0-9]{16}`)
	default:
		return rapid.StringMatching(`[a-z0-9]{10,20}`)
	}
}

// genRemovalPermissionSet generates a valid permission set name.
func genRemovalPermissionSet() *rapid.Generator[string] {
	return rapid.OneOf(
		rapid.SampledFrom([]string{
			"DBaaS-Prod-ViewOnly-Role",
			"DBaaS-Prod-OnCall-Role",
			"DBaaS-Prod-Admin-Role",
			"DBaaS-Prod-Billing-Role",
			"PingCAP-SecurityAdmin-Role",
		}),
		rapid.StringMatching(`[A-Z][a-zA-Z0-9-]{5,30}-Role`),
	)
}

// genRemovalProviderType generates a valid provider type.
func genRemovalProviderType() *rapid.Generator[provider.ProviderType] {
	return rapid.SampledFrom([]provider.ProviderType{
		provider.ProviderAWS,
		provider.ProviderGCP,
		provider.ProviderAzure,
		provider.ProviderAlibaba,
	})
}

// genReadyPendingRemoval generates a pending removal that is ready for processing
// (scheduled time is in the past, meaning grace period has passed).
func genReadyPendingRemoval(providerType provider.ProviderType, accountID string) *rapid.Generator[*state.PendingRemoval] {
	return rapid.Custom(func(t *rapid.T) *state.PendingRemoval {
		// Expired time is in the past (4-10 days ago, past the 3-day grace period)
		daysAgo := rapid.IntRange(4, 10).Draw(t, "days_ago")
		expiredAt := time.Now().Add(-time.Duration(daysAgo) * 24 * time.Hour)
		// Scheduled time is 3 days after expiration (also in the past)
		scheduledAt := expiredAt.Add(3 * 24 * time.Hour)

		return &state.PendingRemoval{
			AccountID:     accountID,
			Email:         genRemovalEmail().Draw(t, "email"),
			PermissionSet: genRemovalPermissionSet().Draw(t, "permission_set"),
			CloudType:     string(providerType),
			ExpiredAt:     expiredAt,
			ScheduledAt:   scheduledAt,
			CreatedAt:     expiredAt,
		}
	})
}

// genNotReadyPendingRemoval generates a pending removal that is NOT ready for processing
// (scheduled time is in the future, meaning grace period has not passed).
func genNotReadyPendingRemoval(providerType provider.ProviderType, accountID string) *rapid.Generator[*state.PendingRemoval] {
	return rapid.Custom(func(t *rapid.T) *state.PendingRemoval {
		// Expired time is recent (1-2 days ago, within the 3-day grace period)
		daysAgo := rapid.IntRange(1, 2).Draw(t, "days_ago")
		expiredAt := time.Now().Add(-time.Duration(daysAgo) * 24 * time.Hour)
		// Scheduled time is 3 days after expiration (still in the future)
		scheduledAt := expiredAt.Add(3 * 24 * time.Hour)

		return &state.PendingRemoval{
			AccountID:     accountID,
			Email:         genRemovalEmail().Draw(t, "email"),
			PermissionSet: genRemovalPermissionSet().Draw(t, "permission_set"),
			CloudType:     string(providerType),
			ExpiredAt:     expiredAt,
			ScheduledAt:   scheduledAt,
			CreatedAt:     expiredAt,
		}
	})
}

// =============================================================================
// Removal Integration Test Setup Helpers
// =============================================================================

// removalIntegrationTestSetup holds all components needed for removal integration testing.
type removalIntegrationTestSetup struct {
	manager    *Manager
	lifecycle  *Lifecycle
	registry   *provider.Registry
	stateStore *removalMockStateStore
	providers  map[provider.ProviderType]*removalMockProvider
	config     *config.Config
	logger     *logger.Logger
}

// createRemovalIntegrationTestSetup creates a complete test setup for removal testing.
func createRemovalIntegrationTestSetup(accounts map[provider.ProviderType]string) (*removalIntegrationTestSetup, error) {
	// Create config with all accounts
	cfg := &config.Config{
		Feishu: config.FeishuConfig{
			ChatID: "test-chat-id",
		},
		Providers: config.ProvidersConfig{},
		Runtime: config.RuntimeConfig{
			GracePeriodDays:     3,
			HTTPTimeout:         10 * time.Second,
			MaxRetries:          3,
			ExpireCheckInterval: 1 * time.Hour,
		},
	}

	// Add accounts to config based on provider type
	for providerType, accountID := range accounts {
		account := config.CloudAccount{ID: accountID, Name: "test-" + accountID, Type: "prod"}
		switch providerType {
		case provider.ProviderAWS:
			cfg.Providers.AWS.Accounts = append(cfg.Providers.AWS.Accounts, account)
		case provider.ProviderGCP:
			cfg.Providers.GCP.Projects = append(cfg.Providers.GCP.Projects, account)
		case provider.ProviderAzure:
			cfg.Providers.Azure.Subscriptions = append(cfg.Providers.Azure.Subscriptions, account)
		case provider.ProviderAlibaba:
			cfg.Providers.Alibaba.Accounts = append(cfg.Providers.Alibaba.Accounts, account)
		}
	}

	// Create registry
	registry, err := provider.NewRegistry(cfg)
	if err != nil {
		return nil, err
	}

	// Create and register mock providers
	providers := make(map[provider.ProviderType]*removalMockProvider)
	for providerType := range accounts {
		mockProv := newRemovalMockProvider(string(providerType))
		providers[providerType] = mockProv
		if err := registry.RegisterProvider(providerType, mockProv); err != nil {
			return nil, err
		}
	}

	// Create logger
	log := logger.NewLogger(logger.Config{Level: logger.ErrorLevel})

	// Create state store
	stateStore := newRemovalMockStateStore()

	// Create manager without notification service (nil feishu client)
	manager := NewManager(registry, nil, stateStore, cfg, log)

	// Create lifecycle manager without notification service
	lifecycle := NewLifecycle(manager, nil, stateStore, cfg, log)

	return &removalIntegrationTestSetup{
		manager:    manager,
		lifecycle:  lifecycle,
		registry:   registry,
		stateStore: stateStore,
		providers:  providers,
		config:     cfg,
		logger:     log,
	}, nil
}

// =============================================================================
// Property 19: 权限自动回收 - Integration Tests
// =============================================================================

// TestRemovalIntegration_PermissionPastGracePeriodIsRevoked tests that for any
// permission past the grace period, the system automatically revokes it.
//
// Feature: autosso-refactoring, Property 19: 权限自动回收
// **Validates: Requirements 10.3**
func TestRemovalIntegration_PermissionPastGracePeriodIsRevoked(t *testing.T) {
	rapid.Check(t, func(rt *rapid.T) {
		// Generate random provider type and account ID
		providerType := genRemovalProviderType().Draw(rt, "provider_type")
		accountID := genRemovalAccountID(providerType).Draw(rt, "account_id")

		// Create test setup
		accounts := map[provider.ProviderType]string{providerType: accountID}
		setup, err := createRemovalIntegrationTestSetup(accounts)
		if err != nil {
			rt.Fatalf("Failed to create test setup: %v", err)
		}

		ctx := context.Background()

		// Generate a pending removal that is ready (past grace period)
		readyRemoval := genReadyPendingRemoval(providerType, accountID).Draw(rt, "ready_removal")

		// Save the pending removal to state store
		if err := setup.stateStore.SavePendingRemoval(ctx, readyRemoval); err != nil {
			rt.Fatalf("Failed to save pending removal: %v", err)
		}

		// Verify pending removal exists before processing
		initialCount := setup.stateStore.getPendingRemovalsCount()
		if initialCount != 1 {
			rt.Fatalf("Expected 1 pending removal before processing, got %d", initialCount)
		}

		// Process pending removals
		processed, errs := setup.manager.ProcessPendingRemovals(ctx)
		if len(errs) > 0 {
			rt.Fatalf("ProcessPendingRemovals returned errors: %v", errs)
		}

		// Verify: Permission was revoked (processed count should be 1)
		if processed != 1 {
			rt.Fatalf("Expected 1 processed removal, got %d", processed)
		}

		// Verify: Provider's RemovePermission was called
		mockProv := setup.providers[providerType]
		calls := mockProv.getRemovePermissionCalls()
		if len(calls) != 1 {
			rt.Fatalf("Expected 1 RemovePermission call, got %d", len(calls))
		}

		// Verify: Call has correct parameters
		call := calls[0]
		if call.AccountID != accountID {
			rt.Fatalf("AccountID mismatch: expected %q, got %q", accountID, call.AccountID)
		}
		if call.UserEmail != readyRemoval.Email {
			rt.Fatalf("UserEmail mismatch: expected %q, got %q", readyRemoval.Email, call.UserEmail)
		}
		if len(call.PermissionSets) != 1 || call.PermissionSets[0] != readyRemoval.PermissionSet {
			rt.Fatalf("PermissionSet mismatch: expected %q, got %v", readyRemoval.PermissionSet, call.PermissionSets)
		}
	})
}

// TestRemovalIntegration_CorrectProviderRemovePermissionCalled tests that the
// correct provider's RemovePermission method is called based on the cloud type.
//
// Feature: autosso-refactoring, Property 19: 权限自动回收
// **Validates: Requirements 10.3**
func TestRemovalIntegration_CorrectProviderRemovePermissionCalled(t *testing.T) {
	allProviderTypes := []provider.ProviderType{
		provider.ProviderAWS,
		provider.ProviderGCP,
		provider.ProviderAzure,
		provider.ProviderAlibaba,
	}

	for _, targetProviderType := range allProviderTypes {
		t.Run(string(targetProviderType), func(t *testing.T) {
			rapid.Check(t, func(rt *rapid.T) {
				// Generate account IDs for all provider types
				accounts := make(map[provider.ProviderType]string)
				for _, pt := range allProviderTypes {
					accounts[pt] = genRemovalAccountID(pt).Draw(rt, "account_"+string(pt))
				}

				// Create test setup with all providers
				setup, err := createRemovalIntegrationTestSetup(accounts)
				if err != nil {
					rt.Fatalf("Failed to create test setup: %v", err)
				}

				ctx := context.Background()

				// Generate a pending removal for the target provider type
				targetAccountID := accounts[targetProviderType]
				readyRemoval := genReadyPendingRemoval(targetProviderType, targetAccountID).Draw(rt, "ready_removal")

				// Save the pending removal
				if err := setup.stateStore.SavePendingRemoval(ctx, readyRemoval); err != nil {
					rt.Fatalf("Failed to save pending removal: %v", err)
				}

				// Process pending removals
				processed, errs := setup.manager.ProcessPendingRemovals(ctx)
				if len(errs) > 0 {
					rt.Fatalf("ProcessPendingRemovals returned errors: %v", errs)
				}
				if processed != 1 {
					rt.Fatalf("Expected 1 processed removal, got %d", processed)
				}

				// Verify: Only the target provider received RemovePermission call
				for pt, mockProv := range setup.providers {
					calls := mockProv.getRemovePermissionCalls()
					if pt == targetProviderType {
						if len(calls) != 1 {
							rt.Fatalf("Expected 1 call to %s provider, got %d", pt, len(calls))
						}
					} else {
						if len(calls) > 0 {
							rt.Fatalf("Unexpected calls to %s provider (expected only %s)", pt, targetProviderType)
						}
					}
				}
			})
		})
	}
}

// TestRemovalIntegration_PendingRemovalDeletedAfterSuccessfulRevocation tests that
// pending removal records are deleted from the state store after successful revocation.
//
// Feature: autosso-refactoring, Property 19: 权限自动回收
// **Validates: Requirements 10.3**
func TestRemovalIntegration_PendingRemovalDeletedAfterSuccessfulRevocation(t *testing.T) {
	rapid.Check(t, func(rt *rapid.T) {
		// Generate random provider type and account ID
		providerType := genRemovalProviderType().Draw(rt, "provider_type")
		accountID := genRemovalAccountID(providerType).Draw(rt, "account_id")

		// Create test setup
		accounts := map[provider.ProviderType]string{providerType: accountID}
		setup, err := createRemovalIntegrationTestSetup(accounts)
		if err != nil {
			rt.Fatalf("Failed to create test setup: %v", err)
		}

		ctx := context.Background()

		// Generate a pending removal that is ready
		readyRemoval := genReadyPendingRemoval(providerType, accountID).Draw(rt, "ready_removal")

		// Save the pending removal
		if err := setup.stateStore.SavePendingRemoval(ctx, readyRemoval); err != nil {
			rt.Fatalf("Failed to save pending removal: %v", err)
		}

		// Verify pending removal exists before processing
		if !setup.stateStore.hasPendingRemovalFor(accountID, readyRemoval.Email, readyRemoval.PermissionSet) {
			rt.Fatal("Pending removal should exist before processing")
		}

		// Process pending removals
		processed, errs := setup.manager.ProcessPendingRemovals(ctx)
		if len(errs) > 0 {
			rt.Fatalf("ProcessPendingRemovals returned errors: %v", errs)
		}
		if processed != 1 {
			rt.Fatalf("Expected 1 processed removal, got %d", processed)
		}

		// Verify: Pending removal was deleted after successful revocation
		if setup.stateStore.hasPendingRemovalFor(accountID, readyRemoval.Email, readyRemoval.PermissionSet) {
			rt.Fatal("Pending removal should be deleted after successful revocation")
		}

		// Verify: Total pending removals count is 0
		finalCount := setup.stateStore.getPendingRemovalsCount()
		if finalCount != 0 {
			rt.Fatalf("Expected 0 pending removals after processing, got %d", finalCount)
		}
	})
}

// TestRemovalIntegration_ErrorDuringRevocationIsHandled tests that errors during
// revocation are properly handled and returned.
//
// Feature: autosso-refactoring, Property 19: 权限自动回收
// **Validates: Requirements 10.3**
func TestRemovalIntegration_ErrorDuringRevocationIsHandled(t *testing.T) {
	rapid.Check(t, func(rt *rapid.T) {
		// Generate random provider type and account ID
		providerType := genRemovalProviderType().Draw(rt, "provider_type")
		accountID := genRemovalAccountID(providerType).Draw(rt, "account_id")

		// Create test setup
		accounts := map[provider.ProviderType]string{providerType: accountID}
		setup, err := createRemovalIntegrationTestSetup(accounts)
		if err != nil {
			rt.Fatalf("Failed to create test setup: %v", err)
		}

		ctx := context.Background()

		// Configure provider to fail
		mockProv := setup.providers[providerType]
		mockProv.shouldFail = true
		mockProv.failureError = errors.New("simulated provider failure")

		// Generate a pending removal that is ready
		readyRemoval := genReadyPendingRemoval(providerType, accountID).Draw(rt, "ready_removal")

		// Save the pending removal
		if err := setup.stateStore.SavePendingRemoval(ctx, readyRemoval); err != nil {
			rt.Fatalf("Failed to save pending removal: %v", err)
		}

		// Process pending removals
		processed, errs := setup.manager.ProcessPendingRemovals(ctx)

		// Verify: Error was returned
		if len(errs) == 0 {
			rt.Fatal("Expected errors when provider fails, got none")
		}

		// Verify: No removals were processed successfully
		if processed != 0 {
			rt.Fatalf("Expected 0 processed removals when provider fails, got %d", processed)
		}

		// Verify: Provider was still called (error happened during execution)
		calls := mockProv.getRemovePermissionCalls()
		if len(calls) == 0 {
			rt.Fatal("Expected provider to be called even when it fails")
		}

		// Verify: Pending removal still exists (not deleted due to failure)
		if !setup.stateStore.hasPendingRemovalFor(accountID, readyRemoval.Email, readyRemoval.PermissionSet) {
			rt.Fatal("Pending removal should still exist after failed revocation")
		}
	})
}

// TestRemovalIntegration_NotReadyRemovalsAreSkipped tests that pending removals
// that are not yet ready (still within grace period) are not processed.
//
// Feature: autosso-refactoring, Property 19: 权限自动回收
// **Validates: Requirements 10.3**
func TestRemovalIntegration_NotReadyRemovalsAreSkipped(t *testing.T) {
	rapid.Check(t, func(rt *rapid.T) {
		// Generate random provider type and account ID
		providerType := genRemovalProviderType().Draw(rt, "provider_type")
		accountID := genRemovalAccountID(providerType).Draw(rt, "account_id")

		// Create test setup
		accounts := map[provider.ProviderType]string{providerType: accountID}
		setup, err := createRemovalIntegrationTestSetup(accounts)
		if err != nil {
			rt.Fatalf("Failed to create test setup: %v", err)
		}

		ctx := context.Background()

		// Generate a pending removal that is NOT ready (within grace period)
		notReadyRemoval := genNotReadyPendingRemoval(providerType, accountID).Draw(rt, "not_ready_removal")

		// Save the pending removal
		if err := setup.stateStore.SavePendingRemoval(ctx, notReadyRemoval); err != nil {
			rt.Fatalf("Failed to save pending removal: %v", err)
		}

		// Verify pending removal exists before processing
		initialCount := setup.stateStore.getPendingRemovalsCount()
		if initialCount != 1 {
			rt.Fatalf("Expected 1 pending removal before processing, got %d", initialCount)
		}

		// Process pending removals
		processed, errs := setup.manager.ProcessPendingRemovals(ctx)
		if len(errs) > 0 {
			rt.Fatalf("ProcessPendingRemovals returned unexpected errors: %v", errs)
		}

		// Verify: No removals were processed (not ready yet)
		if processed != 0 {
			rt.Fatalf("Expected 0 processed removals for not-ready removal, got %d", processed)
		}

		// Verify: Provider's RemovePermission was NOT called
		mockProv := setup.providers[providerType]
		calls := mockProv.getRemovePermissionCalls()
		if len(calls) != 0 {
			rt.Fatalf("Expected 0 RemovePermission calls for not-ready removal, got %d", len(calls))
		}

		// Verify: Pending removal still exists
		if !setup.stateStore.hasPendingRemovalFor(accountID, notReadyRemoval.Email, notReadyRemoval.PermissionSet) {
			rt.Fatal("Pending removal should still exist when not ready")
		}
	})
}

// TestRemovalIntegration_MultipleReadyRemovalsProcessed tests that multiple
// pending removals that are ready are all processed correctly.
//
// Feature: autosso-refactoring, Property 19: 权限自动回收
// **Validates: Requirements 10.3**
func TestRemovalIntegration_MultipleReadyRemovalsProcessed(t *testing.T) {
	rapid.Check(t, func(rt *rapid.T) {
		// Generate random provider type and account ID
		providerType := genRemovalProviderType().Draw(rt, "provider_type")
		accountID := genRemovalAccountID(providerType).Draw(rt, "account_id")

		// Create test setup
		accounts := map[provider.ProviderType]string{providerType: accountID}
		setup, err := createRemovalIntegrationTestSetup(accounts)
		if err != nil {
			rt.Fatalf("Failed to create test setup: %v", err)
		}

		ctx := context.Background()

		// Generate multiple ready pending removals
		numRemovals := rapid.IntRange(2, 5).Draw(rt, "num_removals")
		for i := 0; i < numRemovals; i++ {
			readyRemoval := genReadyPendingRemoval(providerType, accountID).Draw(rt, "ready_removal")
			if err := setup.stateStore.SavePendingRemoval(ctx, readyRemoval); err != nil {
				rt.Fatalf("Failed to save pending removal %d: %v", i, err)
			}
		}

		// Verify pending removals exist before processing
		initialCount := setup.stateStore.getPendingRemovalsCount()
		if initialCount != numRemovals {
			rt.Fatalf("Expected %d pending removals before processing, got %d", numRemovals, initialCount)
		}

		// Process pending removals
		processed, errs := setup.manager.ProcessPendingRemovals(ctx)
		if len(errs) > 0 {
			rt.Fatalf("ProcessPendingRemovals returned errors: %v", errs)
		}

		// Verify: All removals were processed
		if processed != numRemovals {
			rt.Fatalf("Expected %d processed removals, got %d", numRemovals, processed)
		}

		// Verify: Provider's RemovePermission was called for each removal
		mockProv := setup.providers[providerType]
		calls := mockProv.getRemovePermissionCalls()
		if len(calls) != numRemovals {
			rt.Fatalf("Expected %d RemovePermission calls, got %d", numRemovals, len(calls))
		}

		// Verify: All pending removals were deleted
		finalCount := setup.stateStore.getPendingRemovalsCount()
		if finalCount != 0 {
			rt.Fatalf("Expected 0 pending removals after processing, got %d", finalCount)
		}
	})
}

// TestRemovalIntegration_MixedReadyAndNotReadyRemovals tests that when there are
// both ready and not-ready pending removals, only the ready ones are processed.
//
// Feature: autosso-refactoring, Property 19: 权限自动回收
// **Validates: Requirements 10.3**
func TestRemovalIntegration_MixedReadyAndNotReadyRemovals(t *testing.T) {
	rapid.Check(t, func(rt *rapid.T) {
		// Generate random provider type and account ID
		providerType := genRemovalProviderType().Draw(rt, "provider_type")
		accountID := genRemovalAccountID(providerType).Draw(rt, "account_id")

		// Create test setup
		accounts := map[provider.ProviderType]string{providerType: accountID}
		setup, err := createRemovalIntegrationTestSetup(accounts)
		if err != nil {
			rt.Fatalf("Failed to create test setup: %v", err)
		}

		ctx := context.Background()

		// Generate ready and not-ready pending removals
		numReady := rapid.IntRange(1, 3).Draw(rt, "num_ready")
		numNotReady := rapid.IntRange(1, 3).Draw(rt, "num_not_ready")

		for i := 0; i < numReady; i++ {
			readyRemoval := genReadyPendingRemoval(providerType, accountID).Draw(rt, "ready_removal")
			if err := setup.stateStore.SavePendingRemoval(ctx, readyRemoval); err != nil {
				rt.Fatalf("Failed to save ready removal %d: %v", i, err)
			}
		}

		for i := 0; i < numNotReady; i++ {
			notReadyRemoval := genNotReadyPendingRemoval(providerType, accountID).Draw(rt, "not_ready_removal")
			if err := setup.stateStore.SavePendingRemoval(ctx, notReadyRemoval); err != nil {
				rt.Fatalf("Failed to save not-ready removal %d: %v", i, err)
			}
		}

		// Verify total pending removals before processing
		initialCount := setup.stateStore.getPendingRemovalsCount()
		expectedTotal := numReady + numNotReady
		if initialCount != expectedTotal {
			rt.Fatalf("Expected %d pending removals before processing, got %d", expectedTotal, initialCount)
		}

		// Process pending removals
		processed, errs := setup.manager.ProcessPendingRemovals(ctx)
		if len(errs) > 0 {
			rt.Fatalf("ProcessPendingRemovals returned errors: %v", errs)
		}

		// Verify: Only ready removals were processed
		if processed != numReady {
			rt.Fatalf("Expected %d processed removals (ready only), got %d", numReady, processed)
		}

		// Verify: Provider's RemovePermission was called only for ready removals
		mockProv := setup.providers[providerType]
		calls := mockProv.getRemovePermissionCalls()
		if len(calls) != numReady {
			rt.Fatalf("Expected %d RemovePermission calls, got %d", numReady, len(calls))
		}

		// Verify: Not-ready removals still exist
		finalCount := setup.stateStore.getPendingRemovalsCount()
		if finalCount != numNotReady {
			rt.Fatalf("Expected %d pending removals after processing (not-ready only), got %d", numNotReady, finalCount)
		}
	})
}

// TestRemovalIntegration_AllCloudPlatformsRevocation tests that permission
// revocation works correctly for all supported cloud platforms.
//
// Feature: autosso-refactoring, Property 19: 权限自动回收
// **Validates: Requirements 10.3**
func TestRemovalIntegration_AllCloudPlatformsRevocation(t *testing.T) {
	allProviderTypes := []provider.ProviderType{
		provider.ProviderAWS,
		provider.ProviderGCP,
		provider.ProviderAzure,
		provider.ProviderAlibaba,
	}

	rapid.Check(t, func(rt *rapid.T) {
		// Generate account IDs for all provider types
		accounts := make(map[provider.ProviderType]string)
		for _, pt := range allProviderTypes {
			accounts[pt] = genRemovalAccountID(pt).Draw(rt, "account_"+string(pt))
		}

		// Create test setup with all providers
		setup, err := createRemovalIntegrationTestSetup(accounts)
		if err != nil {
			rt.Fatalf("Failed to create test setup: %v", err)
		}

		ctx := context.Background()

		// Create ready pending removals for each cloud platform
		for _, providerType := range allProviderTypes {
			accountID := accounts[providerType]
			readyRemoval := genReadyPendingRemoval(providerType, accountID).Draw(rt, "ready_"+string(providerType))
			if err := setup.stateStore.SavePendingRemoval(ctx, readyRemoval); err != nil {
				rt.Fatalf("Failed to save pending removal for %s: %v", providerType, err)
			}
		}

		// Verify total pending removals before processing
		initialCount := setup.stateStore.getPendingRemovalsCount()
		if initialCount != len(allProviderTypes) {
			rt.Fatalf("Expected %d pending removals before processing, got %d", len(allProviderTypes), initialCount)
		}

		// Process pending removals
		processed, errs := setup.manager.ProcessPendingRemovals(ctx)
		if len(errs) > 0 {
			rt.Fatalf("ProcessPendingRemovals returned errors: %v", errs)
		}

		// Verify: All removals were processed
		if processed != len(allProviderTypes) {
			rt.Fatalf("Expected %d processed removals, got %d", len(allProviderTypes), processed)
		}

		// Verify: Each provider received exactly one RemovePermission call
		for _, providerType := range allProviderTypes {
			mockProv := setup.providers[providerType]
			calls := mockProv.getRemovePermissionCalls()
			if len(calls) != 1 {
				rt.Fatalf("Expected 1 RemovePermission call for %s, got %d", providerType, len(calls))
			}
		}

		// Verify: All pending removals were deleted
		finalCount := setup.stateStore.getPendingRemovalsCount()
		if finalCount != 0 {
			rt.Fatalf("Expected 0 pending removals after processing, got %d", finalCount)
		}
	})
}

// TestRemovalIntegration_LifecycleProcessScheduledRemovals tests that the
// Lifecycle component correctly processes scheduled removals through the Manager.
//
// Feature: autosso-refactoring, Property 19: 权限自动回收
// **Validates: Requirements 10.3**
func TestRemovalIntegration_LifecycleProcessScheduledRemovals(t *testing.T) {
	rapid.Check(t, func(rt *rapid.T) {
		// Generate random provider type and account ID
		providerType := genRemovalProviderType().Draw(rt, "provider_type")
		accountID := genRemovalAccountID(providerType).Draw(rt, "account_id")

		// Create test setup
		accounts := map[provider.ProviderType]string{providerType: accountID}
		setup, err := createRemovalIntegrationTestSetup(accounts)
		if err != nil {
			rt.Fatalf("Failed to create test setup: %v", err)
		}

		ctx := context.Background()

		// Generate a ready pending removal
		readyRemoval := genReadyPendingRemoval(providerType, accountID).Draw(rt, "ready_removal")

		// Save the pending removal
		if err := setup.stateStore.SavePendingRemoval(ctx, readyRemoval); err != nil {
			rt.Fatalf("Failed to save pending removal: %v", err)
		}

		// Process scheduled removals through Lifecycle
		processed, errs := setup.lifecycle.ProcessScheduledRemovals(ctx)
		if len(errs) > 0 {
			rt.Fatalf("ProcessScheduledRemovals returned errors: %v", errs)
		}

		// Verify: Removal was processed
		if processed != 1 {
			rt.Fatalf("Expected 1 processed removal, got %d", processed)
		}

		// Verify: Provider's RemovePermission was called
		mockProv := setup.providers[providerType]
		calls := mockProv.getRemovePermissionCalls()
		if len(calls) != 1 {
			rt.Fatalf("Expected 1 RemovePermission call, got %d", len(calls))
		}

		// Verify: Pending removal was deleted
		finalCount := setup.stateStore.getPendingRemovalsCount()
		if finalCount != 0 {
			rt.Fatalf("Expected 0 pending removals after processing, got %d", finalCount)
		}
	})
}

// TestRemovalIntegration_RemovalContainsCorrectUserDetails tests that the
// removal operation uses the correct user email and permission details.
//
// Feature: autosso-refactoring, Property 19: 权限自动回收
// **Validates: Requirements 10.3**
func TestRemovalIntegration_RemovalContainsCorrectUserDetails(t *testing.T) {
	rapid.Check(t, func(rt *rapid.T) {
		// Generate random provider type and account ID
		providerType := genRemovalProviderType().Draw(rt, "provider_type")
		accountID := genRemovalAccountID(providerType).Draw(rt, "account_id")

		// Create test setup
		accounts := map[provider.ProviderType]string{providerType: accountID}
		setup, err := createRemovalIntegrationTestSetup(accounts)
		if err != nil {
			rt.Fatalf("Failed to create test setup: %v", err)
		}

		ctx := context.Background()

		// Generate specific user details
		email := genRemovalEmail().Draw(rt, "email")
		permissionSet := genRemovalPermissionSet().Draw(rt, "permission_set")

		// Create a ready pending removal with specific details
		daysAgo := rapid.IntRange(4, 10).Draw(rt, "days_ago")
		expiredAt := time.Now().Add(-time.Duration(daysAgo) * 24 * time.Hour)
		scheduledAt := expiredAt.Add(3 * 24 * time.Hour)

		readyRemoval := &state.PendingRemoval{
			AccountID:     accountID,
			Email:         email,
			PermissionSet: permissionSet,
			CloudType:     string(providerType),
			ExpiredAt:     expiredAt,
			ScheduledAt:   scheduledAt,
			CreatedAt:     expiredAt,
		}

		// Save the pending removal
		if err := setup.stateStore.SavePendingRemoval(ctx, readyRemoval); err != nil {
			rt.Fatalf("Failed to save pending removal: %v", err)
		}

		// Process pending removals
		processed, errs := setup.manager.ProcessPendingRemovals(ctx)
		if len(errs) > 0 {
			rt.Fatalf("ProcessPendingRemovals returned errors: %v", errs)
		}
		if processed != 1 {
			rt.Fatalf("Expected 1 processed removal, got %d", processed)
		}

		// Verify: Provider received correct user details
		mockProv := setup.providers[providerType]
		calls := mockProv.getRemovePermissionCalls()
		if len(calls) != 1 {
			rt.Fatalf("Expected 1 RemovePermission call, got %d", len(calls))
		}

		call := calls[0]
		if call.UserEmail != email {
			rt.Fatalf("UserEmail mismatch: expected %q, got %q", email, call.UserEmail)
		}
		if call.AccountID != accountID {
			rt.Fatalf("AccountID mismatch: expected %q, got %q", accountID, call.AccountID)
		}
		if len(call.PermissionSets) != 1 || call.PermissionSets[0] != permissionSet {
			rt.Fatalf("PermissionSet mismatch: expected [%q], got %v", permissionSet, call.PermissionSets)
		}
	})
}

// TestRemovalIntegration_PartialFailureHandling tests that when some removals
// fail, the successful ones are still processed and recorded.
//
// Feature: autosso-refactoring, Property 19: 权限自动回收
// **Validates: Requirements 10.3**
func TestRemovalIntegration_PartialFailureHandling(t *testing.T) {
	// This test requires a custom setup where we can control which removals fail
	// We'll use two different provider types - one that succeeds and one that fails

	rapid.Check(t, func(rt *rapid.T) {
		// Use AWS (will succeed) and GCP (will fail)
		awsAccountID := genRemovalAccountID(provider.ProviderAWS).Draw(rt, "aws_account")
		gcpAccountID := genRemovalAccountID(provider.ProviderGCP).Draw(rt, "gcp_account")

		accounts := map[provider.ProviderType]string{
			provider.ProviderAWS: awsAccountID,
			provider.ProviderGCP: gcpAccountID,
		}

		setup, err := createRemovalIntegrationTestSetup(accounts)
		if err != nil {
			rt.Fatalf("Failed to create test setup: %v", err)
		}

		ctx := context.Background()

		// Configure GCP provider to fail
		gcpProvider := setup.providers[provider.ProviderGCP]
		gcpProvider.shouldFail = true
		gcpProvider.failureError = errors.New("simulated GCP failure")

		// Create ready pending removals for both providers
		awsRemoval := genReadyPendingRemoval(provider.ProviderAWS, awsAccountID).Draw(rt, "aws_removal")
		gcpRemoval := genReadyPendingRemoval(provider.ProviderGCP, gcpAccountID).Draw(rt, "gcp_removal")

		if err := setup.stateStore.SavePendingRemoval(ctx, awsRemoval); err != nil {
			rt.Fatalf("Failed to save AWS removal: %v", err)
		}
		if err := setup.stateStore.SavePendingRemoval(ctx, gcpRemoval); err != nil {
			rt.Fatalf("Failed to save GCP removal: %v", err)
		}

		// Verify 2 pending removals before processing
		initialCount := setup.stateStore.getPendingRemovalsCount()
		if initialCount != 2 {
			rt.Fatalf("Expected 2 pending removals before processing, got %d", initialCount)
		}

		// Process pending removals
		processed, errs := setup.manager.ProcessPendingRemovals(ctx)

		// Verify: One error (GCP failure)
		if len(errs) != 1 {
			rt.Fatalf("Expected 1 error, got %d", len(errs))
		}

		// Verify: One successful removal (AWS)
		if processed != 1 {
			rt.Fatalf("Expected 1 processed removal, got %d", processed)
		}

		// Verify: AWS provider was called and succeeded
		awsProvider := setup.providers[provider.ProviderAWS]
		awsCalls := awsProvider.getRemovePermissionCalls()
		if len(awsCalls) != 1 {
			rt.Fatalf("Expected 1 AWS RemovePermission call, got %d", len(awsCalls))
		}

		// Verify: GCP provider was called but failed
		gcpCalls := gcpProvider.getRemovePermissionCalls()
		if len(gcpCalls) != 1 {
			rt.Fatalf("Expected 1 GCP RemovePermission call, got %d", len(gcpCalls))
		}

		// Verify: AWS removal was deleted, GCP removal still exists
		finalCount := setup.stateStore.getPendingRemovalsCount()
		if finalCount != 1 {
			rt.Fatalf("Expected 1 pending removal after processing (GCP only), got %d", finalCount)
		}

		// Verify: The remaining removal is the GCP one
		if !setup.stateStore.hasPendingRemovalFor(gcpAccountID, gcpRemoval.Email, gcpRemoval.PermissionSet) {
			rt.Fatal("GCP pending removal should still exist after failure")
		}
	})
}

// TestRemovalIntegration_ContextCancellation tests that context cancellation
// is properly handled during removal processing.
//
// Feature: autosso-refactoring, Property 19: 权限自动回收
// **Validates: Requirements 10.3**
func TestRemovalIntegration_ContextCancellation(t *testing.T) {
	rapid.Check(t, func(rt *rapid.T) {
		// Generate random provider type and account ID
		providerType := genRemovalProviderType().Draw(rt, "provider_type")
		accountID := genRemovalAccountID(providerType).Draw(rt, "account_id")

		// Create test setup
		accounts := map[provider.ProviderType]string{providerType: accountID}
		setup, err := createRemovalIntegrationTestSetup(accounts)
		if err != nil {
			rt.Fatalf("Failed to create test setup: %v", err)
		}

		// Create a cancelled context
		ctx, cancel := context.WithCancel(context.Background())
		cancel() // Cancel immediately

		// Generate a ready pending removal
		readyRemoval := genReadyPendingRemoval(providerType, accountID).Draw(rt, "ready_removal")

		// Save the pending removal
		if err := setup.stateStore.SavePendingRemoval(context.Background(), readyRemoval); err != nil {
			rt.Fatalf("Failed to save pending removal: %v", err)
		}

		// Process pending removals with cancelled context
		// Note: The behavior depends on when the context is checked
		// The GetPendingRemovals call may succeed before context is checked
		_, _ = setup.manager.ProcessPendingRemovals(ctx)

		// The test passes as long as no panic occurs
		// Context cancellation handling is implementation-dependent
	})
}

// TestRemovalIntegration_EmptyPendingRemovals tests that processing with no
// pending removals completes successfully without errors.
//
// Feature: autosso-refactoring, Property 19: 权限自动回收
// **Validates: Requirements 10.3**
func TestRemovalIntegration_EmptyPendingRemovals(t *testing.T) {
	rapid.Check(t, func(rt *rapid.T) {
		// Generate random provider type and account ID
		providerType := genRemovalProviderType().Draw(rt, "provider_type")
		accountID := genRemovalAccountID(providerType).Draw(rt, "account_id")

		// Create test setup
		accounts := map[provider.ProviderType]string{providerType: accountID}
		setup, err := createRemovalIntegrationTestSetup(accounts)
		if err != nil {
			rt.Fatalf("Failed to create test setup: %v", err)
		}

		ctx := context.Background()

		// Don't add any pending removals

		// Process pending removals
		processed, errs := setup.manager.ProcessPendingRemovals(ctx)

		// Verify: No errors
		if len(errs) > 0 {
			rt.Fatalf("Expected no errors for empty pending removals, got %v", errs)
		}

		// Verify: No removals processed
		if processed != 0 {
			rt.Fatalf("Expected 0 processed removals, got %d", processed)
		}

		// Verify: No provider calls
		mockProv := setup.providers[providerType]
		calls := mockProv.getRemovePermissionCalls()
		if len(calls) != 0 {
			rt.Fatalf("Expected 0 RemovePermission calls, got %d", len(calls))
		}
	})
}
