package services

import (
	"testing"
	"time"

	"github.com/google/uuid"
)

// TestNotificationService_CreateNotification tests notification creation
func TestNotificationService_CreateNotification(t *testing.T) {
	tests := []struct {
		name        string
		userID      uuid.UUID
		notifType   NotificationType
		title       string
		body        string
		data        map[string]interface{}
		expectError bool
	}{
		{
			name:      "valid notification",
			userID:    uuid.New(),
			notifType: NotificationTypeConsentRequired,
			title:     "Consent Required",
			body:      "Please review and accept the new terms",
			data:      map[string]interface{}{"document_id": "123"},
			expectError: false,
		},
		{
			name:      "notification without data",
			userID:    uuid.New(),
			notifType: NotificationTypeGeneral,
			title:     "General Notification",
			body:      "This is a test",
			data:      nil,
			expectError: false,
		},
		{
			name:        "empty user ID",
			userID:      uuid.Nil,
			notifType:   NotificationTypeGeneral,
			title:       "Test",
			body:        "Test",
			expectError: true,
		},
		{
			name:        "empty title",
			userID:      uuid.New(),
			notifType:   NotificationTypeGeneral,
			title:       "",
			body:        "Test body",
			expectError: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			var err error
			if tt.userID == uuid.Nil {
				err = &ValidationError{Field: "user ID", Message: "required"}
			} else if tt.title == "" {
				err = &ValidationError{Field: "title", Message: "required"}
			}

			if tt.expectError && err == nil {
				t.Error("Expected error, got nil")
			}
			if !tt.expectError && err != nil {
				t.Errorf("Expected no error, got %v", err)
			}
		})
	}
}

// TestNotificationService_NotificationTypes tests notification type validation
func TestNotificationService_NotificationTypes(t *testing.T) {
	tests := []struct {
		notifType NotificationType
		isValid   bool
	}{
		{NotificationTypeConsentRequired, true},
		{NotificationTypeConsentReminder, true},
		{NotificationTypeVersionPublished, true},
		{NotificationTypeVersionApproved, true},
		{NotificationTypeVersionRejected, true},
		{NotificationTypeAccountSuspended, true},
		{NotificationTypeAccountRestored, true},
		{NotificationTypeGeneral, true},
		{NotificationType("invalid_type"), false},
		{NotificationType(""), false},
	}

	validTypes := map[NotificationType]bool{
		NotificationTypeConsentRequired:  true,
		NotificationTypeConsentReminder:  true,
		NotificationTypeVersionPublished: true,
		NotificationTypeVersionApproved:  true,
		NotificationTypeVersionRejected:  true,
		NotificationTypeAccountSuspended: true,
		NotificationTypeAccountRestored:  true,
		NotificationTypeGeneral:          true,
	}

	for _, tt := range tests {
		t.Run(string(tt.notifType), func(t *testing.T) {
			isValid := validTypes[tt.notifType]

			if isValid != tt.isValid {
				t.Errorf("Type %s: expected valid=%v, got %v", tt.notifType, tt.isValid, isValid)
			}
		})
	}
}

// TestNotificationService_NotificationChannels tests channel validation
func TestNotificationService_NotificationChannels(t *testing.T) {
	tests := []struct {
		channel NotificationChannel
		isValid bool
	}{
		{ChannelInApp, true},
		{ChannelEmail, true},
		{ChannelPush, true},
		{NotificationChannel("sms"), false},
		{NotificationChannel(""), false},
	}

	validChannels := map[NotificationChannel]bool{
		ChannelInApp: true,
		ChannelEmail: true,
		ChannelPush:  true,
	}

	for _, tt := range tests {
		t.Run(string(tt.channel), func(t *testing.T) {
			isValid := validChannels[tt.channel]

			if isValid != tt.isValid {
				t.Errorf("Channel %s: expected valid=%v, got %v", tt.channel, tt.isValid, isValid)
			}
		})
	}
}

// TestNotificationService_GetUserNotifications tests retrieving notifications
func TestNotificationService_GetUserNotifications(t *testing.T) {
	tests := []struct {
		name         string
		userID       uuid.UUID
		limit        int
		offset       int
		unreadOnly   bool
		expectError  bool
	}{
		{
			name:        "get all notifications",
			userID:      uuid.New(),
			limit:       50,
			offset:      0,
			unreadOnly:  false,
			expectError: false,
		},
		{
			name:        "get unread only",
			userID:      uuid.New(),
			limit:       50,
			offset:      0,
			unreadOnly:  true,
			expectError: false,
		},
		{
			name:        "with pagination",
			userID:      uuid.New(),
			limit:       10,
			offset:      20,
			unreadOnly:  false,
			expectError: false,
		},
		{
			name:        "invalid user ID",
			userID:      uuid.Nil,
			limit:       50,
			offset:      0,
			unreadOnly:  false,
			expectError: true,
		},
		{
			name:        "negative limit",
			userID:      uuid.New(),
			limit:       -1,
			offset:      0,
			unreadOnly:  false,
			expectError: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			var err error
			if tt.userID == uuid.Nil {
				err = &ValidationError{Field: "user ID", Message: "required"}
			} else if tt.limit < 0 {
				err = &ValidationError{Field: "limit", Message: "must be >= 0"}
			}

			if tt.expectError && err == nil {
				t.Error("Expected error, got nil")
			}
			if !tt.expectError && err != nil {
				t.Errorf("Expected no error, got %v", err)
			}
		})
	}
}

// TestNotificationService_MarkAsRead tests marking notifications as read
func TestNotificationService_MarkAsRead(t *testing.T) {
	tests := []struct {
		name           string
		notificationID uuid.UUID
		userID         uuid.UUID
		expectError    bool
	}{
		{
			name:           "mark valid notification as read",
			notificationID: uuid.New(),
			userID:         uuid.New(),
			expectError:    false,
		},
		{
			name:           "invalid notification ID",
			notificationID: uuid.Nil,
			userID:         uuid.New(),
			expectError:    true,
		},
		{
			name:           "invalid user ID",
			notificationID: uuid.New(),
			userID:         uuid.Nil,
			expectError:    true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			var err error
			if tt.notificationID == uuid.Nil {
				err = &ValidationError{Field: "notification ID", Message: "required"}
			} else if tt.userID == uuid.Nil {
				err = &ValidationError{Field: "user ID", Message: "required"}
			}

			if tt.expectError && err == nil {
				t.Error("Expected error, got nil")
			}
			if !tt.expectError && err != nil {
				t.Errorf("Expected no error, got %v", err)
			}
		})
	}
}

// TestNotificationService_GetPreferences tests retrieving user preferences
func TestNotificationService_GetPreferences(t *testing.T) {
	tests := []struct {
		name        string
		userID      uuid.UUID
		expectError bool
	}{
		{
			name:        "get valid user preferences",
			userID:      uuid.New(),
			expectError: false,
		},
		{
			name:        "invalid user ID",
			userID:      uuid.Nil,
			expectError: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			var err error
			if tt.userID == uuid.Nil {
				err = &ValidationError{Field: "user ID", Message: "required"}
			}

			if tt.expectError && err == nil {
				t.Error("Expected error, got nil")
			}
			if !tt.expectError && err != nil {
				t.Errorf("Expected no error, got %v", err)
			}
		})
	}
}

// TestNotificationService_UpdatePreferences tests updating notification preferences
func TestNotificationService_UpdatePreferences(t *testing.T) {
	tests := []struct {
		name              string
		userID            uuid.UUID
		emailEnabled      bool
		pushEnabled       bool
		inAppEnabled      bool
		reminderFrequency string
		expectError       bool
	}{
		{
			name:              "enable all notifications",
			userID:            uuid.New(),
			emailEnabled:      true,
			pushEnabled:       true,
			inAppEnabled:      true,
			reminderFrequency: "daily",
			expectError:       false,
		},
		{
			name:              "disable email notifications",
			userID:            uuid.New(),
			emailEnabled:      false,
			pushEnabled:       true,
			inAppEnabled:      true,
			reminderFrequency: "weekly",
			expectError:       false,
		},
		{
			name:              "set reminder frequency to never",
			userID:            uuid.New(),
			emailEnabled:      true,
			pushEnabled:       false,
			inAppEnabled:      true,
			reminderFrequency: "never",
			expectError:       false,
		},
		{
			name:              "invalid reminder frequency",
			userID:            uuid.New(),
			emailEnabled:      true,
			pushEnabled:       true,
			inAppEnabled:      true,
			reminderFrequency: "hourly",
			expectError:       true,
		},
	}

	validFrequencies := map[string]bool{
		"daily":  true,
		"weekly": true,
		"never":  true,
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			var err error
			if !validFrequencies[tt.reminderFrequency] {
				err = &ValidationError{Field: "reminder_frequency", Message: "must be daily, weekly, or never"}
			}

			if tt.expectError && err == nil {
				t.Error("Expected error, got nil")
			}
			if !tt.expectError && err != nil {
				t.Errorf("Expected no error, got %v", err)
			}
		})
	}
}

// TestNotificationService_NotifyConsentRequired tests consent required notification
func TestNotificationService_NotifyConsentRequired(t *testing.T) {
	tests := []struct {
		name         string
		documentName string
		versionID    string
		expectError  bool
	}{
		{
			name:         "valid consent notification",
			documentName: "Terms of Service",
			versionID:    uuid.New().String(),
			expectError:  false,
		},
		{
			name:         "empty document name",
			documentName: "",
			versionID:    uuid.New().String(),
			expectError:  true,
		},
		{
			name:         "empty version ID",
			documentName: "Privacy Policy",
			versionID:    "",
			expectError:  true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			var err error
			if tt.documentName == "" {
				err = &ValidationError{Field: "document name", Message: "required"}
			} else if tt.versionID == "" {
				err = &ValidationError{Field: "version ID", Message: "required"}
			}

			if tt.expectError && err == nil {
				t.Error("Expected error, got nil")
			}
			if !tt.expectError && err != nil {
				t.Errorf("Expected no error, got %v", err)
			}
		})
	}
}

// TestNotificationService_DeleteNotification tests deleting notifications
func TestNotificationService_DeleteNotification(t *testing.T) {
	tests := []struct {
		name           string
		notificationID uuid.UUID
		userID         uuid.UUID
		expectError    bool
	}{
		{
			name:           "delete valid notification",
			notificationID: uuid.New(),
			userID:         uuid.New(),
			expectError:    false,
		},
		{
			name:           "invalid notification ID",
			notificationID: uuid.Nil,
			userID:         uuid.New(),
			expectError:    true,
		},
		{
			name:           "invalid user ID",
			notificationID: uuid.New(),
			userID:         uuid.Nil,
			expectError:    true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			var err error
			if tt.notificationID == uuid.Nil {
				err = &ValidationError{Field: "notification ID", Message: "required"}
			} else if tt.userID == uuid.Nil {
				err = &ValidationError{Field: "user ID", Message: "required"}
			}

			if tt.expectError && err == nil {
				t.Error("Expected error, got nil")
			}
			if !tt.expectError && err != nil {
				t.Errorf("Expected no error, got %v", err)
			}
		})
	}
}

// TestNotificationService_BatchMarkAsRead tests batch marking as read
func TestNotificationService_BatchMarkAsRead(t *testing.T) {
	tests := []struct {
		name            string
		notificationIDs []uuid.UUID
		userID          uuid.UUID
		expectError     bool
	}{
		{
			name:            "mark multiple notifications",
			notificationIDs: []uuid.UUID{uuid.New(), uuid.New(), uuid.New()},
			userID:          uuid.New(),
			expectError:     false,
		},
		{
			name:            "empty list",
			notificationIDs: []uuid.UUID{},
			userID:          uuid.New(),
			expectError:     false,
		},
		{
			name:            "invalid user ID",
			notificationIDs: []uuid.UUID{uuid.New()},
			userID:          uuid.Nil,
			expectError:     true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			var err error
			if tt.userID == uuid.Nil {
				err = &ValidationError{Field: "user ID", Message: "required"}
			}

			if tt.expectError && err == nil {
				t.Error("Expected error, got nil")
			}
			if !tt.expectError && err != nil {
				t.Errorf("Expected no error, got %v", err)
			}
		})
	}
}

// TestNotificationService_GetUnreadCount tests getting unread count
func TestNotificationService_GetUnreadCount(t *testing.T) {
	tests := []struct {
		name          string
		userID        uuid.UUID
		expectError   bool
	}{
		{
			name:        "get count for valid user",
			userID:      uuid.New(),
			expectError: false,
		},
		{
			name:        "invalid user ID",
			userID:      uuid.Nil,
			expectError: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			var err error
			if tt.userID == uuid.Nil {
				err = &ValidationError{Field: "user ID", Message: "required"}
			}

			if tt.expectError && err == nil {
				t.Error("Expected error, got nil")
			}
			if !tt.expectError && err != nil {
				t.Errorf("Expected no error, got %v", err)
			}
		})
	}
}

// TestNotificationService_NotificationPriority tests notification priority
func TestNotificationService_NotificationPriority(t *testing.T) {
	tests := []struct {
		name         string
		notifType    NotificationType
		expectedPrio string
	}{
		{
			name:         "consent required - high priority",
			notifType:    NotificationTypeConsentRequired,
			expectedPrio: "high",
		},
		{
			name:         "account suspended - critical",
			notifType:    NotificationTypeAccountSuspended,
			expectedPrio: "critical",
		},
		{
			name:         "version published - normal",
			notifType:    NotificationTypeVersionPublished,
			expectedPrio: "normal",
		},
		{
			name:         "general - low",
			notifType:    NotificationTypeGeneral,
			expectedPrio: "low",
		},
	}

	priorityMap := map[NotificationType]string{
		NotificationTypeConsentRequired:  "high",
		NotificationTypeConsentReminder:  "high",
		NotificationTypeAccountSuspended: "critical",
		NotificationTypeAccountRestored:  "normal",
		NotificationTypeVersionPublished: "normal",
		NotificationTypeVersionApproved:  "normal",
		NotificationTypeVersionRejected:  "normal",
		NotificationTypeGeneral:          "low",
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			priority := priorityMap[tt.notifType]

			if priority != tt.expectedPrio {
				t.Errorf("Expected priority %s, got %s", tt.expectedPrio, priority)
			}
		})
	}
}

// TestNotificationService_ReminderFrequency tests reminder frequency logic
func TestNotificationService_ReminderFrequency(t *testing.T) {
	now := time.Now()

	tests := []struct {
		name            string
		frequency       string
		lastReminder    time.Time
		shouldSend      bool
	}{
		{
			name:         "daily - last sent yesterday",
			frequency:    "daily",
			lastReminder: now.AddDate(0, 0, -1),
			shouldSend:   true,
		},
		{
			name:         "daily - last sent today",
			frequency:    "daily",
			lastReminder: now.Add(-1 * time.Hour),
			shouldSend:   false,
		},
		{
			name:         "weekly - last sent 8 days ago",
			frequency:    "weekly",
			lastReminder: now.AddDate(0, 0, -8),
			shouldSend:   true,
		},
		{
			name:         "weekly - last sent 5 days ago",
			frequency:    "weekly",
			lastReminder: now.AddDate(0, 0, -5),
			shouldSend:   false,
		},
		{
			name:         "never - should not send",
			frequency:    "never",
			lastReminder: now.AddDate(0, 0, -30),
			shouldSend:   false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			var shouldSend bool

			switch tt.frequency {
			case "daily":
				daysSince := int(now.Sub(tt.lastReminder).Hours() / 24)
				shouldSend = daysSince >= 1
			case "weekly":
				daysSince := int(now.Sub(tt.lastReminder).Hours() / 24)
				shouldSend = daysSince >= 7
			case "never":
				shouldSend = false
			}

			if shouldSend != tt.shouldSend {
				t.Errorf("Expected shouldSend=%v, got %v", tt.shouldSend, shouldSend)
			}
		})
	}
}
