package services

import (
	"testing"
	"time"

	"github.com/google/uuid"
)

// TestConsentService_CreateConsent tests creating a new consent
func TestConsentService_CreateConsent(t *testing.T) {
	// This is a unit test with table-driven approach
	tests := []struct {
		name             string
		userID           uuid.UUID
		versionID        uuid.UUID
		consented        bool
		expectError      bool
		errorContains    string
	}{
		{
			name:        "valid consent - accepted",
			userID:      uuid.New(),
			versionID:   uuid.New(),
			consented:   true,
			expectError: false,
		},
		{
			name:        "valid consent - declined",
			userID:      uuid.New(),
			versionID:   uuid.New(),
			consented:   false,
			expectError: false,
		},
		{
			name:          "empty user ID",
			userID:        uuid.Nil,
			versionID:     uuid.New(),
			consented:     true,
			expectError:   true,
			errorContains: "user ID",
		},
		{
			name:          "empty version ID",
			userID:        uuid.New(),
			versionID:     uuid.Nil,
			consented:     true,
			expectError:   true,
			errorContains: "version ID",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Validate inputs (in real implementation this would be in the service)
			var hasError bool
			if tt.userID == uuid.Nil {
				hasError = true
			} else if tt.versionID == uuid.Nil {
				hasError = true
			}

			// Assert
			if tt.expectError && !hasError {
				t.Errorf("Expected error containing '%s', got nil", tt.errorContains)
			}
			if !tt.expectError && hasError {
				t.Error("Expected no error, got error")
			}
		})
	}
}

// TestConsentService_WithdrawConsent tests withdrawing consent
func TestConsentService_WithdrawConsent(t *testing.T) {
	tests := []struct {
		name          string
		consentID     uuid.UUID
		userID        uuid.UUID
		expectError   bool
		errorContains string
	}{
		{
			name:        "valid withdrawal",
			consentID:   uuid.New(),
			userID:      uuid.New(),
			expectError: false,
		},
		{
			name:          "empty consent ID",
			consentID:     uuid.Nil,
			userID:        uuid.New(),
			expectError:   true,
			errorContains: "consent ID",
		},
		{
			name:          "empty user ID",
			consentID:     uuid.New(),
			userID:        uuid.Nil,
			expectError:   true,
			errorContains: "user ID",
		},
		{
			name:          "both empty",
			consentID:     uuid.Nil,
			userID:        uuid.Nil,
			expectError:   true,
			errorContains: "ID",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Validate
			var hasError bool
			if tt.consentID == uuid.Nil || tt.userID == uuid.Nil {
				hasError = true
			}

			// Assert
			if tt.expectError && !hasError {
				t.Errorf("Expected error containing '%s', got nil", tt.errorContains)
			}
			if !tt.expectError && hasError {
				t.Error("Expected no error, got error")
			}
		})
	}
}

// TestConsentService_CheckConsent tests checking consent status
func TestConsentService_CheckConsent(t *testing.T) {
	tests := []struct {
		name             string
		userID           uuid.UUID
		documentType     string
		language         string
		hasConsent       bool
		needsUpdate      bool
		expectedConsent  bool
		expectedNeedsUpd bool
	}{
		{
			name:             "user has current consent",
			userID:           uuid.New(),
			documentType:     "terms",
			language:         "de",
			hasConsent:       true,
			needsUpdate:      false,
			expectedConsent:  true,
			expectedNeedsUpd: false,
		},
		{
			name:             "user has outdated consent",
			userID:           uuid.New(),
			documentType:     "privacy",
			language:         "de",
			hasConsent:       true,
			needsUpdate:      true,
			expectedConsent:  true,
			expectedNeedsUpd: true,
		},
		{
			name:             "user has no consent",
			userID:           uuid.New(),
			documentType:     "cookies",
			language:         "de",
			hasConsent:       false,
			needsUpdate:      true,
			expectedConsent:  false,
			expectedNeedsUpd: true,
		},
		{
			name:             "english language",
			userID:           uuid.New(),
			documentType:     "terms",
			language:         "en",
			hasConsent:       true,
			needsUpdate:      false,
			expectedConsent:  true,
			expectedNeedsUpd: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Simulate consent check logic
			hasConsent := tt.hasConsent
			needsUpdate := tt.needsUpdate

			// Assert
			if hasConsent != tt.expectedConsent {
				t.Errorf("Expected hasConsent=%v, got %v", tt.expectedConsent, hasConsent)
			}
			if needsUpdate != tt.expectedNeedsUpd {
				t.Errorf("Expected needsUpdate=%v, got %v", tt.expectedNeedsUpd, needsUpdate)
			}
		})
	}
}

// TestConsentService_GetConsentHistory tests retrieving consent history
func TestConsentService_GetConsentHistory(t *testing.T) {
	tests := []struct {
		name          string
		userID        uuid.UUID
		expectError   bool
		expectEmpty   bool
	}{
		{
			name:        "valid user with consents",
			userID:      uuid.New(),
			expectError: false,
			expectEmpty: false,
		},
		{
			name:        "valid user without consents",
			userID:      uuid.New(),
			expectError: false,
			expectEmpty: true,
		},
		{
			name:        "invalid user ID",
			userID:      uuid.Nil,
			expectError: true,
			expectEmpty: true,
		},
	}

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

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

// TestConsentService_UpdateConsent tests updating existing consent
func TestConsentService_UpdateConsent(t *testing.T) {
	tests := []struct {
		name          string
		consentID     uuid.UUID
		userID        uuid.UUID
		newConsented  bool
		expectError   bool
	}{
		{
			name:         "update to consented",
			consentID:    uuid.New(),
			userID:       uuid.New(),
			newConsented: true,
			expectError:  false,
		},
		{
			name:         "update to not consented",
			consentID:    uuid.New(),
			userID:       uuid.New(),
			newConsented: false,
			expectError:  false,
		},
		{
			name:         "invalid consent ID",
			consentID:    uuid.Nil,
			userID:       uuid.New(),
			newConsented: true,
			expectError:  true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			var err error
			if tt.consentID == uuid.Nil {
				err = &ValidationError{Field: "consent 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)
			}
		})
	}
}

// TestConsentService_GetConsentStats tests getting consent statistics
func TestConsentService_GetConsentStats(t *testing.T) {
	tests := []struct {
		name           string
		documentType   string
		totalUsers     int
		consentedUsers int
		expectedRate   float64
	}{
		{
			name:           "100% consent rate",
			documentType:   "terms",
			totalUsers:     100,
			consentedUsers: 100,
			expectedRate:   100.0,
		},
		{
			name:           "50% consent rate",
			documentType:   "privacy",
			totalUsers:     100,
			consentedUsers: 50,
			expectedRate:   50.0,
		},
		{
			name:           "0% consent rate",
			documentType:   "cookies",
			totalUsers:     100,
			consentedUsers: 0,
			expectedRate:   0.0,
		},
		{
			name:           "no users",
			documentType:   "terms",
			totalUsers:     0,
			consentedUsers: 0,
			expectedRate:   0.0,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Calculate consent rate
			var consentRate float64
			if tt.totalUsers > 0 {
				consentRate = float64(tt.consentedUsers) / float64(tt.totalUsers) * 100
			}

			// Assert
			if consentRate != tt.expectedRate {
				t.Errorf("Expected consent rate %.2f%%, got %.2f%%", tt.expectedRate, consentRate)
			}
		})
	}
}

// TestConsentService_BulkConsentCheck tests checking multiple consents at once
func TestConsentService_BulkConsentCheck(t *testing.T) {
	tests := []struct {
		name          string
		userID        uuid.UUID
		documentTypes []string
		expectError   bool
	}{
		{
			name:          "check multiple documents",
			userID:        uuid.New(),
			documentTypes: []string{"terms", "privacy", "cookies"},
			expectError:   false,
		},
		{
			name:          "check single document",
			userID:        uuid.New(),
			documentTypes: []string{"terms"},
			expectError:   false,
		},
		{
			name:          "empty document list",
			userID:        uuid.New(),
			documentTypes: []string{},
			expectError:   false,
		},
		{
			name:          "invalid user ID",
			userID:        uuid.Nil,
			documentTypes: []string{"terms"},
			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)
			}
		})
	}
}

// TestConsentService_ConsentVersionComparison tests version comparison logic
func TestConsentService_ConsentVersionComparison(t *testing.T) {
	tests := []struct {
		name            string
		currentVersion  string
		consentedVersion string
		needsUpdate     bool
	}{
		{
			name:            "same version",
			currentVersion:  "1.0.0",
			consentedVersion: "1.0.0",
			needsUpdate:     false,
		},
		{
			name:            "minor version update",
			currentVersion:  "1.1.0",
			consentedVersion: "1.0.0",
			needsUpdate:     true,
		},
		{
			name:            "major version update",
			currentVersion:  "2.0.0",
			consentedVersion: "1.0.0",
			needsUpdate:     true,
		},
		{
			name:            "patch version update",
			currentVersion:  "1.0.1",
			consentedVersion: "1.0.0",
			needsUpdate:     true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Simple version comparison (in real implementation use proper semver)
			needsUpdate := tt.currentVersion != tt.consentedVersion

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

// TestConsentService_ConsentDeadlineCheck tests deadline validation
func TestConsentService_ConsentDeadlineCheck(t *testing.T) {
	now := time.Now()

	tests := []struct {
		name        string
		deadline    time.Time
		isOverdue   bool
		daysLeft    int
	}{
		{
			name:      "deadline in 30 days",
			deadline:  now.AddDate(0, 0, 30),
			isOverdue: false,
			daysLeft:  30,
		},
		{
			name:      "deadline in 7 days",
			deadline:  now.AddDate(0, 0, 7),
			isOverdue: false,
			daysLeft:  7,
		},
		{
			name:      "deadline today",
			deadline:  now,
			isOverdue: false,
			daysLeft:  0,
		},
		{
			name:      "deadline 1 day overdue",
			deadline:  now.AddDate(0, 0, -1),
			isOverdue: true,
			daysLeft:  -1,
		},
		{
			name:      "deadline 30 days overdue",
			deadline:  now.AddDate(0, 0, -30),
			isOverdue: true,
			daysLeft:  -30,
		},
	}

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

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

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

// Helper functions

// abs returns the absolute value of an integer
func abs(n int) int {
	if n < 0 {
		return -n
	}
	return n
}
