package services

import (
	"context"
	"testing"
	"time"

	"go.uber.org/zap"
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
)

func TestNewAnalyticsAggregationService(t *testing.T) {
	db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
	if err != nil {
		t.Fatalf("Failed to open database: %v", err)
	}

	logger := zap.NewNop()
	service := NewAnalyticsAggregationService(db, logger)

	if service == nil {
		t.Error("NewAnalyticsAggregationService() returned nil")
	}
	if service.db == nil {
		t.Error("NewAnalyticsAggregationService() returned service with nil db")
	}
	if service.logger == nil {
		t.Error("NewAnalyticsAggregationService() returned service with nil logger")
	}
}

func TestAnalyticsAggregationPeriod_Constants(t *testing.T) {
	tests := []struct {
		name     string
		period   AnalyticsAggregationPeriod
		expected string
	}{
		{
			name:     "hour period",
			period:   AnalyticsPeriodHour,
			expected: "hour",
		},
		{
			name:     "day period",
			period:   AnalyticsPeriodDay,
			expected: "day",
		},
		{
			name:     "week period",
			period:   AnalyticsPeriodWeek,
			expected: "week",
		},
		{
			name:     "month period",
			period:   AnalyticsPeriodMonth,
			expected: "month",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if string(tt.period) != tt.expected {
				t.Errorf("Period = %v, want %v", tt.period, tt.expected)
			}
		})
	}
}

func TestEventAggregationParams_Validation(t *testing.T) {
	now := time.Now()
	params := EventAggregationParams{
		StartDate: now.Add(-7 * 24 * time.Hour),
		EndDate:   now,
		Period:    AnalyticsPeriodDay,
	}

	// Test that invalid period would be caught in AggregateEvents
	invalidPeriod := AnalyticsAggregationPeriod("invalid")
	params.Period = invalidPeriod

	// This would fail validation in AggregateEvents
	if params.Period == AnalyticsPeriodDay || params.Period == AnalyticsPeriodWeek ||
		params.Period == AnalyticsPeriodMonth || params.Period == AnalyticsPeriodHour {
		t.Error("Invalid period should not match valid periods")
	}
}

func TestMin(t *testing.T) {
	tests := []struct {
		name     string
		a        int
		b        int
		expected int
	}{
		{
			name:     "a < b",
			a:        5,
			b:        10,
			expected: 5,
		},
		{
			name:     "a > b",
			a:        10,
			b:        5,
			expected: 5,
		},
		{
			name:     "a == b",
			a:        5,
			b:        5,
			expected: 5,
		},
		{
			name:     "negative values",
			a:        -5,
			b:        -10,
			expected: -10,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := min(tt.a, tt.b)
			if result != tt.expected {
				t.Errorf("min(%d, %d) = %d, want %d", tt.a, tt.b, result, tt.expected)
			}
		})
	}
}

func TestGetEventCounts_EmptyParams(t *testing.T) {
	db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
	if err != nil {
		t.Fatalf("Failed to open database: %v", err)
	}

	service := NewAnalyticsAggregationService(db, zap.NewNop())

	ctx := context.Background()
	now := time.Now()
	counts, err := service.GetEventCounts(ctx, now.Add(-7*24*time.Hour), now, []string{})
	if err != nil {
		// Expected to fail with empty database, but should not panic
		if counts != nil {
			t.Error("GetEventCounts() should return nil counts on error")
		}
	}
}

func TestGetTopEvents_EmptyParams(t *testing.T) {
	db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
	if err != nil {
		t.Fatalf("Failed to open database: %v", err)
	}

	service := NewAnalyticsAggregationService(db, zap.NewNop())

	ctx := context.Background()
	now := time.Now()
	results, err := service.GetTopEvents(ctx, now.Add(-7*24*time.Hour), now, 10)
	if err != nil {
		// Expected to fail with empty database, but should not panic
		if results != nil {
			t.Error("GetTopEvents() should return nil results on error")
		}
	} else {
		// If no error, results should be empty slice, not nil
		if results == nil {
			t.Error("GetTopEvents() should return empty slice, not nil")
		}
	}
}

// Note: Full integration tests would require:
// 1. A real PostgreSQL database with analytics_events table
// 2. Test data (analytics events with various event names, user IDs, payloads)
// 3. Verification of aggregation queries
// 4. Performance testing with large datasets
//
// Example integration test structure:
// func TestAnalyticsAggregationService_AggregateEvents_Integration(t *testing.T) {
//     // Setup test database
//     db := setupTestDB(t)
//     defer cleanupTestDB(t, db)
//
//     // Create test data
//     userID1 := uuid.New()
//     userID2 := uuid.New()
//     createTestAnalyticsEvents(t, db, []AnalyticsEvent{
//         {EventName: "track_play", UserID: &userID1, Payload: `{"track_id": "123"}`},
//         {EventName: "track_play", UserID: &userID2, Payload: `{"track_id": "123"}`},
//         {EventName: "user_login", UserID: &userID1, Payload: `{"ip": "1.2.3.4"}`},
//     })
//
//     service := NewAnalyticsAggregationService(db, zap.NewNop())
//
//     ctx := context.Background()
//     params := EventAggregationParams{
//         StartDate: time.Now().Add(-7 * 24 * time.Hour),
//         EndDate:   time.Now(),
//         Period:    AnalyticsPeriodDay,
//     }
//
//     summary, err := service.AggregateEvents(ctx, params)
//     if err != nil {
//         t.Fatalf("AggregateEvents() error = %v", err)
//     }
//
//     if summary.TotalEvents == 0 {
//         t.Error("AggregateEvents() returned zero total events")
//     }
//
//     if len(summary.Results) == 0 {
//         t.Error("AggregateEvents() returned no results")
//     }
// }
