package services

import (
	"context"
	"testing"
	"time"

	"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 setupTestTrackStatsDB(t *testing.T) (*TrackService, *gorm.DB, func()) {
	// Setup in-memory SQLite database
	db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
	require.NoError(t, err)

	// Auto-migrate
	err = db.AutoMigrate(
		&models.User{},
		&models.Track{},
		&models.TrackLike{},
		&models.TrackComment{},
		&models.TrackPlay{},
		&models.TrackShare{},
	)
	require.NoError(t, err)

	// Create test user
	user := &models.User{
		ID:       1,
		Username: "testuser",
		Email:    "test@example.com",
		IsActive: true,
	}
	err = db.Create(user).Error
	require.NoError(t, err)

	// Create logger
	logger := zap.NewNop()

	// Create service
	service := NewTrackService(db, logger, "test_uploads")

	// Cleanup function
	cleanup := func() {
		// SQLite in-memory database doesn't need explicit cleanup
	}

	return service, db, cleanup
}

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

	ctx := context.Background()

	// Create a track
	track := &models.Track{
		UserID:   1,
		Title:    "Test Track",
		FilePath: "/path/to/track.mp3",
		FileSize: 1024,
		Format:   "MP3",
		Duration: 180,
		IsPublic: true,
		Status:   models.TrackStatusCompleted,
	}
	err := db.Create(track).Error
	require.NoError(t, err)

	// Create some likes
	like1 := &models.TrackLike{UserID: 1, TrackID: track.ID}
	like2 := &models.TrackLike{UserID: 2, TrackID: track.ID}
	err = db.Create(like1).Error
	require.NoError(t, err)
	err = db.Create(like2).Error
	require.NoError(t, err)

	// Create some comments
	comment1 := &models.TrackComment{
		TrackID: track.ID,
		UserID:  1,
		Content: "Great track!",
	}
	comment2 := &models.TrackComment{
		TrackID: track.ID,
		UserID:  2,
		Content: "Love it!",
	}
	err = db.Create(comment1).Error
	require.NoError(t, err)
	err = db.Create(comment2).Error
	require.NoError(t, err)

	// Create some plays
	play1 := &models.TrackPlay{
		TrackID:  track.ID,
		UserID:   &[]int64{1}[0],
		Duration: 120,
		PlayedAt: time.Now(),
	}
	play2 := &models.TrackPlay{
		TrackID:  track.ID,
		UserID:   &[]int64{2}[0],
		Duration: 150,
		PlayedAt: time.Now(),
	}
	play3 := &models.TrackPlay{
		TrackID:  track.ID,
		UserID:   nil, // Anonymous play
		Duration: 60,
		PlayedAt: time.Now(),
	}
	err = db.Create(play1).Error
	require.NoError(t, err)
	err = db.Create(play2).Error
	require.NoError(t, err)
	err = db.Create(play3).Error
	require.NoError(t, err)

	// Create a share with download permission and access count
	share := &models.TrackShare{
		TrackID:     track.ID,
		UserID:      1,
		ShareToken:  "test-token",
		Permissions: "read,download",
		AccessCount: 5,
	}
	err = db.Create(share).Error
	require.NoError(t, err)

	// Get stats
	stats, err := service.GetTrackStats(ctx, track.ID)
	require.NoError(t, err)
	require.NotNil(t, stats)

	// Verify stats
	assert.Equal(t, int64(3), stats.Views)              // 3 plays
	assert.Equal(t, int64(2), stats.Likes)              // 2 likes
	assert.Equal(t, int64(2), stats.Comments)           // 2 comments
	assert.Equal(t, int64(330), stats.TotalPlayTime)    // 120 + 150 + 60 = 330 seconds
	assert.Equal(t, int64(5), stats.Downloads)          // 5 downloads from share
}

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

	ctx := context.Background()

	// Try to get stats for non-existent track
	stats, err := service.GetTrackStats(ctx, 999)
	assert.Error(t, err)
	assert.Nil(t, stats)
	assert.Equal(t, ErrTrackNotFound, err)
}

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

	ctx := context.Background()

	// Create a track with no interactions
	track := &models.Track{
		UserID:   1,
		Title:    "Empty Track",
		FilePath: "/path/to/track.mp3",
		FileSize: 1024,
		Format:   "MP3",
		Duration: 180,
		IsPublic: true,
		Status:   models.TrackStatusCompleted,
	}
	err := db.Create(track).Error
	require.NoError(t, err)

	// Get stats
	stats, err := service.GetTrackStats(ctx, track.ID)
	require.NoError(t, err)
	require.NotNil(t, stats)

	// Verify all stats are zero
	assert.Equal(t, int64(0), stats.Views)
	assert.Equal(t, int64(0), stats.Likes)
	assert.Equal(t, int64(0), stats.Comments)
	assert.Equal(t, int64(0), stats.TotalPlayTime)
	assert.Equal(t, int64(0), stats.Downloads)
}

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

	ctx := context.Background()

	// Create a track
	track := &models.Track{
		UserID:   1,
		Title:    "Shared Track",
		FilePath: "/path/to/track.mp3",
		FileSize: 1024,
		Format:   "MP3",
		Duration: 180,
		IsPublic: true,
		Status:   models.TrackStatusCompleted,
	}
	err := db.Create(track).Error
	require.NoError(t, err)

	// Create multiple shares with download permissions
	share1 := &models.TrackShare{
		TrackID:     track.ID,
		UserID:      1,
		ShareToken:  "token1",
		Permissions: "read,download",
		AccessCount: 3,
	}
	share2 := &models.TrackShare{
		TrackID:     track.ID,
		UserID:      1,
		ShareToken:  "token2",
		Permissions: "download",
		AccessCount: 2,
	}
	share3 := &models.TrackShare{
		TrackID:     track.ID,
		UserID:      1,
		ShareToken:  "token3",
		Permissions: "read", // No download permission
		AccessCount: 10,
	}
	err = db.Create(share1).Error
	require.NoError(t, err)
	err = db.Create(share2).Error
	require.NoError(t, err)
	err = db.Create(share3).Error
	require.NoError(t, err)

	// Get stats
	stats, err := service.GetTrackStats(ctx, track.ID)
	require.NoError(t, err)
	require.NotNil(t, stats)

	// Verify downloads count only shares with download permission
	assert.Equal(t, int64(5), stats.Downloads) // 3 + 2 = 5 (share3 excluded)
}

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

	ctx := context.Background()

	// Create a track
	track := &models.Track{
		UserID:   1,
		Title:    "Track with Deleted Comments",
		FilePath: "/path/to/track.mp3",
		FileSize: 1024,
		Format:   "MP3",
		Duration: 180,
		IsPublic: true,
		Status:   models.TrackStatusCompleted,
	}
	err := db.Create(track).Error
	require.NoError(t, err)

	// Create comments
	comment1 := &models.TrackComment{
		TrackID: track.ID,
		UserID:  1,
		Content: "Comment 1",
	}
	comment2 := &models.TrackComment{
		TrackID: track.ID,
		UserID:  2,
		Content: "Comment 2",
	}
	err = db.Create(comment1).Error
	require.NoError(t, err)
	err = db.Create(comment2).Error
	require.NoError(t, err)

	// Soft delete one comment
	err = db.Delete(comment1).Error
	require.NoError(t, err)

	// Get stats
	stats, err := service.GetTrackStats(ctx, track.ID)
	require.NoError(t, err)
	require.NotNil(t, stats)

	// Verify only non-deleted comments are counted
	// Note: GORM's Count by default excludes soft-deleted records
	assert.Equal(t, int64(1), stats.Comments)
}

