package services

import (
	"testing"
	"time"

	"github.com/google/uuid"
)

// TestDeadlineService_CreateDeadline tests creating consent deadlines
func TestDeadlineService_CreateDeadline(t *testing.T) {
	tests := []struct {
		name        string
		userID      uuid.UUID
		versionID   uuid.UUID
		deadlineAt  time.Time
		expectError bool
	}{
		{
			name:        "valid deadline - 30 days",
			userID:      uuid.New(),
			versionID:   uuid.New(),
			deadlineAt:  time.Now().AddDate(0, 0, 30),
			expectError: false,
		},
		{
			name:        "valid deadline - 14 days",
			userID:      uuid.New(),
			versionID:   uuid.New(),
			deadlineAt:  time.Now().AddDate(0, 0, 14),
			expectError: false,
		},
		{
			name:        "invalid user ID",
			userID:      uuid.Nil,
			versionID:   uuid.New(),
			deadlineAt:  time.Now().AddDate(0, 0, 30),
			expectError: true,
		},
		{
			name:        "invalid version ID",
			userID:      uuid.New(),
			versionID:   uuid.Nil,
			deadlineAt:  time.Now().AddDate(0, 0, 30),
			expectError: true,
		},
		{
			name:        "deadline in past",
			userID:      uuid.New(),
			versionID:   uuid.New(),
			deadlineAt:  time.Now().AddDate(0, 0, -1),
			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.versionID == uuid.Nil {
				err = &ValidationError{Field: "version ID", Message: "required"}
			} else if tt.deadlineAt.Before(time.Now()) {
				err = &ValidationError{Field: "deadline", Message: "must be in the future"}
			}

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

// TestDeadlineService_CheckDeadlineStatus tests deadline status checking
func TestDeadlineService_CheckDeadlineStatus(t *testing.T) {
	now := time.Now()

	tests := []struct {
		name       string
		deadlineAt time.Time
		isOverdue  bool
		daysLeft   int
		urgency    string
	}{
		{
			name:       "30 days left",
			deadlineAt: now.AddDate(0, 0, 30),
			isOverdue:  false,
			daysLeft:   30,
			urgency:    "normal",
		},
		{
			name:       "7 days left - warning",
			deadlineAt: now.AddDate(0, 0, 7),
			isOverdue:  false,
			daysLeft:   7,
			urgency:    "warning",
		},
		{
			name:       "3 days left - urgent",
			deadlineAt: now.AddDate(0, 0, 3),
			isOverdue:  false,
			daysLeft:   3,
			urgency:    "urgent",
		},
		{
			name:       "1 day left - critical",
			deadlineAt: now.AddDate(0, 0, 1),
			isOverdue:  false,
			daysLeft:   1,
			urgency:    "critical",
		},
		{
			name:       "overdue by 1 day",
			deadlineAt: now.AddDate(0, 0, -1),
			isOverdue:  true,
			daysLeft:   -1,
			urgency:    "overdue",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			isOverdue := tt.deadlineAt.Before(now)
			daysLeft := int(tt.deadlineAt.Sub(now).Hours() / 24)

			var urgency string
			if isOverdue {
				urgency = "overdue"
			} else if daysLeft <= 1 {
				urgency = "critical"
			} else if daysLeft <= 3 {
				urgency = "urgent"
			} else if daysLeft <= 7 {
				urgency = "warning"
			} else {
				urgency = "normal"
			}

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

			if abs(daysLeft-tt.daysLeft) > 1 { // Allow 1 day difference
				t.Errorf("Expected daysLeft=%d, got %d", tt.daysLeft, daysLeft)
			}

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

// TestDeadlineService_SendReminders tests reminder scheduling
func TestDeadlineService_SendReminders(t *testing.T) {
	now := time.Now()

	tests := []struct {
		name           string
		deadlineAt     time.Time
		lastReminderAt *time.Time
		reminderCount  int
		shouldSend     bool
		nextReminder   int // days before deadline
	}{
		{
			name:           "first reminder - 14 days before",
			deadlineAt:     now.AddDate(0, 0, 14),
			lastReminderAt: nil,
			reminderCount:  0,
			shouldSend:     true,
			nextReminder:   14,
		},
		{
			name:           "second reminder - 7 days before",
			deadlineAt:     now.AddDate(0, 0, 7),
			lastReminderAt: ptrTime(now.AddDate(0, 0, -7)),
			reminderCount:  1,
			shouldSend:     true,
			nextReminder:   7,
		},
		{
			name:           "third reminder - 3 days before",
			deadlineAt:     now.AddDate(0, 0, 3),
			lastReminderAt: ptrTime(now.AddDate(0, 0, -4)),
			reminderCount:  2,
			shouldSend:     true,
			nextReminder:   3,
		},
		{
			name:           "final reminder - 1 day before",
			deadlineAt:     now.AddDate(0, 0, 1),
			lastReminderAt: ptrTime(now.AddDate(0, 0, -2)),
			reminderCount:  3,
			shouldSend:     true,
			nextReminder:   1,
		},
		{
			name:           "too soon for next reminder",
			deadlineAt:     now.AddDate(0, 0, 10),
			lastReminderAt: ptrTime(now.AddDate(0, 0, -1)),
			reminderCount:  1,
			shouldSend:     false,
			nextReminder:   0,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			daysUntilDeadline := int(tt.deadlineAt.Sub(now).Hours() / 24)

			// Reminder schedule: 14, 7, 3, 1 days before deadline
			reminderDays := []int{14, 7, 3, 1}
			shouldSend := false

			for _, day := range reminderDays {
				if daysUntilDeadline == day {
					// Check if enough time passed since last reminder
					if tt.lastReminderAt == nil || now.Sub(*tt.lastReminderAt) > 12*time.Hour {
						shouldSend = true
						break
					}
				}
			}

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

// TestDeadlineService_SuspendAccount tests account suspension logic
func TestDeadlineService_SuspendAccount(t *testing.T) {
	tests := []struct {
		name          string
		userID        uuid.UUID
		reason        string
		shouldSuspend bool
		expectError   bool
	}{
		{
			name:          "suspend for missed deadline",
			userID:        uuid.New(),
			reason:        "consent_deadline_exceeded",
			shouldSuspend: true,
			expectError:   false,
		},
		{
			name:          "invalid user ID",
			userID:        uuid.Nil,
			reason:        "consent_deadline_exceeded",
			shouldSuspend: false,
			expectError:   true,
		},
		{
			name:          "invalid reason",
			userID:        uuid.New(),
			reason:        "",
			shouldSuspend: false,
			expectError:   true,
		},
	}

	validReasons := map[string]bool{
		"consent_deadline_exceeded": true,
		"mandatory_consent_missing": 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 !validReasons[tt.reason] && tt.reason != "" {
				err = &ValidationError{Field: "reason", Message: "invalid suspension reason"}
			} else if tt.reason == "" {
				err = &ValidationError{Field: "reason", 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)
			}
		})
	}
}

// TestDeadlineService_LiftSuspension tests lifting account suspension
func TestDeadlineService_LiftSuspension(t *testing.T) {
	tests := []struct {
		name        string
		userID      uuid.UUID
		adminID     uuid.UUID
		reason      string
		expectError bool
	}{
		{
			name:        "lift valid suspension",
			userID:      uuid.New(),
			adminID:     uuid.New(),
			reason:      "consent provided",
			expectError: false,
		},
		{
			name:        "invalid user ID",
			userID:      uuid.Nil,
			adminID:     uuid.New(),
			reason:      "consent provided",
			expectError: true,
		},
		{
			name:        "invalid admin ID",
			userID:      uuid.New(),
			adminID:     uuid.Nil,
			reason:      "consent provided",
			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.adminID == uuid.Nil {
				err = &ValidationError{Field: "admin 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)
			}
		})
	}
}

// TestDeadlineService_GetOverdueDeadlines tests finding overdue deadlines
func TestDeadlineService_GetOverdueDeadlines(t *testing.T) {
	now := time.Now()

	tests := []struct {
		name      string
		deadlines []time.Time
		expected  int // number of overdue
	}{
		{
			name: "no overdue deadlines",
			deadlines: []time.Time{
				now.AddDate(0, 0, 1),
				now.AddDate(0, 0, 7),
				now.AddDate(0, 0, 30),
			},
			expected: 0,
		},
		{
			name: "some overdue",
			deadlines: []time.Time{
				now.AddDate(0, 0, -1),
				now.AddDate(0, 0, -5),
				now.AddDate(0, 0, 7),
			},
			expected: 2,
		},
		{
			name: "all overdue",
			deadlines: []time.Time{
				now.AddDate(0, 0, -1),
				now.AddDate(0, 0, -7),
				now.AddDate(0, 0, -30),
			},
			expected: 3,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			overdueCount := 0
			for _, deadline := range tt.deadlines {
				if deadline.Before(now) {
					overdueCount++
				}
			}

			if overdueCount != tt.expected {
				t.Errorf("Expected %d overdue, got %d", tt.expected, overdueCount)
			}
		})
	}
}

// TestDeadlineService_ProcessScheduledTasks tests scheduled task processing
func TestDeadlineService_ProcessScheduledTasks(t *testing.T) {
	now := time.Now()

	tests := []struct {
		name           string
		task           string
		scheduledAt    time.Time
		shouldProcess  bool
	}{
		{
			name:          "process due task",
			task:          "send_reminder",
			scheduledAt:   now.Add(-1 * time.Hour),
			shouldProcess: true,
		},
		{
			name:          "skip future task",
			task:          "send_reminder",
			scheduledAt:   now.Add(1 * time.Hour),
			shouldProcess: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			shouldProcess := tt.scheduledAt.Before(now) || tt.scheduledAt.Equal(now)

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

// Helper functions

func ptrTime(t time.Time) *time.Time {
	return &t
}
