package services

import (
	"context"
	"testing"
	"time"

	"github.com/google/uuid"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"go.uber.org/zap/zaptest"
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"

	"veza-backend-api/internal/models"
)

func setupTestPlaybackAggregationServiceDB(t *testing.T) *gorm.DB {
	db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
	require.NoError(t, err)

	db.Exec("PRAGMA foreign_keys = ON")
	err = db.AutoMigrate(&models.User{}, &models.Track{}, &models.PlaybackAnalytics{})
	require.NoError(t, err)

	return db
}

func TestNewPlaybackAggregationService(t *testing.T) {
	db := setupTestPlaybackAggregationServiceDB(t)
	logger := zaptest.NewLogger(t)

	service := NewPlaybackAggregationService(db, logger)

	assert.NotNil(t, service)
	assert.Equal(t, db, service.db)
}

func TestPlaybackAggregationService_AggregateByPeriod_Day(t *testing.T) {
	db := setupTestPlaybackAggregationServiceDB(t)
	logger := zaptest.NewLogger(t)
	service := NewPlaybackAggregationService(db, logger)

	// Créer test user et track
	userID := uuid.New()
	user := &models.User{ID: userID, Username: "testuser", Email: "test@example.com", IsActive: true}
	db.Create(user)

	trackID := uuid.New()
	track := &models.Track{
		ID:       trackID,
		UserID:   userID,
		Title:    "Test Track",
		FilePath: "/test.mp3",
		FileSize: 1024,
		Format:   "MP3",
		Duration: 180,
		IsPublic: true,
		Status:   models.TrackStatusCompleted,
	}
	db.Create(track)

	// Créer des sessions sur différentes dates
	now := time.Now()
	sessions := []models.PlaybackAnalytics{
		{
			TrackID:        trackID,
			UserID:         userID,
			PlayTime:       120,
			PauseCount:     2,
			SeekCount:      3,
			CompletionRate: 75.0,
			StartedAt:      now.AddDate(0, 0, -2),
			CreatedAt:      now.AddDate(0, 0, -2),
		},
		{
			TrackID:        trackID,
			UserID:         userID,
			PlayTime:       150,
			PauseCount:     1,
			SeekCount:      2,
			CompletionRate: 90.0,
			StartedAt:      now.AddDate(0, 0, -2),
			CreatedAt:      now.AddDate(0, 0, -2),
		},
		{
			TrackID:        trackID,
			UserID:         userID,
			PlayTime:       100,
			PauseCount:     3,
			SeekCount:      1,
			CompletionRate: 60.0,
			StartedAt:      now.AddDate(0, 0, -1),
			CreatedAt:      now.AddDate(0, 0, -1),
		},
	}
	for _, session := range sessions {
		db.Create(&session)
	}

	startDate := now.AddDate(0, 0, -3)
	endDate := now

	result, err := service.AggregateByPeriod(context.Background(), trackID, PeriodDay, startDate, endDate)

	require.NoError(t, err)
	assert.NotNil(t, result)
	assert.Equal(t, int64(3), result.TotalSessions)
	assert.Equal(t, int64(370), result.TotalPlayTime)
	assert.InDelta(t, 123.33, result.AveragePlayTime, 0.1)

	// Vérifier qu'il y a 2 périodes (2 jours différents)
	assert.Len(t, result.Periods, 2)

	// Vérifier la première période (jour -2)
	period1 := result.Periods[0]
	assert.Equal(t, int64(2), period1.Sessions)
	assert.Equal(t, int64(270), period1.TotalPlayTime)
	assert.InDelta(t, 135.0, period1.AveragePlayTime, 0.1)
}

func TestPlaybackAggregationService_AggregateByPeriod_Week(t *testing.T) {
	db := setupTestPlaybackAggregationServiceDB(t)
	logger := zaptest.NewLogger(t)
	service := NewPlaybackAggregationService(db, logger)

	user := &models.User{ID: uuid.New(), Username: "testuser", Email: "test@example.com", IsActive: true}
	db.Create(user)
	track := &models.Track{
		ID:       uuid.New(),
		UserID:   user.ID,
		Title:    "Test Track",
		FilePath: "/test.mp3",
		FileSize: 1024,
		Format:   "MP3",
		Duration: 180,
		IsPublic: true,
		Status:   models.TrackStatusCompleted,
	}
	db.Create(track)

	now := time.Now()
	startDate := now.AddDate(0, 0, -14)
	endDate := now

	// Créer des sessions dans différentes semaines
	sessions := []models.PlaybackAnalytics{
		{
			TrackID:        track.ID,
			UserID:         user.ID,
			PlayTime:       120,
			PauseCount:     2,
			SeekCount:      3,
			CompletionRate: 75.0,
			StartedAt:      startDate.AddDate(0, 0, 1),
			CreatedAt:      startDate.AddDate(0, 0, 1),
		},
		{
			TrackID:        track.ID,
			UserID:         user.ID,
			PlayTime:       150,
			PauseCount:     1,
			SeekCount:      2,
			CompletionRate: 90.0,
			StartedAt:      startDate.AddDate(0, 0, 8),
			CreatedAt:      startDate.AddDate(0, 0, 8),
		},
	}
	for _, session := range sessions {
		db.Create(&session)
	}

	result, err := service.AggregateByPeriod(context.Background(), track.ID, PeriodWeek, startDate, endDate)

	require.NoError(t, err)
	assert.NotNil(t, result)
	assert.Equal(t, int64(2), result.TotalSessions)
}

func TestPlaybackAggregationService_AggregateByPeriod_Month(t *testing.T) {
	db := setupTestPlaybackAggregationServiceDB(t)
	logger := zaptest.NewLogger(t)
	service := NewPlaybackAggregationService(db, logger)

	user := &models.User{ID: uuid.New(), Username: "testuser", Email: "test@example.com", IsActive: true}
	db.Create(user)
	track := &models.Track{
		ID:       uuid.New(),
		UserID:   user.ID,
		Title:    "Test Track",
		FilePath: "/test.mp3",
		FileSize: 1024,
		Format:   "MP3",
		Duration: 180,
		IsPublic: true,
		Status:   models.TrackStatusCompleted,
	}
	db.Create(track)

	now := time.Now()
	startDate := now.AddDate(0, -2, 0)
	endDate := now

	// Créer des sessions dans différents mois
	sessions := []models.PlaybackAnalytics{
		{
			TrackID:        track.ID,
			UserID:         user.ID,
			PlayTime:       120,
			PauseCount:     2,
			SeekCount:      3,
			CompletionRate: 75.0,
			StartedAt:      startDate.AddDate(0, 0, 1),
			CreatedAt:      startDate.AddDate(0, 0, 1),
		},
		{
			TrackID:        track.ID,
			UserID:         user.ID,
			PlayTime:       150,
			PauseCount:     1,
			SeekCount:      2,
			CompletionRate: 90.0,
			StartedAt:      startDate.AddDate(0, 1, 0),
			CreatedAt:      startDate.AddDate(0, 1, 0),
		},
	}
	for _, session := range sessions {
		db.Create(&session)
	}

	result, err := service.AggregateByPeriod(context.Background(), track.ID, PeriodMonth, startDate, endDate)

	require.NoError(t, err)
	assert.NotNil(t, result)
	assert.Equal(t, int64(2), result.TotalSessions)
}

func TestPlaybackAggregationService_AggregateByPeriod_InvalidTrackID(t *testing.T) {
	db := setupTestPlaybackAggregationServiceDB(t)
	logger := zaptest.NewLogger(t)
	service := NewPlaybackAggregationService(db, logger)

	now := time.Now()
	startDate := now.AddDate(0, 0, -7)
	endDate := now

	_, err := service.AggregateByPeriod(context.Background(), uuid.Nil, PeriodDay, startDate, endDate)
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "invalid track ID")
}

func TestPlaybackAggregationService_AggregateByPeriod_TrackNotFound(t *testing.T) {
	db := setupTestPlaybackAggregationServiceDB(t)
	logger := zaptest.NewLogger(t)
	service := NewPlaybackAggregationService(db, logger)

	now := time.Now()
	startDate := now.AddDate(0, 0, -7)
	endDate := now

	_, err := service.AggregateByPeriod(context.Background(), uuid.New(), PeriodDay, startDate, endDate)
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "track not found")
}

func TestPlaybackAggregationService_AggregateByPeriod_InvalidPeriod(t *testing.T) {
	db := setupTestPlaybackAggregationServiceDB(t)
	logger := zaptest.NewLogger(t)
	service := NewPlaybackAggregationService(db, logger)

	user := &models.User{ID: uuid.New(), Username: "testuser", Email: "test@example.com", IsActive: true}
	db.Create(user)
	track := &models.Track{
		ID:       uuid.New(),
		UserID:   user.ID,
		Title:    "Test Track",
		FilePath: "/test.mp3",
		FileSize: 1024,
		Format:   "MP3",
		Duration: 180,
		IsPublic: true,
		Status:   models.TrackStatusCompleted,
	}
	db.Create(track)

	now := time.Now()
	startDate := now.AddDate(0, 0, -7)
	endDate := now

	_, err := service.AggregateByPeriod(context.Background(), track.ID, PeriodType("invalid"), startDate, endDate)
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "invalid period type")
}

func TestPlaybackAggregationService_AggregateByPeriod_NoData(t *testing.T) {
	db := setupTestPlaybackAggregationServiceDB(t)
	logger := zaptest.NewLogger(t)
	service := NewPlaybackAggregationService(db, logger)

	user := &models.User{ID: uuid.New(), Username: "testuser", Email: "test@example.com", IsActive: true}
	db.Create(user)
	track := &models.Track{
		ID:       uuid.New(),
		UserID:   user.ID,
		Title:    "Test Track",
		FilePath: "/test.mp3",
		FileSize: 1024,
		Format:   "MP3",
		Duration: 180,
		IsPublic: true,
		Status:   models.TrackStatusCompleted,
	}
	db.Create(track)

	now := time.Now()
	startDate := now.AddDate(0, 0, -7)
	endDate := now

	result, err := service.AggregateByPeriod(context.Background(), track.ID, PeriodDay, startDate, endDate)

	require.NoError(t, err)
	assert.NotNil(t, result)
	assert.Equal(t, int64(0), result.TotalSessions)
	assert.Len(t, result.Periods, 0)
}

func TestPlaybackAggregationService_AggregateByPeriod_Trends(t *testing.T) {
	db := setupTestPlaybackAggregationServiceDB(t)
	logger := zaptest.NewLogger(t)
	service := NewPlaybackAggregationService(db, logger)

	user := &models.User{ID: uuid.New(), Username: "testuser", Email: "test@example.com", IsActive: true}
	db.Create(user)
	track := &models.Track{
		ID:       uuid.New(),
		UserID:   user.ID,
		Title:    "Test Track",
		FilePath: "/test.mp3",
		FileSize: 1024,
		Format:   "MP3",
		Duration: 180,
		IsPublic: true,
		Status:   models.TrackStatusCompleted,
	}
	db.Create(track)

	now := time.Now()
	// Créer des sessions avec des valeurs croissantes pour tester les tendances
	// Important: créer dans des jours différents pour avoir plusieurs périodes
	sessions := []models.PlaybackAnalytics{
		{
			TrackID:        track.ID,
			UserID:         user.ID,
			PlayTime:       100,
			PauseCount:     1,
			SeekCount:      1,
			CompletionRate: 50.0,
			StartedAt:      now.AddDate(0, 0, -3),
			CreatedAt:      now.AddDate(0, 0, -3),
		},
		{
			TrackID:        track.ID,
			UserID:         user.ID,
			PlayTime:       200,
			PauseCount:     2,
			SeekCount:      2,
			CompletionRate: 100.0,
			StartedAt:      now.AddDate(0, 0, -1),
			CreatedAt:      now.AddDate(0, 0, -1),
		},
	}
	for _, session := range sessions {
		db.Create(&session)
	}

	startDate := now.AddDate(0, 0, -4)
	endDate := now

	result, err := service.AggregateByPeriod(context.Background(), track.ID, PeriodDay, startDate, endDate)

	require.NoError(t, err)
	assert.NotNil(t, result)

	// Les tendances ne sont calculées que s'il y a au moins 2 périodes
	if len(result.Periods) >= 2 {
		assert.NotNil(t, result.Trends)

		// Vérifier que les tendances sont calculées (croissance attendue)
		if result.Trends != nil {
			// Les tendances peuvent être positives (croissance) ou négatives (décroissance)
			// On vérifie juste qu'elles sont calculées (non nulles si les valeurs changent)
			assert.NotNil(t, result.Trends.SessionsTrend)
			assert.NotNil(t, result.Trends.PlayTimeTrend)
			assert.NotNil(t, result.Trends.CompletionTrend)
		}
	} else {
		// Si moins de 2 périodes, les tendances ne sont pas calculées
		assert.Nil(t, result.Trends)
	}
}

func TestPlaybackAggregationService_AggregateByDateRange(t *testing.T) {
	db := setupTestPlaybackAggregationServiceDB(t)
	logger := zaptest.NewLogger(t)
	service := NewPlaybackAggregationService(db, logger)

	user := &models.User{ID: uuid.New(), Username: "testuser", Email: "test@example.com", IsActive: true}
	db.Create(user)
	track := &models.Track{
		ID:       uuid.New(),
		UserID:   user.ID,
		Title:    "Test Track",
		FilePath: "/test.mp3",
		FileSize: 1024,
		Format:   "MP3",
		Duration: 180,
		IsPublic: true,
		Status:   models.TrackStatusCompleted,
	}
	db.Create(track)

	now := time.Now()
	sessions := []models.PlaybackAnalytics{
		{
			TrackID:        track.ID,
			UserID:         user.ID,
			PlayTime:       120,
			PauseCount:     2,
			SeekCount:      3,
			CompletionRate: 75.0,
			StartedAt:      now.AddDate(0, 0, -2),
			CreatedAt:      now.AddDate(0, 0, -2),
		},
		{
			TrackID:        track.ID,
			UserID:         user.ID,
			PlayTime:       150,
			PauseCount:     1,
			SeekCount:      2,
			CompletionRate: 90.0,
			StartedAt:      now.AddDate(0, 0, -1),
			CreatedAt:      now.AddDate(0, 0, -1),
		},
	}
	for _, session := range sessions {
		db.Create(&session)
	}

	startDate := now.AddDate(0, 0, -3)
	endDate := now

	result, err := service.AggregateByDateRange(context.Background(), track.ID, startDate, endDate)

	require.NoError(t, err)
	assert.NotNil(t, result)
	assert.Equal(t, int64(2), result.Sessions)
	assert.Equal(t, int64(270), result.TotalPlayTime)
	assert.InDelta(t, 135.0, result.AveragePlayTime, 0.1)
	assert.InDelta(t, 82.5, result.AverageCompletion, 0.1)
}

func TestPlaybackAggregationService_GetTopTracksByPlayback(t *testing.T) {
	db := setupTestPlaybackAggregationServiceDB(t)
	logger := zaptest.NewLogger(t)
	service := NewPlaybackAggregationService(db, logger)

	userID := uuid.New()
	user := &models.User{ID: userID, Username: "testuser", Email: "test@example.com", IsActive: true}
	db.Create(user)

	// Créer plusieurs tracks
	track1ID := uuid.New()
	track2ID := uuid.New()
	tracks := []models.Track{
		{ID: track1ID, UserID: userID, Title: "Track 1", FilePath: "/1.mp3", FileSize: 1024, Format: "MP3", Duration: 180, IsPublic: true, Status: models.TrackStatusCompleted},
		{ID: track2ID, UserID: userID, Title: "Track 2", FilePath: "/2.mp3", FileSize: 1024, Format: "MP3", Duration: 180, IsPublic: true, Status: models.TrackStatusCompleted},
	}
	for _, track := range tracks {
		db.Create(&track)
	}

	now := time.Now()
	// Créer plus de sessions pour le track 1
	sessions := []models.PlaybackAnalytics{
		{TrackID: track1ID, UserID: userID, PlayTime: 120, CompletionRate: 75.0, StartedAt: now, CreatedAt: now},
		{TrackID: track1ID, UserID: userID, PlayTime: 150, CompletionRate: 90.0, StartedAt: now, CreatedAt: now},
		{TrackID: track2ID, UserID: userID, PlayTime: 100, CompletionRate: 60.0, StartedAt: now, CreatedAt: now},
	}
	for _, session := range sessions {
		db.Create(&session)
	}

	result, err := service.GetTopTracksByPlayback(context.Background(), 10, nil, nil)

	require.NoError(t, err)
	assert.NotNil(t, result)
	assert.Len(t, result, 2)

	// Vérifier que le track 1 est en premier (plus de sessions)
	// track_id is now uuid.UUID, not int64
	trackID, ok := result[0]["track_id"].(uuid.UUID)
	require.True(t, ok, "track_id should be uuid.UUID")
	assert.Equal(t, track1ID, trackID)
	assert.Equal(t, int64(2), result[0]["sessions"])
}

func TestPlaybackAggregationService_GetTopTracksByPlayback_WithDateRange(t *testing.T) {
	db := setupTestPlaybackAggregationServiceDB(t)
	logger := zaptest.NewLogger(t)
	service := NewPlaybackAggregationService(db, logger)

	user := &models.User{ID: uuid.New(), Username: "testuser", Email: "test@example.com", IsActive: true}
	db.Create(user)
	track := &models.Track{
		ID:       uuid.New(),
		UserID:   user.ID,
		Title:    "Test Track",
		FilePath: "/test.mp3",
		FileSize: 1024,
		Format:   "MP3",
		Duration: 180,
		IsPublic: true,
		Status:   models.TrackStatusCompleted,
	}
	db.Create(track)

	now := time.Now()
	startDate := now.AddDate(0, 0, -7)
	endDate := now

	// Créer une session dans la plage
	session := models.PlaybackAnalytics{
		TrackID:        track.ID,
		UserID:         user.ID,
		PlayTime:       120,
		CompletionRate: 75.0,
		StartedAt:      now.AddDate(0, 0, -3),
		CreatedAt:      now.AddDate(0, 0, -3),
	}
	db.Create(&session)

	result, err := service.GetTopTracksByPlayback(context.Background(), 10, &startDate, &endDate)

	require.NoError(t, err)
	assert.NotNil(t, result)
	assert.Len(t, result, 1)
	assert.Equal(t, track.ID, result[0]["track_id"])
}

func TestPlaybackAggregationService_GetTopTracksByPlayback_DefaultLimit(t *testing.T) {
	db := setupTestPlaybackAggregationServiceDB(t)
	logger := zaptest.NewLogger(t)
	service := NewPlaybackAggregationService(db, logger)

	user := &models.User{ID: uuid.New(), Username: "testuser", Email: "test@example.com", IsActive: true}
	db.Create(user)

	// Créer plusieurs tracks
	for i := 1; i <= 15; i++ {
		trackID := uuid.New()
		track := models.Track{
			ID:       trackID,
			UserID:   user.ID,
			Title:    "Track " + string(rune(i)),
			FilePath: "/test.mp3",
			FileSize: 1024,
			Format:   "MP3",
			Duration: 180,
			IsPublic: true,
			Status:   models.TrackStatusCompleted,
		}
		db.Create(&track)

		session := models.PlaybackAnalytics{
			TrackID:        trackID,
			UserID:         user.ID,
			PlayTime:       120,
			CompletionRate: 75.0,
			StartedAt:      time.Now(),
			CreatedAt:      time.Now(),
		}
		db.Create(&session)
	}

	result, err := service.GetTopTracksByPlayback(context.Background(), 0, nil, nil)

	require.NoError(t, err)
	assert.NotNil(t, result)
	// Devrait utiliser la limite par défaut de 10
	assert.LessOrEqual(t, len(result), 10)
}
