// Package state provides state management for AutoSSO.
package state

import (
	"testing"
	"time"
)

// TestStatusConstants verifies that status constants are defined correctly.
func TestStatusConstants(t *testing.T) {
	tests := []struct {
		name     string
		status   string
		expected string
	}{
		{"StatusPending", StatusPending, "pending"},
		{"StatusApproved", StatusApproved, "approved"},
		{"StatusExpired", StatusExpired, "expired"},
		{"StatusRevoked", StatusRevoked, "revoked"},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if tt.status != tt.expected {
				t.Errorf("expected %s, got %s", tt.expected, tt.status)
			}
		})
	}
}

// TestCloudTypeConstants verifies that cloud type constants are defined correctly.
func TestCloudTypeConstants(t *testing.T) {
	tests := []struct {
		name      string
		cloudType string
		expected  string
	}{
		{"CloudTypeAWS", CloudTypeAWS, "AWS"},
		{"CloudTypeGCP", CloudTypeGCP, "GCP"},
		{"CloudTypeAzure", CloudTypeAzure, "Azure"},
		{"CloudTypeAlibaba", CloudTypeAlibaba, "Alibaba"},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if tt.cloudType != tt.expected {
				t.Errorf("expected %s, got %s", tt.expected, tt.cloudType)
			}
		})
	}
}

// TestValidStatus verifies the ValidStatus function.
func TestValidStatus(t *testing.T) {
	tests := []struct {
		status   string
		expected bool
	}{
		{StatusPending, true},
		{StatusApproved, true},
		{StatusExpired, true},
		{StatusRevoked, true},
		{"invalid", false},
		{"", false},
		{"PENDING", false}, // case sensitive
	}

	for _, tt := range tests {
		t.Run(tt.status, func(t *testing.T) {
			result := ValidStatus(tt.status)
			if result != tt.expected {
				t.Errorf("ValidStatus(%q) = %v, expected %v", tt.status, result, tt.expected)
			}
		})
	}
}

// TestValidCloudType verifies the ValidCloudType function.
func TestValidCloudType(t *testing.T) {
	tests := []struct {
		cloudType string
		expected  bool
	}{
		{CloudTypeAWS, true},
		{CloudTypeGCP, true},
		{CloudTypeAzure, true},
		{CloudTypeAlibaba, true},
		{"invalid", false},
		{"", false},
		{"aws", false}, // case sensitive
	}

	for _, tt := range tests {
		t.Run(tt.cloudType, func(t *testing.T) {
			result := ValidCloudType(tt.cloudType)
			if result != tt.expected {
				t.Errorf("ValidCloudType(%q) = %v, expected %v", tt.cloudType, result, tt.expected)
			}
		})
	}
}

// TestApprovalInstanceIsExpired verifies the IsExpired method.
func TestApprovalInstanceIsExpired(t *testing.T) {
	now := time.Now()

	tests := []struct {
		name     string
		endTime  time.Time
		expected bool
	}{
		{"expired yesterday", now.Add(-24 * time.Hour), true},
		{"expired 1 hour ago", now.Add(-1 * time.Hour), true},
		{"expires in 1 hour", now.Add(1 * time.Hour), false},
		{"expires tomorrow", now.Add(24 * time.Hour), false},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			instance := &ApprovalInstance{
				EndTime: tt.endTime,
			}
			result := instance.IsExpired()
			if result != tt.expected {
				t.Errorf("IsExpired() = %v, expected %v", result, tt.expected)
			}
		})
	}
}

// TestApprovalInstanceIsActive verifies the IsActive method.
func TestApprovalInstanceIsActive(t *testing.T) {
	now := time.Now()

	tests := []struct {
		name      string
		status    string
		startTime time.Time
		endTime   time.Time
		expected  bool
	}{
		{
			name:      "active approved instance",
			status:    StatusApproved,
			startTime: now.Add(-1 * time.Hour),
			endTime:   now.Add(1 * time.Hour),
			expected:  true,
		},
		{
			name:      "pending instance",
			status:    StatusPending,
			startTime: now.Add(-1 * time.Hour),
			endTime:   now.Add(1 * time.Hour),
			expected:  false,
		},
		{
			name:      "expired instance",
			status:    StatusApproved,
			startTime: now.Add(-2 * time.Hour),
			endTime:   now.Add(-1 * time.Hour),
			expected:  false,
		},
		{
			name:      "not yet started",
			status:    StatusApproved,
			startTime: now.Add(1 * time.Hour),
			endTime:   now.Add(2 * time.Hour),
			expected:  false,
		},
		{
			name:      "revoked instance",
			status:    StatusRevoked,
			startTime: now.Add(-1 * time.Hour),
			endTime:   now.Add(1 * time.Hour),
			expected:  false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			instance := &ApprovalInstance{
				Status:    tt.status,
				StartTime: tt.startTime,
				EndTime:   tt.endTime,
			}
			result := instance.IsActive()
			if result != tt.expected {
				t.Errorf("IsActive() = %v, expected %v", result, tt.expected)
			}
		})
	}
}

// TestApprovalInstanceDaysUntilExpiry verifies the DaysUntilExpiry method.
func TestApprovalInstanceDaysUntilExpiry(t *testing.T) {
	now := time.Now()

	tests := []struct {
		name     string
		endTime  time.Time
		expected int
	}{
		{"already expired", now.Add(-24 * time.Hour), 0},
		{"expires in less than 24 hours", now.Add(23 * time.Hour), 0},
		{"expires in about 1 day", now.Add(25 * time.Hour), 1},
		{"expires in about 2 days", now.Add(49 * time.Hour), 2},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			instance := &ApprovalInstance{
				EndTime: tt.endTime,
			}
			result := instance.DaysUntilExpiry()
			if result != tt.expected {
				t.Errorf("DaysUntilExpiry() = %d, expected %d", result, tt.expected)
			}
		})
	}
}

// TestApprovalInstanceDaysSinceExpiry verifies the DaysSinceExpiry method.
func TestApprovalInstanceDaysSinceExpiry(t *testing.T) {
	now := time.Now()

	tests := []struct {
		name     string
		endTime  time.Time
		expected int
	}{
		{"not expired", now.Add(24 * time.Hour), 0},
		{"expired 1 day ago", now.Add(-24 * time.Hour), 1},
		{"expired 3 days ago", now.Add(-3 * 24 * time.Hour), 3},
		{"expired 7 days ago", now.Add(-7 * 24 * time.Hour), 7},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			instance := &ApprovalInstance{
				EndTime: tt.endTime,
			}
			result := instance.DaysSinceExpiry()
			if result != tt.expected {
				t.Errorf("DaysSinceExpiry() = %d, expected %d", result, tt.expected)
			}
		})
	}
}

// TestPendingRemovalIsReadyForRemoval verifies the IsReadyForRemoval method.
func TestPendingRemovalIsReadyForRemoval(t *testing.T) {
	now := time.Now()

	tests := []struct {
		name        string
		scheduledAt time.Time
		expected    bool
	}{
		{"scheduled in the past", now.Add(-1 * time.Hour), true},
		{"scheduled now", now.Add(-1 * time.Second), true},
		{"scheduled in the future", now.Add(1 * time.Hour), false},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			removal := &PendingRemoval{
				ScheduledAt: tt.scheduledAt,
			}
			result := removal.IsReadyForRemoval()
			if result != tt.expected {
				t.Errorf("IsReadyForRemoval() = %v, expected %v", result, tt.expected)
			}
		})
	}
}

// TestPendingRemovalDaysUntilRemoval verifies the DaysUntilRemoval method.
func TestPendingRemovalDaysUntilRemoval(t *testing.T) {
	now := time.Now()

	tests := []struct {
		name        string
		scheduledAt time.Time
		expected    int
	}{
		{"already ready", now.Add(-24 * time.Hour), 0},
		{"scheduled in less than 24 hours", now.Add(23 * time.Hour), 0},
		{"scheduled in about 1 day", now.Add(25 * time.Hour), 1},
		{"scheduled in about 2 days", now.Add(49 * time.Hour), 2},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			removal := &PendingRemoval{
				ScheduledAt: tt.scheduledAt,
			}
			result := removal.DaysUntilRemoval()
			if result != tt.expected {
				t.Errorf("DaysUntilRemoval() = %d, expected %d", result, tt.expected)
			}
		})
	}
}

// TestInstanceFilterIsEmpty verifies the IsEmpty method.
func TestInstanceFilterIsEmpty(t *testing.T) {
	tests := []struct {
		name     string
		filter   *InstanceFilter
		expected bool
	}{
		{"nil filter", nil, true},
		{"empty filter", &InstanceFilter{}, true},
		{"filter with status", &InstanceFilter{Status: StatusApproved}, false},
		{"filter with cloud type", &InstanceFilter{CloudType: CloudTypeAWS}, false},
		{"filter with email", &InstanceFilter{Email: "test@example.com"}, false},
		{"filter with start time", &InstanceFilter{StartTime: time.Now()}, false},
		{"filter with end time", &InstanceFilter{EndTime: time.Now()}, false},
		{"filter with account ID", &InstanceFilter{AccountID: "123456"}, false},
		{"filter with limit", &InstanceFilter{Limit: 10}, false},
		{"filter with offset", &InstanceFilter{Offset: 5}, false},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := tt.filter.IsEmpty()
			if result != tt.expected {
				t.Errorf("IsEmpty() = %v, expected %v", result, tt.expected)
			}
		})
	}
}

// TestNewApprovalInstance verifies the NewApprovalInstance constructor.
func TestNewApprovalInstance(t *testing.T) {
	id := "test-instance-123"
	email := "user@example.com"
	accountID := "557537366020"
	cloudType := CloudTypeAWS
	permissionSets := []string{"ViewOnly", "Admin"}
	startTime := time.Now()
	endTime := startTime.Add(24 * time.Hour)

	instance := NewApprovalInstance(id, email, accountID, cloudType, permissionSets, startTime, endTime)

	if instance.ID != id {
		t.Errorf("ID = %s, expected %s", instance.ID, id)
	}
	if instance.Email != email {
		t.Errorf("Email = %s, expected %s", instance.Email, email)
	}
	if instance.AccountID != accountID {
		t.Errorf("AccountID = %s, expected %s", instance.AccountID, accountID)
	}
	if instance.CloudType != cloudType {
		t.Errorf("CloudType = %s, expected %s", instance.CloudType, cloudType)
	}
	if len(instance.PermissionSets) != len(permissionSets) {
		t.Errorf("PermissionSets length = %d, expected %d", len(instance.PermissionSets), len(permissionSets))
	}
	if !instance.StartTime.Equal(startTime) {
		t.Errorf("StartTime = %v, expected %v", instance.StartTime, startTime)
	}
	if !instance.EndTime.Equal(endTime) {
		t.Errorf("EndTime = %v, expected %v", instance.EndTime, endTime)
	}
	if instance.Status != StatusPending {
		t.Errorf("Status = %s, expected %s", instance.Status, StatusPending)
	}
	if instance.CreatedAt.IsZero() {
		t.Error("CreatedAt should not be zero")
	}
	if instance.UpdatedAt.IsZero() {
		t.Error("UpdatedAt should not be zero")
	}
}

// TestNewPendingRemoval verifies the NewPendingRemoval constructor.
func TestNewPendingRemoval(t *testing.T) {
	accountID := "557537366020"
	email := "user@example.com"
	permissionSet := "ViewOnly"
	cloudType := CloudTypeAWS
	expiredAt := time.Now()
	scheduledAt := expiredAt.Add(3 * 24 * time.Hour)

	removal := NewPendingRemoval(accountID, email, permissionSet, cloudType, expiredAt, scheduledAt)

	if removal.AccountID != accountID {
		t.Errorf("AccountID = %s, expected %s", removal.AccountID, accountID)
	}
	if removal.Email != email {
		t.Errorf("Email = %s, expected %s", removal.Email, email)
	}
	if removal.PermissionSet != permissionSet {
		t.Errorf("PermissionSet = %s, expected %s", removal.PermissionSet, permissionSet)
	}
	if removal.CloudType != cloudType {
		t.Errorf("CloudType = %s, expected %s", removal.CloudType, cloudType)
	}
	if !removal.ExpiredAt.Equal(expiredAt) {
		t.Errorf("ExpiredAt = %v, expected %v", removal.ExpiredAt, expiredAt)
	}
	if !removal.ScheduledAt.Equal(scheduledAt) {
		t.Errorf("ScheduledAt = %v, expected %v", removal.ScheduledAt, scheduledAt)
	}
	if removal.CreatedAt.IsZero() {
		t.Error("CreatedAt should not be zero")
	}
	// ID should be 0 as it's auto-generated by the database
	if removal.ID != 0 {
		t.Errorf("ID = %d, expected 0", removal.ID)
	}
}

// TestApprovalInstancePermissionSetsSlice verifies that PermissionSets is properly handled.
func TestApprovalInstancePermissionSetsSlice(t *testing.T) {
	tests := []struct {
		name           string
		permissionSets []string
		expectedLen    int
	}{
		{"nil permissions", nil, 0},
		{"empty permissions", []string{}, 0},
		{"single permission", []string{"ViewOnly"}, 1},
		{"multiple permissions", []string{"ViewOnly", "Admin", "Billing"}, 3},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			instance := &ApprovalInstance{
				PermissionSets: tt.permissionSets,
			}
			if len(instance.PermissionSets) != tt.expectedLen {
				t.Errorf("PermissionSets length = %d, expected %d", len(instance.PermissionSets), tt.expectedLen)
			}
		})
	}
}

// TestInstanceFilterMultipleCriteria verifies filter with multiple criteria.
func TestInstanceFilterMultipleCriteria(t *testing.T) {
	now := time.Now()
	filter := &InstanceFilter{
		Status:    StatusApproved,
		CloudType: CloudTypeAWS,
		Email:     "user@example.com",
		StartTime: now.Add(-24 * time.Hour),
		EndTime:   now.Add(24 * time.Hour),
		AccountID: "123456",
		Limit:     10,
		Offset:    5,
	}

	if filter.IsEmpty() {
		t.Error("Filter with multiple criteria should not be empty")
	}

	// Verify all fields are set correctly
	if filter.Status != StatusApproved {
		t.Errorf("Status = %s, expected %s", filter.Status, StatusApproved)
	}
	if filter.CloudType != CloudTypeAWS {
		t.Errorf("CloudType = %s, expected %s", filter.CloudType, CloudTypeAWS)
	}
	if filter.Email != "user@example.com" {
		t.Errorf("Email = %s, expected user@example.com", filter.Email)
	}
	if filter.Limit != 10 {
		t.Errorf("Limit = %d, expected 10", filter.Limit)
	}
	if filter.Offset != 5 {
		t.Errorf("Offset = %d, expected 5", filter.Offset)
	}
}
