package services

import (
	"context"
	"github.com/google/uuid"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"go.uber.org/zap"
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
	"veza-backend-api/internal/models"
)

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

	// Enable foreign keys for SQLite
	db.Exec("PRAGMA foreign_keys = ON")

	// Auto-migrate
	err = db.AutoMigrate(
		&models.User{},
		&models.Track{},
		&models.Playlist{},
		&models.PlaylistTrack{},
		&models.PlaylistShareLink{},
		&models.PlaylistFollow{},
		&models.TrackPlay{},
	)
	require.NoError(t, err)

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

	cleanup := func() {
		// Database will be closed automatically
	}

	return service, db, cleanup
}

func TestPlaylistAnalyticsService_GetPlaylistStats(t *testing.T) {
	service, db, cleanup := setupTestPlaylistAnalyticsService(t)
	defer cleanup()

	ctx := context.Background()

	// Create test user
	user := &models.User{
		Username:     "user1",
		Email:        "user1@example.com",
		PasswordHash: "hash1",
		Slug:         "user1",
		IsActive:     true,
	}
	require.NoError(t, db.Create(user).Error)

	// Create test tracks
	track1 := &models.Track{
		UserID:    user.ID,
		Title:     "Track 1",
		FilePath:  "/path/to/track1.mp3",
		FileSize:  1024,
		Format:    "MP3",
		Duration:  180,
		IsPublic:  true,
		Status:    models.TrackStatusCompleted,
		PlayCount: 0,
		LikeCount: 0,
	}
	track2 := &models.Track{
		UserID:    user.ID,
		Title:     "Track 2",
		FilePath:  "/path/to/track2.mp3",
		FileSize:  2048,
		Format:    "MP3",
		Duration:  240,
		IsPublic:  true,
		Status:    models.TrackStatusCompleted,
		PlayCount: 0,
		LikeCount: 0,
	}
	require.NoError(t, db.Create(track1).Error)
	require.NoError(t, db.Create(track2).Error)

	// Create test playlist
	playlist := &models.Playlist{
		UserID:        user.ID,
		Title:         "Test Playlist",
		Description:   "A test playlist",
		IsPublic:      true,
		TrackCount:    2,
		FollowerCount: 0,
	}
	require.NoError(t, db.Create(playlist).Error)

	// Add tracks to playlist
	playlistTrack1 := &models.PlaylistTrack{
		PlaylistID: playlist.ID,
		TrackID:    track1.ID,
		Position:   1,
	}
	playlistTrack2 := &models.PlaylistTrack{
		PlaylistID: playlist.ID,
		TrackID:    track2.ID,
		Position:   2,
	}
	require.NoError(t, db.Create(playlistTrack1).Error)
	require.NoError(t, db.Create(playlistTrack2).Error)

	// Create some track plays
	play1 := &models.TrackPlay{
		TrackID:  track1.ID,
		UserID:   &user.ID,
		Duration: 180,
	}
	play2 := &models.TrackPlay{
		TrackID:  track1.ID,
		UserID:   &user.ID,
		Duration: 150,
	}
	play3 := &models.TrackPlay{
		TrackID:  track2.ID,
		UserID:   &user.ID,
		Duration: 240,
	}
	require.NoError(t, db.Create(play1).Error)
	require.NoError(t, db.Create(play2).Error)
	require.NoError(t, db.Create(play3).Error)

	// Create share link
	shareLink := &models.PlaylistShareLink{
		PlaylistID:  playlist.ID,
		UserID:      user.ID,
		ShareToken:  "test-token-123",
		AccessCount: 0,
	}
	require.NoError(t, db.Create(shareLink).Error)

	// Create follow
	follow := &models.PlaylistFollow{
		PlaylistID: playlist.ID,
		UserID:     user.ID,
	}
	require.NoError(t, db.Create(follow).Error)

	// Update follower count
	playlist.FollowerCount = 1
	require.NoError(t, db.Save(playlist).Error)

	// Get stats
	stats, err := service.GetPlaylistStats(ctx, playlist.ID)
	require.NoError(t, err)
	assert.NotNil(t, stats)

	// Verify stats
	assert.Equal(t, int64(3), stats.Plays)     // 3 plays total (2 for track1, 1 for track2)
	assert.Equal(t, int64(1), stats.Shares)    // 1 share link
	assert.Equal(t, int64(1), stats.Likes)     // 1 follow
	assert.Equal(t, int64(1), stats.Followers) // 1 follower
	assert.Equal(t, 2, stats.TrackCount)       // 2 tracks
}

func TestPlaylistAnalyticsService_GetPlaylistStats_NotFound(t *testing.T) {
	service, _, cleanup := setupTestPlaylistAnalyticsService(t)
	defer cleanup()

	ctx := context.Background()

	// Get stats for non-existent playlist
	stats, err := service.GetPlaylistStats(ctx, uuid.New())
	assert.Error(t, err)
	assert.Nil(t, stats)
	assert.Equal(t, "playlist not found", err.Error())
}

func TestPlaylistAnalyticsService_GetPlaylistStats_EmptyPlaylist(t *testing.T) {
	service, db, cleanup := setupTestPlaylistAnalyticsService(t)
	defer cleanup()

	ctx := context.Background()

	// Create test user
	user := &models.User{
		Username:     "user1",
		Email:        "user1@example.com",
		PasswordHash: "hash1",
		Slug:         "user1",
		IsActive:     true,
	}
	require.NoError(t, db.Create(user).Error)

	// Create empty playlist
	playlist := &models.Playlist{
		UserID:        user.ID,
		Title:         "Empty Playlist",
		Description:   "An empty playlist",
		IsPublic:      true,
		TrackCount:    0,
		FollowerCount: 0,
	}
	require.NoError(t, db.Create(playlist).Error)

	// Get stats
	stats, err := service.GetPlaylistStats(ctx, playlist.ID)
	require.NoError(t, err)
	assert.NotNil(t, stats)

	// Verify stats for empty playlist
	assert.Equal(t, int64(0), stats.Plays)
	assert.Equal(t, int64(0), stats.Shares)
	assert.Equal(t, int64(0), stats.Likes)
	assert.Equal(t, int64(0), stats.Followers)
	assert.Equal(t, 0, stats.TrackCount)
}

func TestPlaylistAnalyticsService_GetPlaylistStats_MultipleShares(t *testing.T) {
	service, db, cleanup := setupTestPlaylistAnalyticsService(t)
	defer cleanup()

	ctx := context.Background()

	// Create test user
	user := &models.User{
		Username:     "user1",
		Email:        "user1@example.com",
		PasswordHash: "hash1",
		Slug:         "user1",
		IsActive:     true,
	}
	require.NoError(t, db.Create(user).Error)

	// Create test playlist
	playlist := &models.Playlist{
		UserID:        user.ID,
		Title:         "Test Playlist",
		Description:   "A test playlist",
		IsPublic:      true,
		TrackCount:    0,
		FollowerCount: 0,
	}
	require.NoError(t, db.Create(playlist).Error)

	// Create multiple share links
	shareLink1 := &models.PlaylistShareLink{
		PlaylistID:  playlist.ID,
		UserID:      user.ID,
		ShareToken:  "token-1",
		AccessCount: 0,
	}
	shareLink2 := &models.PlaylistShareLink{
		PlaylistID:  playlist.ID,
		UserID:      user.ID,
		ShareToken:  "token-2",
		AccessCount: 0,
	}
	require.NoError(t, db.Create(shareLink1).Error)
	require.NoError(t, db.Create(shareLink2).Error)

	// Get stats
	stats, err := service.GetPlaylistStats(ctx, playlist.ID)
	require.NoError(t, err)
	assert.NotNil(t, stats)

	// Verify shares count
	assert.Equal(t, int64(2), stats.Shares)
}

func TestPlaylistAnalyticsService_GetPlaylistStats_MultipleFollows(t *testing.T) {
	service, db, cleanup := setupTestPlaylistAnalyticsService(t)
	defer cleanup()

	ctx := context.Background()

	// Create test users
	user1 := &models.User{
		Username:     "user1",
		Email:        "user1@example.com",
		PasswordHash: "hash1",
		Slug:         "user1",
		IsActive:     true,
	}
	user2 := &models.User{
		Username:     "user2",
		Email:        "user2@example.com",
		PasswordHash: "hash2",
		Slug:         "user2",
		IsActive:     true,
	}
	user3 := &models.User{
		Username:     "user3",
		Email:        "user3@example.com",
		PasswordHash: "hash3",
		Slug:         "user3",
		IsActive:     true,
	}
	require.NoError(t, db.Create(user1).Error)
	require.NoError(t, db.Create(user2).Error)
	require.NoError(t, db.Create(user3).Error)

	// Create test playlist
	playlist := &models.Playlist{
		UserID:        user1.ID,
		Title:         "Test Playlist",
		Description:   "A test playlist",
		IsPublic:      true,
		TrackCount:    0,
		FollowerCount: 0,
	}
	require.NoError(t, db.Create(playlist).Error)

	// Create multiple follows
	follow1 := &models.PlaylistFollow{
		PlaylistID: playlist.ID,
		UserID:     user2.ID,
	}
	follow2 := &models.PlaylistFollow{
		PlaylistID: playlist.ID,
		UserID:     user3.ID,
	}
	require.NoError(t, db.Create(follow1).Error)
	require.NoError(t, db.Create(follow2).Error)

	// Update follower count
	playlist.FollowerCount = 2
	require.NoError(t, db.Save(playlist).Error)

	// Get stats
	stats, err := service.GetPlaylistStats(ctx, playlist.ID)
	require.NoError(t, err)
	assert.NotNil(t, stats)

	// Verify follows count
	assert.Equal(t, int64(2), stats.Likes)
	assert.Equal(t, int64(2), stats.Followers)
}

func TestPlaylistAnalyticsService_IncrementPlaylistPlays(t *testing.T) {
	service, _, cleanup := setupTestPlaylistAnalyticsService(t)
	defer cleanup()

	ctx := context.Background()

	// Test increment (should not error, but doesn't do anything for now)
	err := service.IncrementPlaylistPlays(ctx, uuid.New())
	assert.NoError(t, err)
}
