// Package permission provides the core permission management logic for AutoSSO.
// This file contains unit tests for the Monitor component.
package permission

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"net/http/httptest"
	"strings"
	"sync"
	"testing"
	"time"

	"AutoSSO/internal/config"
	"AutoSSO/internal/feishu"
	"AutoSSO/internal/pkg/httpclient"
	"AutoSSO/internal/pkg/logger"
	"AutoSSO/internal/state"

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

// mockMonitorStateStore is a mock implementation of the state store for monitor testing.
type mockMonitorStateStore struct {
	mu                sync.RWMutex
	instances         map[string]*state.ApprovalInstance
	pendingRemovals   []*state.PendingRemoval
	deletedInstances  map[string]bool
	saveInstanceErr   error
	getInstanceErr    error
	listInstancesErr  error
	deleteInstanceErr error
	savePendingErr    error
	getPendingErr     error
	deletePendingErr  error
	saveDeletedErr    error
	isDeletedErr      error
}

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

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

func (m *mockMonitorStateStore) GetInstance(ctx context.Context, instanceID string) (*state.ApprovalInstance, error) {
	if m.getInstanceErr != nil {
		return nil, m.getInstanceErr
	}
	m.mu.RLock()
	defer m.mu.RUnlock()
	return m.instances[instanceID], nil
}

func (m *mockMonitorStateStore) ListInstances(ctx context.Context, filter *state.InstanceFilter) ([]*state.ApprovalInstance, error) {
	if m.listInstancesErr != nil {
		return nil, m.listInstancesErr
	}
	m.mu.RLock()
	defer m.mu.RUnlock()

	result := make([]*state.ApprovalInstance, 0)
	for _, inst := range m.instances {
		if filter != nil && filter.Status != "" && inst.Status != filter.Status {
			continue
		}
		result = append(result, inst)
	}
	return result, nil
}

func (m *mockMonitorStateStore) DeleteInstance(ctx context.Context, instanceID string) error {
	if m.deleteInstanceErr != nil {
		return m.deleteInstanceErr
	}
	m.mu.Lock()
	defer m.mu.Unlock()
	delete(m.instances, instanceID)
	return nil
}

func (m *mockMonitorStateStore) SavePendingRemoval(ctx context.Context, removal *state.PendingRemoval) error {
	if m.savePendingErr != nil {
		return m.savePendingErr
	}
	m.mu.Lock()
	defer m.mu.Unlock()
	removal.ID = int64(len(m.pendingRemovals) + 1)
	m.pendingRemovals = append(m.pendingRemovals, removal)
	return nil
}

func (m *mockMonitorStateStore) GetPendingRemovals(ctx context.Context) ([]*state.PendingRemoval, error) {
	if m.getPendingErr != nil {
		return nil, m.getPendingErr
	}
	m.mu.RLock()
	defer m.mu.RUnlock()
	return m.pendingRemovals, nil
}

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

func (m *mockMonitorStateStore) SaveDeletedInstance(ctx context.Context, instanceID string) error {
	if m.saveDeletedErr != nil {
		return m.saveDeletedErr
	}
	m.mu.Lock()
	defer m.mu.Unlock()
	m.deletedInstances[instanceID] = true
	return nil
}

func (m *mockMonitorStateStore) IsInstanceDeleted(ctx context.Context, instanceID string) (bool, error) {
	if m.isDeletedErr != nil {
		return false, m.isDeletedErr
	}
	m.mu.RLock()
	defer m.mu.RUnlock()
	return m.deletedInstances[instanceID], nil
}

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

// mockHTTPClient is a mock HTTP client for testing.
type mockHTTPClient struct{}

// createMonitorTestConfig creates a test configuration for monitor tests.
func createMonitorTestConfig() *config.Config {
	return &config.Config{
		Feishu: config.FeishuConfig{
			AppID:        "test-app-id",
			AppSecret:    "test-app-secret",
			ApprovalCode: "test-approval-code",
			ChatID:       "test-chat-id",
			BaseURL:      "https://open.feishu.cn",
		},
		Runtime: config.RuntimeConfig{
			PollInterval:        100 * time.Millisecond,
			ExpireCheckInterval: 100 * time.Millisecond,
			GracePeriodDays:     3,
			HTTPTimeout:         10 * time.Second,
			MaxRetries:          3,
			LogLevel:            "debug",
		},
	}
}

// createMonitorTestLogger creates a test logger for monitor tests.
func createMonitorTestLogger() *logger.Logger {
	return logger.NewLogger(logger.Config{
		Level:       logger.DebugLevel,
		PrettyPrint: false,
	})
}

// createMockApprovalResponse creates a mock Feishu approval response.
func createMockApprovalResponse(email, accountID, cloudType string, permissionSets []string, isApproved bool, startTime, endTime time.Time) []byte {
	formFields := []map[string]interface{}{
		{
			"id":    "email",
			"name":  "申请人邮箱",
			"type":  "input",
			"value": email,
		},
		{
			"id":    "app_type",
			"name":  "申请人选择申请类型",
			"type":  "radioV2",
			"value": "生产账号申请 SSO",
		},
		{
			"id":    "account",
			"name":  "生产账号选择",
			"type":  "radioV2",
			"value": fmt.Sprintf("Account (#%s)", accountID),
		},
		{
			"id":    "permissions",
			"name":  "选择生产 SSO Role",
			"type":  "checkboxV2",
			"value": permissionSets,
		},
		{
			"id":   "date_range",
			"name": "申请时间范围",
			"type": "dateInterval",
			"value": map[string]string{
				"start": startTime.Format(time.RFC3339),
				"end":   endTime.Format(time.RFC3339),
			},
		},
	}

	formJSON, _ := json.Marshal(formFields)

	status := "PENDING"
	if isApproved {
		status = "APPROVED"
	}

	response := map[string]interface{}{
		"code": 0,
		"msg":  "success",
		"data": map[string]interface{}{
			"instance_code": "test-instance-001",
			"status":        status,
			"form":          string(formJSON),
		},
	}

	responseJSON, _ := json.Marshal(response)
	return responseJSON
}

func createMockInvalidApprovalResponse(email, accountID string, permissionSets []string, startTime, endTime time.Time) []byte {
	formFields := []map[string]interface{}{
		{
			"id":    "email",
			"name":  "申请人邮箱",
			"type":  "input",
			"value": email,
		},
		{
			"id":    "app_type",
			"name":  "申请人选择申请类型",
			"type":  "radioV2",
			"value": "其他申请",
		},
		{
			"id":    "account",
			"name":  "生产账号选择",
			"type":  "radioV2",
			"value": fmt.Sprintf("Account (#%s)", accountID),
		},
		{
			"id":    "permissions",
			"name":  "选择生产 SSO Role",
			"type":  "checkboxV2",
			"value": permissionSets,
		},
		{
			"id":   "date_range",
			"name": "申请时间范围",
			"type": "dateInterval",
			"value": map[string]string{
				"start": startTime.Format(time.RFC3339),
				"end":   endTime.Format(time.RFC3339),
			},
		},
	}

	formJSON, _ := json.Marshal(formFields)

	response := map[string]interface{}{
		"code": 0,
		"msg":  "success",
		"data": map[string]interface{}{
			"instance_code": "test-instance-001",
			"status":        "APPROVED",
			"form":          string(formJSON),
		},
	}

	responseJSON, _ := json.Marshal(response)
	return responseJSON
}

func TestNewMonitor(t *testing.T) {
	cfg := createMonitorTestConfig()
	log := createMonitorTestLogger()
	stateStore := newMockMonitorStateStore()

	monitor := NewMonitor(nil, nil, stateStore, cfg, log)

	assert.NotNil(t, monitor)
	assert.NotNil(t, monitor.parser)
	assert.NotNil(t, monitor.pendingInstances)
	assert.Equal(t, cfg, monitor.config)
	assert.Equal(t, stateStore, monitor.state)
}

func TestMonitor_GetPendingInstanceCount(t *testing.T) {
	cfg := createMonitorTestConfig()
	log := createMonitorTestLogger()

	monitor := NewMonitor(nil, nil, nil, cfg, log)

	// Initially should be 0
	assert.Equal(t, 0, monitor.GetPendingInstanceCount())

	// Add some pending instances
	monitor.pendingMu.Lock()
	monitor.pendingInstances["instance-1"] = true
	monitor.pendingInstances["instance-2"] = true
	monitor.pendingMu.Unlock()

	assert.Equal(t, 2, monitor.GetPendingInstanceCount())
}

func TestMonitor_GetPendingInstances(t *testing.T) {
	cfg := createMonitorTestConfig()
	log := createMonitorTestLogger()

	monitor := NewMonitor(nil, nil, nil, cfg, log)

	// Add some pending instances
	monitor.pendingMu.Lock()
	monitor.pendingInstances["instance-1"] = true
	monitor.pendingInstances["instance-2"] = true
	monitor.pendingMu.Unlock()

	instances := monitor.GetPendingInstances()
	assert.Len(t, instances, 2)
	assert.Contains(t, instances, "instance-1")
	assert.Contains(t, instances, "instance-2")
}

func TestMonitor_ClearPendingInstances(t *testing.T) {
	cfg := createMonitorTestConfig()
	log := createMonitorTestLogger()

	monitor := NewMonitor(nil, nil, nil, cfg, log)

	// Add some pending instances
	monitor.pendingMu.Lock()
	monitor.pendingInstances["instance-1"] = true
	monitor.pendingInstances["instance-2"] = true
	monitor.pendingMu.Unlock()

	assert.Equal(t, 2, monitor.GetPendingInstanceCount())

	// Clear pending instances
	monitor.ClearPendingInstances()

	assert.Equal(t, 0, monitor.GetPendingInstanceCount())
}

func TestMonitor_SetLastPollTime(t *testing.T) {
	cfg := createMonitorTestConfig()
	log := createMonitorTestLogger()

	monitor := NewMonitor(nil, nil, nil, cfg, log)

	testTime := time.Date(2024, 1, 15, 10, 30, 0, 0, time.UTC)
	monitor.SetLastPollTime(testTime)

	assert.Equal(t, testTime, monitor.GetLastPollTime())
}

func TestMonitor_StartApprovalMonitor_NoFeishuClient(t *testing.T) {
	cfg := createMonitorTestConfig()
	log := createMonitorTestLogger()

	monitor := NewMonitor(nil, nil, nil, cfg, log)

	ctx := context.Background()
	err := monitor.StartApprovalMonitor(ctx)

	assert.Error(t, err)
	assert.Contains(t, err.Error(), "feishu client is not configured")
}

func TestMonitor_StartExpirationMonitor_NoStateStore(t *testing.T) {
	cfg := createMonitorTestConfig()
	log := createMonitorTestLogger()

	monitor := NewMonitor(nil, nil, nil, cfg, log)

	ctx := context.Background()
	err := monitor.StartExpirationMonitor(ctx)

	assert.Error(t, err)
	assert.Contains(t, err.Error(), "state store is not configured")
}

func TestMonitor_ProcessNewApprovals_NoFeishuClient(t *testing.T) {
	cfg := createMonitorTestConfig()
	log := createMonitorTestLogger()

	monitor := NewMonitor(nil, nil, nil, cfg, log)

	ctx := context.Background()
	err := monitor.ProcessNewApprovals(ctx)

	assert.Error(t, err)
	assert.Contains(t, err.Error(), "feishu client is not configured")
}

func TestMonitor_ProcessExpiredPermissions_NoManager(t *testing.T) {
	cfg := createMonitorTestConfig()
	log := createMonitorTestLogger()

	monitor := NewMonitor(nil, nil, nil, cfg, log)

	ctx := context.Background()
	err := monitor.ProcessExpiredPermissions(ctx)

	assert.Error(t, err)
	assert.Contains(t, err.Error(), "permission manager is not configured")
}

func TestMonitor_StartApprovalMonitor_ContextCancellation(t *testing.T) {
	cfg := createMonitorTestConfig()
	cfg.Runtime.PollInterval = 50 * time.Millisecond
	log := createMonitorTestLogger()
	stateStore := newMockMonitorStateStore()

	// Create a manager with the state store
	manager := &Manager{
		state:  stateStore,
		config: cfg,
		logger: log.WithModule("permission-manager"),
	}

	// For this test, we use a nil feishu client and test that the monitor
	// properly handles context cancellation even when processing fails
	monitor := NewMonitor(manager, nil, stateStore, cfg, log)

	ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
	defer cancel()

	// The monitor should return an error because feishu client is nil
	err := monitor.StartApprovalMonitor(ctx)

	// Should return an error (feishu client not configured)
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "feishu client is not configured")
}

func TestMonitor_StartExpirationMonitor_ContextCancellation(t *testing.T) {
	cfg := createMonitorTestConfig()
	cfg.Runtime.ExpireCheckInterval = 50 * time.Millisecond
	log := createMonitorTestLogger()
	stateStore := newMockMonitorStateStore()

	// Create a mock manager
	manager := &Manager{
		state:  stateStore,
		config: cfg,
		logger: log.WithModule("permission-manager"),
	}

	monitor := NewMonitor(manager, nil, stateStore, cfg, log)

	ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
	defer cancel()

	err := monitor.StartExpirationMonitor(ctx)

	// Should return context error
	assert.Error(t, err)
	assert.Equal(t, context.DeadlineExceeded, err)
}

func TestMonitor_ProcessExpiredPermissions_WithExpiredInstances(t *testing.T) {
	cfg := createMonitorTestConfig()
	log := createMonitorTestLogger()
	stateStore := newMockMonitorStateStore()

	// Add an expired instance
	expiredInstance := &state.ApprovalInstance{
		ID:             "expired-instance-001",
		Email:          "test@example.com",
		AccountID:      "123456789",
		PermissionSets: []string{"ViewOnly"},
		StartTime:      time.Now().Add(-48 * time.Hour),
		EndTime:        time.Now().Add(-24 * time.Hour), // Expired 24 hours ago
		Status:         state.StatusApproved,
		CloudType:      "AWS",
	}
	stateStore.instances[expiredInstance.ID] = expiredInstance

	// Create a mock manager
	manager := &Manager{
		state:  stateStore,
		config: cfg,
		logger: log.WithModule("permission-manager"),
	}

	monitor := NewMonitor(manager, nil, stateStore, cfg, log)

	ctx := context.Background()
	err := monitor.ProcessExpiredPermissions(ctx)

	assert.NoError(t, err)

	// Check that the instance status was updated to expired
	updatedInstance, _ := stateStore.GetInstance(ctx, expiredInstance.ID)
	assert.Equal(t, state.StatusExpired, updatedInstance.Status)
}

func TestMonitor_ProcessExpiredPermissions_NoPendingRemovals(t *testing.T) {
	cfg := createMonitorTestConfig()
	log := createMonitorTestLogger()
	stateStore := newMockMonitorStateStore()

	// Create a mock manager
	manager := &Manager{
		state:  stateStore,
		config: cfg,
		logger: log.WithModule("permission-manager"),
	}

	monitor := NewMonitor(manager, nil, stateStore, cfg, log)

	ctx := context.Background()
	err := monitor.ProcessExpiredPermissions(ctx)

	assert.NoError(t, err)
}

func TestMonitor_ProcessExpiredPermissions_DoesNotExecutePendingRemovals(t *testing.T) {
	cfg := createMonitorTestConfig()
	log := createMonitorTestLogger()
	stateStore := newMockMonitorStateStore()

	stateStore.pendingRemovals = append(stateStore.pendingRemovals, &state.PendingRemoval{
		ID:            1,
		AccountID:     "123456789",
		Email:         "test@example.com",
		PermissionSet: "ViewOnly",
		CloudType:     "AWS",
		ScheduledAt:   time.Now().Add(-time.Hour),
	})

	manager := &Manager{
		state:  stateStore,
		config: cfg,
		logger: log.WithModule("permission-manager"),
	}

	monitor := NewMonitor(manager, nil, stateStore, cfg, log)

	err := monitor.ProcessExpiredPermissions(context.Background())
	assert.NoError(t, err)
	assert.Len(t, stateStore.pendingRemovals, 1)
	assert.Equal(t, int64(1), stateStore.pendingRemovals[0].ID)
}

func TestMonitor_ConcurrentPendingInstanceAccess(t *testing.T) {
	cfg := createMonitorTestConfig()
	log := createMonitorTestLogger()

	monitor := NewMonitor(nil, nil, nil, cfg, log)

	// Test concurrent access to pending instances
	var wg sync.WaitGroup
	numGoroutines := 10
	numOperations := 100

	// Writers
	for i := 0; i < numGoroutines; i++ {
		wg.Add(1)
		go func(id int) {
			defer wg.Done()
			for j := 0; j < numOperations; j++ {
				instanceID := fmt.Sprintf("instance-%d-%d", id, j)
				monitor.pendingMu.Lock()
				monitor.pendingInstances[instanceID] = true
				monitor.pendingMu.Unlock()
			}
		}(i)
	}

	// Readers
	for i := 0; i < numGoroutines; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			for j := 0; j < numOperations; j++ {
				_ = monitor.GetPendingInstanceCount()
				_ = monitor.GetPendingInstances()
			}
		}()
	}

	wg.Wait()

	// Verify all instances were added
	assert.Equal(t, numGoroutines*numOperations, monitor.GetPendingInstanceCount())
}

func TestMonitor_DefaultPollInterval(t *testing.T) {
	cfg := createMonitorTestConfig()
	cfg.Runtime.PollInterval = 0 // Set to 0 to test default
	log := createMonitorTestLogger()
	stateStore := newMockMonitorStateStore()

	// Create a manager
	manager := &Manager{
		state:  stateStore,
		config: cfg,
		logger: log.WithModule("permission-manager"),
	}

	// Use nil feishu client to test that the monitor handles missing client
	monitor := NewMonitor(manager, nil, stateStore, cfg, log)

	// Start with a very short timeout to verify it starts
	ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)
	defer cancel()

	err := monitor.StartApprovalMonitor(ctx)

	// Should return an error (feishu client not configured)
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "feishu client is not configured")
}

func TestMonitor_DefaultExpireCheckInterval(t *testing.T) {
	cfg := createMonitorTestConfig()
	cfg.Runtime.ExpireCheckInterval = 0 // Set to 0 to test default
	log := createMonitorTestLogger()
	stateStore := newMockMonitorStateStore()

	manager := &Manager{
		state:  stateStore,
		config: cfg,
		logger: log.WithModule("permission-manager"),
	}

	monitor := NewMonitor(manager, nil, stateStore, cfg, log)

	// Start with a very short timeout to verify it starts
	ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)
	defer cancel()

	err := monitor.StartExpirationMonitor(ctx)

	// Should return context error (not a configuration error)
	assert.Error(t, err)
	assert.Equal(t, context.DeadlineExceeded, err)
}

func TestMonitor_ProcessInstance_AlreadyDeleted(t *testing.T) {
	cfg := createMonitorTestConfig()
	log := createMonitorTestLogger()
	stateStore := newMockMonitorStateStore()

	// Mark instance as already deleted
	stateStore.deletedInstances["test-instance-001"] = true

	// Create monitor without feishu client (we're testing the skip logic)
	monitor := NewMonitor(nil, nil, stateStore, cfg, log)

	ctx := context.Background()
	err := monitor.processInstance(ctx, "test-instance-001")

	// Should return nil (skipped)
	assert.NoError(t, err)
}

func TestMonitor_ProcessInstance_InvalidApprovalLogsInfo(t *testing.T) {
	cfg := createMonitorTestConfig()
	stateStore := newMockMonitorStateStore()

	var logBuf bytes.Buffer
	log := logger.NewLogger(logger.Config{
		Level:       logger.InfoLevel,
		Output:      &logBuf,
		PrettyPrint: false,
	})

	invalidResponse := createMockInvalidApprovalResponse(
		"test@example.com",
		"123456789",
		[]string{"ViewOnly"},
		time.Now().Add(-2*time.Hour),
		time.Now().Add(2*time.Hour),
	)

	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")

		switch {
		case strings.Contains(r.URL.Path, "/auth/v3/tenant_access_token"):
			json.NewEncoder(w).Encode(map[string]interface{}{
				"code":                0,
				"msg":                 "success",
				"tenant_access_token": "test-token-12345",
				"expire":              7200,
			})
		case strings.Contains(r.URL.Path, "/approval/v4/instances/"):
			w.Write(invalidResponse)
		default:
			w.WriteHeader(http.StatusNotFound)
			json.NewEncoder(w).Encode(map[string]interface{}{
				"code": 404,
				"msg":  "not found",
			})
		}
	}))
	defer server.Close()

	cfg.Feishu.BaseURL = server.URL
	httpClient := httpclient.New(&httpclient.Config{Timeout: 5 * time.Second})
	feishuClient := feishu.NewClient(&cfg.Feishu, httpClient)

	monitor := NewMonitor(nil, feishuClient, stateStore, cfg, log)

	err := monitor.processInstance(context.Background(), "test-instance-001")
	assert.NoError(t, err)

	assert.Contains(t, logBuf.String(), "[SKIP] Instance is not a valid SSO application")
}

func TestMonitor_CheckNewlyExpiredPermissions_NoState(t *testing.T) {
	cfg := createMonitorTestConfig()
	log := createMonitorTestLogger()

	monitor := NewMonitor(nil, nil, nil, cfg, log)

	ctx := context.Background()
	err := monitor.checkNewlyExpiredPermissions(ctx)

	// Should return nil when state is nil
	assert.NoError(t, err)
}

func TestMonitor_CheckNewlyExpiredPermissions_ListError(t *testing.T) {
	cfg := createMonitorTestConfig()
	log := createMonitorTestLogger()
	stateStore := newMockMonitorStateStore()
	stateStore.listInstancesErr = fmt.Errorf("database error")

	monitor := NewMonitor(nil, nil, stateStore, cfg, log)

	ctx := context.Background()
	err := monitor.checkNewlyExpiredPermissions(ctx)

	assert.Error(t, err)
	assert.Contains(t, err.Error(), "failed to list instances")
}

func TestMonitor_CheckNewlyExpiredPermissions_ContextCancellation(t *testing.T) {
	cfg := createMonitorTestConfig()
	log := createMonitorTestLogger()
	stateStore := newMockMonitorStateStore()

	// Add multiple instances
	for i := 0; i < 5; i++ {
		instance := &state.ApprovalInstance{
			ID:             fmt.Sprintf("instance-%d", i),
			Email:          "test@example.com",
			AccountID:      "123456789",
			PermissionSets: []string{"ViewOnly"},
			StartTime:      time.Now().Add(-48 * time.Hour),
			EndTime:        time.Now().Add(-24 * time.Hour),
			Status:         state.StatusApproved,
			CloudType:      "AWS",
		}
		stateStore.instances[instance.ID] = instance
	}

	monitor := NewMonitor(nil, nil, stateStore, cfg, log)

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

	err := monitor.checkNewlyExpiredPermissions(ctx)

	assert.Error(t, err)
	assert.Equal(t, context.Canceled, err)
}

func TestMonitor_CheckNewlyExpiredPermissions_NonExpiredInstances(t *testing.T) {
	cfg := createMonitorTestConfig()
	log := createMonitorTestLogger()
	stateStore := newMockMonitorStateStore()

	// Add a non-expired instance
	instance := &state.ApprovalInstance{
		ID:             "active-instance",
		Email:          "test@example.com",
		AccountID:      "123456789",
		PermissionSets: []string{"ViewOnly"},
		StartTime:      time.Now().Add(-24 * time.Hour),
		EndTime:        time.Now().Add(24 * time.Hour), // Not expired
		Status:         state.StatusApproved,
		CloudType:      "AWS",
	}
	stateStore.instances[instance.ID] = instance

	monitor := NewMonitor(nil, nil, stateStore, cfg, log)

	ctx := context.Background()
	err := monitor.checkNewlyExpiredPermissions(ctx)

	assert.NoError(t, err)

	// Instance status should not change
	updatedInstance, _ := stateStore.GetInstance(ctx, instance.ID)
	assert.Equal(t, state.StatusApproved, updatedInstance.Status)
}

func TestMonitor_Integration_ApprovalAndExpiration(t *testing.T) {
	cfg := createMonitorTestConfig()
	log := createMonitorTestLogger()
	stateStore := newMockMonitorStateStore()

	// Create a manager with the state store
	manager := &Manager{
		state:  stateStore,
		config: cfg,
		logger: log.WithModule("permission-manager"),
	}

	monitor := NewMonitor(manager, nil, stateStore, cfg, log)

	// Add an approved but expired instance
	expiredInstance := &state.ApprovalInstance{
		ID:             "integration-test-001",
		Email:          "integration@example.com",
		AccountID:      "987654321",
		PermissionSets: []string{"Admin", "ViewOnly"},
		StartTime:      time.Now().Add(-72 * time.Hour),
		EndTime:        time.Now().Add(-48 * time.Hour),
		Status:         state.StatusApproved,
		CloudType:      "GCP",
	}
	require.NoError(t, stateStore.SaveInstance(context.Background(), expiredInstance))

	// Process expired permissions
	ctx := context.Background()
	err := monitor.ProcessExpiredPermissions(ctx)
	require.NoError(t, err)

	// Verify the instance was marked as expired
	updatedInstance, err := stateStore.GetInstance(ctx, expiredInstance.ID)
	require.NoError(t, err)
	assert.Equal(t, state.StatusExpired, updatedInstance.Status)
}
